142
0
0

rbs collection manager design doc

Published at February 4, 2021 3:03 p.m.
Edited at February 5, 2021 6:07 p.m.

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 in ghq. 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

rbs.yaml
# 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 or rbs.yml ? (or both?)
      • I like .yaml because of https://yaml.org/faq.html
          1. Is there an official extension for YAML files?

          Please use ".yaml" when possible.

    • rbs.yaml or RBSFile or RBSFile.yaml or rbsfile.yamlor rbs_config.yaml or rbs_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:

  1. A user installs rbs with revision xxx
    1. The lock file is updated to use revision xxx
  2. gem_rbs_collection is updated to yyy revision
  3. Another user updates the revision to yyy
    1. The lock file is updated to use revision yyy
  4. The first user re-install rbs with the new lock file.
    1. 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

ruby
# 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:

ruby
loader = RBS::EnvironmentLoader.from_config(config)

Steep integration

just idea

ruby
# Steepfile

target :lib do
  rbs_config "rbs_config.yml", with: :*
end

target :test do
  rbs_config "rbs_config.yml", with: :test
end