Make rbs collection say where the gem list comes from - #3094
Draft
pocke wants to merge 2 commits into
Draft
Conversation
Ask a coding agent to set up RBS and Steep in a project and it usually writes a Steepfile that lists every dependency with `library`, one gem per line. `rbs collection` exists so that nobody has to maintain that list: it reads `Gemfile.lock`, and `rbs` and Steep load the installed RBS files on their own. Agents rarely find it, and the tools we ship are part of why. `rbs collection init` printed one line, `created: rbs_collection.yaml`. It named neither the next command nor the fact that would stop someone from enumerating gems. The generated `rbs_collection.yaml` had the same gap: its comments explained `sources`, `path` and `gems[].ignore` -- the knobs -- but never said the gem list is read from `Gemfile.lock`. The file is read right after it is written, so it is a good place to say so. - `init` now prints what the command does, that gems in `Gemfile.lock` need no listing anywhere, and the next steps: `.gitignore` and `rbs collection install`. `rbs_collection.lock.yaml` is what pins the RBS revisions -- the source is `revision: main` -- so the output says to keep it in version control. - The generated config opens with the same two facts. - Its commented-out `gems:` example showed only `ignore: true`. The other half -- adding an entry for a library that `Gemfile.lock` doesn't carry -- was documented only in `docs/collection.md`. `socket` is the example: it is a non-gem standard library (`NONGEM_STDLIBS`), unlike `pathname`, whose types moved to `core/pathname.rbs` in 8066053, and unlike `csv`, which is in `ALUMNI_STDLIBS` and warns when installed. - `docs/collection.md` gets a quickstart, so readers who see only the first screen get the two commands. Its `cat rbs_collection.yaml` had drifted from the generated file (no `type: git`, no local source comment) and now matches it byte for byte. The `.gitignore` paragraph right below the `init` output is gone, since that output now says it. Ref: #3093 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BXtMkmp27LieHgSjNyEWZ8
Running `rbs collection install` before the files it needs exist ended in an uncaught exception with a full backtrace, because `exe/rbs` has no rescue. Someone -- or a coding agent -- who sees that concludes that `rbs collection` is broken and goes back to listing gems with `library`. `rbs collection init` now points at `rbs collection install`, so these paths are easier to reach than they were. Four of them: - No `rbs_collection.yaml`: `Errno::ENOENT` from `Config.from_path`, for both `install` and `update`. Now a message naming `rbs collection init`. - No `rbs_collection.lock.yaml` under `--frozen`: `Errno::ENOENT` from `Installer#initialize`. The message says to run without `--frozen`, or, when the config is missing too, to run `rbs collection init` first, so that one message is enough to get unstuck. - No `Gemfile`: `Bundler::GemfileNotFound` from `Bundler.definition`. - A `Gemfile` but no `Gemfile.lock`: `Bundler.definition` returns, and `LockfileGenerator#initialize` dies with `undefined method 'specs' for nil` on `definition.locked_gems.specs`. Now a message naming `bundle install`. Raising `RBS::Collection::Config::CollectionNotAvailable`, as the issue suggested, would keep the backtrace for exactly the same reason, so these write to `stderr` and return 1 like the rest of `run_collection`. `already exists` and `should exist to clean` used `Kernel#puts`, which writes to the real `$stdout` and ignores the injected IO. Both are failures that return 1, so they go to `stderr` now; this changes which stream they appear on. Tests cover each of the four paths, `--frozen` with neither file, and `--frozen` with a lock file and no config, which keeps working. Ref: #3093 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BXtMkmp27LieHgSjNyEWZ8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #3093.
Ask a coding agent to set up RBS and Steep in a project and it usually writes a Steepfile that lists every dependency with
library, one gem per line.rbs collectionexists so that nobody has to maintain that list -- it readsGemfile.lock, andrbsand Steep load the installed RBS files on their own -- but nothing an agent (or a person) reads on the way there says so. This PR changes whatrbs collectionprints and writes. Thesteep inittemplate, the other half of #3093, is soutaro/steep#2269.initsays where the gem list comes fromrbs collection initprintedcreated: rbs_collection.yamland nothing else: not the next command, not the fact that would stop someone from enumerating gems. The generatedrbs_collection.yamlhad the same gap -- comments forsources,pathandgems[].ignore, none for "the gem list is read fromGemfile.lock". Now:The generated config opens with the same two facts. Its commented-out
gems:example showed onlyignore: true; the other half -- adding an entry for a library thatGemfile.lockdoesn't carry -- was documented only indocs/collection.md. The example issocket, a non-gem standard library (NONGEM_STDLIBS), rather thanpathname, whose types moved tocore/pathname.rbsin 8066053, orcsv, which is inALUMNI_STDLIBSand warns when installed.docs/collection.mdgets a quickstart at the top, itscat rbs_collection.yamlblock matches the generated file again (it had losttype: gitand the local source comment), and the.gitignoreparagraph that now duplicates theinitoutput is gone.The missing-file paths no longer end in a backtrace
exe/rbshas no rescue, so every one of these printed a full backtrace, which reads as "rbs collectionis broken" and sends people back tolibrary.initnow points atrbs collection install, so they are easier to reach than before.rbs_collection.yaml(install,update)Errno::ENOENTrbs collection init--frozenErrno::ENOENT--frozen, or to runinitfirst when the config is missing tooGemfileBundler::GemfileNotFoundGemfile.lockGemfilebut noGemfile.lockundefined method 'specs' for nilbundle installRaising
RBS::Collection::Config::CollectionNotAvailable, which #3093 suggested, would keep the backtrace for the same reason, so these write tostderrand return 1 like the rest ofrun_collection.Observable change:
already existsandshould exist to cleanusedKernel#puts, which writes to the real$stdoutand ignores the injected IO. Both are failures that return 1, so they move tostderr.Notes
gems:here, or withlibraryin a Steepfile -- so the two templates are not in conflict; each shows the option that belongs to it.--frozenwith neither file, and--frozenwith a lock file and no config, which keeps working.steep checkdoes not run in my environment (steep/patch.rbsdefinesModule#ruby2_keywords, which now collides withcore/module.rbs, onmasteras well); with that onesignatureline removed it reportsNo type error detected.