rbs collection manager design doc
This is the design doc of the RBS collection manager.
Motivations
We have the following motivations.
Download RBSs from ruby/gem_rbs GitHub repository
Currently, we provide no downloader for gem_rbs. So the users need to download it manually. For example, coping RBSs from the repository, or using git submodule. But they look not the best way.
VS git submodule
Actually, we already can download RBSs from the GitHub repository by using git submodule. But it has some difficulty.
- Download speed
- We can easily predict that the GitHub repository will grow. It means download time will grow also.
- So downloading the entire repository will be a not good approach.
- Disk usage
- It is related to the above problem. If the repository grows, it consumes larger disk space.
- Version control
- Git submodule only can pin version by git commit hash.
- It means it cannot use different versions for individual gems.
- git submodule is difficult
- I don't like git submodule
- ruby/gem_rbs repository uses
git submodule
. - It will be problematic when using
git submodule update --init --recursive
. The--recursive
option downloads submodels recursively, and it will introduce unexpected downloads.-
--recursive
is enabled by default inghq
. So I think it is not a rare case. Actually I had a problem with it.
-
Manage loading RBS for some tools such as rbs
and steep
Currently, we also provide no manager to load RBSs of gems. So the users need to specify -r
option of rbs
cli or library
DSL in Steepfile
.
They have some problems like the following.
- It is not integrated with the RBS downloader
- It does not resolve any dependencies.
The name of the tool
TBD
-
rbs bundle
rbs bundle install
rbs bundle update
-
rbs collection
rbs collection install
rbs collection update
Dependencies
- Bundler
- Because it resolves gem dependencies with Gemfile.lock
-
git(1)
command- To clone gem_rbs_collection git repository from GitHub
- I guess we can replace
git(1)
with GitHub API in the future.
Specification
Configuration files
The main configuration file
The user writes this configuration file.
File Structure
# TODO
collections: # or how about `source`?
- git: github.com:ruby/gem_rbs_collection.git
commit: 12345
- name: pocke
git: github.com:pocke/gem_rbs_collection.git
branch: main
# It copies RBSs to the specified directory
path: vendor/collections
# vendor/collections/gem_rbs_collection/
# vendor/collections/pocke/
# Basically this tool manage gems that are specified in Gemfile.lock,
# But you can change the behavior by writing `gem` section explicitly.
gems:
# For stdlib because stdlib is not included by Gemfile in many cases.
- name: pathname
# To ignore when you have any troubles with the type
- name: nokogiri
ignore: true
# To specify the collection source. It is useful to use forked repo.
- name: activesupport
source: pocke
# To pin commit hash
- name: activerecord
commit: 12345
# Add meta info for Steep (or other tools)
- name: minitest
group: test
Decide the followings
-
Name (
rbs.yaml
?)-
rbs.yaml
or.rbs.yaml
? -
rbs.yaml
orrbs.yml
? (or both?)- I like
.yaml
because of https://yaml.org/faq.html-
- Is there an official extension for YAML files?
Please use ".yaml" when possible.
-
- I like
-
rbs.yaml
orRBSFile
orRBSFile.yaml
orrbsfile.yaml
orrbs_config.yaml
orrbs_collection.yaml
or ...- If the name starts with a capital letter, we can find the file easily because capital letters are smaller than lower letters in ASCII order.
- I like the file name with an extension, so I don't like
RBSFile
. Editors detect filetype easily by the extension.
-
The lock file
The CLI tool generates the lock file from the main configuration file and Gemfile.lock
.
The user doesn't edit the lock file.
File Structure
The same as the main configuration file. But the lock file has more information to lock version.
Decide the followings
-
Name (
.rbs.lock.yaml
?)- The name is based on the main configuration file
The version information for each gem
Like gem_rbs/gems/activesupport/6.0/.info.yaml
The CLI needs to know installed gems' revisions. It is necessary to determine to update.
For example:
- A user installs rbs with revision
xxx
- The lock file is updated to use revision
xxx
- The lock file is updated to use revision
- gem_rbs_collection is updated to
yyy
revision - Another user updates the revision to
yyy
- The lock file is updated to use revision
yyy
- The lock file is updated to use revision
- The first user re-install rbs with the new lock file.
- It needs to check the installed rbs's revision and the locked revision. So it needs the installed revision.
I guess YAML is overkill for it because it needs only a revision, perhaps.
gem_rbs_cli install
Create the lock file
# pseudo code
def create_the_lock_file
if lockfile.exist?
if has_difference_between_config_and_lock?
lockfile.write resolve_dependencies_and_versions_from_gemfile_lock
else
# no chnage
end
else
lockfile.write resolve_dependencies_and_versions_from_gemfile_lock
end
end
def has_difference_between_config_and_lock?
config_has_removed_gem? || config_has_new_gem? || config_has_commit_hash_difference?
end
def resolve_dependencies_and_versions_from_gemfile_lock
# TODO
end
Install RBSs from the lock file
TBD
gem_rbs_cli update
It is easy. Remove the lock file and run install.
Resolve Dependency
Bundler integration
RBS command integration
rbs
CLI loads ./.gem_rbs_cli.yaml
automatically, or specify from the command line option.
It exposes a loader, for example:
loader = RBS::EnvironmentLoader.from_config(config)
Steep integration
just idea
# Steepfile
target :lib do
rbs_config "rbs_config.yml", with: :*
end
target :test do
rbs_config "rbs_config.yml", with: :test
end