Repository navigation
Fix vendoring a gem the lock does not resolve (#2075) - #2079
Mikola Lysenko (mikolalysenko) wants to merge 5 commits into
Conversation
Refs #2075. Assisted-by: Claude Code:claude-fable-5-2 Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011f9aePg6MaoEvntLaEAf2x
Three failing tests for #2075: scan --mode vendored (wet and dry run) and vendor on a project whose gem home holds a patchable gem version that Gemfile.lock doesn't resolve. Today the lock edit fails with "GEM specs has no entry" and the run exits 1. Assisted-by: Claude Code:claude-fable-5-2 Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011f9aePg6MaoEvntLaEAf2x
A project that installs into a shared gem home is crawled together with the gems Ruby ships and the versions other projects installed. Vendored mode handed each of them to the gem backend, whose lock edit failed with "Gemfile.lock GEM specs has no entry", so the run exited 1 although nothing in the project could be vendored. Hosted mode has skipped such a version since #1055. The gem backend now refuses a version no section of Gemfile.lock lists as vendor_gem_version_not_locked, and the vendored refusals raise the same refusal for an installed gem before any download. vendor, scan and get report it as a skip and exit 0; the dry run previews it. get <uuid> still reports it as its error. The unwind test that used a version skew to make the lock edit fail now uses a spec listed under two sources, which still fails there. Fixes #2075 Assisted-by: Claude Code:claude-fable-5-2 Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011f9aePg6MaoEvntLaEAf2x
Follow-up to the review of the previous commit. The backend refuses an unlocked gem whether or not it is installed, so the pre-download refusal no longer filters on the installed copy (which also cost a second crawl of the gem home), and the detail no longer claims the gem is installed. The lock is read with the shared Bundler lock parser (listed_specs, beside hosted mode's locked_specs) instead of a second line scan. get <uuid> --mode vendored reports the skip as a skip (exit 0), like the search path, scan and vendor. The dry run prints the wet run's [skip] line and counts it as "would be skipped", not refused. Refs #2075 Assisted-by: Claude Code:claude-fable-5-2 Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011f9aePg6MaoEvntLaEAf2x
|
[agent] bugbot run Generated by Claude Code |
PR SummaryLow Risk Overview The core adds lock parsing via Documentation updates cover the new warning code, dry-run wording, and shared-gem-home behavior in Reviewed by Cursor Bugbot for commit e537285. Configure here. |
get.rs called vendor::refusal_is_benign, an import from one command module into another that command_module_layering forbids (coverage job). Every input of the predicate is a core constant, so it now lives beside the refusals in socket-patch-core, and vendor, scan, get and the download phase all read it there. Refs #2075 Assisted-by: Claude Code:claude-fable-5-2 Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011f9aePg6MaoEvntLaEAf2x
LLM Description written by Claude Code:claude-fable-5-2
Fixes #2075
Summary
scan --mode vendoredon a Bundler project that installs into a shared gemhome (the Bundler default) failed with exit 1 as soon as that gem home held a
patchable gem version the project doesn't lock: a gem Ruby ships (
rake,rexml), or a version another project installed.Such a gem is now skipped, before its patch is downloaded, and the run exits 0:
Nothing the project does lock changes: those gems are vendored exactly as
before.
Root cause
The crawl of a shared gem home returns every gem version in it. Vendoring a
gem is a
Gemfile.lockedit (its spec block moves from aGEMsection to aPATHsection), and vendored mode handed the backend versions whose spec thelock doesn't have. The lock edit failed, which fails the run. Hosted mode has
skipped these since #1055 (
redirect_gem_version_not_locked); vendored modehad no such check.
The change
vendor/gem.rs,vendor/refusals.rs,formats/gem/mod.rs): thegem backend refuses a version that no section of
Gemfile.locklists, withthe new code
vendor_gem_version_not_locked. The same check is one of thevendored refusals that every entry point reads before any download. It uses
the existing lock parser (
listed_specs, next to hosted mode'slocked_specs).refusal_is_benign, moved into core sogetcan read it without importing thevendorcommand module), sovendor,scan --mode vendoredandget --mode vendored(by purl and by uuid)report a
skippedevent and exit 0. The dry run prints the same[skip]line and counts it as "would be skipped", not "would be refused".
CLI_CONTRACT.md, the dry-run headline there,and a paragraph in
docs/ecosystems.md.What counts as "the lock resolves it": a spec entry for that name and version
in any source section (
GEM,PATH,GIT,PLUGIN SOURCE), including aplatform build of that version. So this PR only changes the outcome for a
version the lock doesn't mention at all. With no readable
Gemfile.lock, or amanifest Bundler doesn't load (
gems.rb,BUNDLE_GEMFILE), the backend's ownrefusal is reported as before.
Evidence
Red before the fix (commit
c5aac79, tests only), on unfixed source:Green after. Tests that cover
Fixes #2075, each over three projects (theproject doesn't use the gem; the lock resolves a newer version; the same with
a CRLF lock):
crates/socket-patch-cli/tests/in_process_vendor.rs)scan_vendor_gem_skips_a_version_the_lock_does_not_resolvestatus: success,skippedevent, Gemfile pair byte-identical, no.socket/, patch never downloadedscan_vendor_gem_dry_run_previews_the_unlocked_version_skipscan_vendor_gem_unlocked_version_skip_reads_as_a_skip[skip]line, no errorvendor_gem_skips_a_version_the_lock_does_not_resolvevendorandvendor --dry-runfrom a manifest recordget_uuid_vendored_gem_skips_a_version_the_lock_does_not_resolveget <uuid> --mode vendored, JSON and humanCore unit tests:
vendor::gem::tests::version_the_lock_does_not_resolve_is_refused_untouched,listed_specs_reads_spec_entries_of_every_section,version_not_locked_refusals_*,vendor::refusals::tests::gem::*, andscan::render::tests::dry_run_line_counts_expected_skips.Against a real shared gem home (Ruby 3.3.6, Bundler 4.0.18, Ruby's bundled
rake 13.1.0, a project that doesn't lock it):Local checks:
cargo clippy --locked --workspace --all-features -- -D warnings(CI'scommand): clean.
cargo test -p socket-patch-core --lib,cargo test -p socket-patch-cli --lib --bins: pass.in_process_vendor,scan_vendor_e2e,e2e_redirect_gem_stale_install,e2e_vex_vendor,vendor_group_commit_e2e,command_module_layering: pass.cargo test -p socket-patch-cli --test e2e_vendor_gem_build -- --ignored(real Bundler 4.0.18, Ruby 3.3.6): 11 passed.
Notes for the reviewer
test_unwind_on_lock_edit_failureused a version skew to make the lock edit fail after the Gemfile was
rewritten. That input is now refused earlier, so the test uses a spec listed
under two
GEMsections, which still fails at the lock edit. Its assertionsare unchanged.
get <uuid> --mode vendorednow exits 0 for this code. Every otherpre-download refusal there is still an error. I made it a skip so the same
condition isn't
failedon one path andskippedon the others.cargo clippy --all-targetsreports lints in test code this PR doesn'ttouch (
nuget_feed.rs,crawler_npm_e2e.rs,repair.rs, ...); the samelints are on
main. None are in the files changed here.Review findings not fixed here
/code-review highran on the diff; its findings are fixed except two,which are separate behavior:
GITorPATHsection, with a registry copyof the same version in the shared gem home, still fails in the backend as it
does on
main. Hosted mode skips it withredirect_gem_source_option.Skipping it here needs its own message and tests.
Gemfile.lockstill fails each crawled gem withvendor_lockfile_missing, where hosted mode skips withredirect_gem_no_lockfile.Filtering the gem selection by the lock once, for both modes, would replace
both per-mode checks. It changes what
scandiscovers and reports, so it isleft for a maintainer to decide.
https://claude.ai/code/session_011f9aePg6MaoEvntLaEAf2x
Generated by Claude Code