Fix vendored gem rewrite breaking positional args (#847) - #849
Conversation
Vendoring a gem declared with a splat, constant or method-call version (`gem "rack", *V`, `gem "rack", VERSION`, `ENV.fetch(...)`) wrote that argument after the new `path:` keyword. Ruby rejects that, so every later `bundle` command failed to parse the Gemfile even though vendor reported success and VEX attested the patch. The exact pin supersedes these constraints, so they are now dropped like quoted ones. Keyword options such as `require: false` and a trailing comment still follow `path:`. A real-bundler e2e checks the rewritten Gemfile installs frozen and loads the vendored copy. Fixes #847 Assisted-by: Claude Code:claude-opus-5-5
43c8ce7 to
e64c425
Compare
#605 taught the name-keyed npm resolver to probe bundled store trees, so it now finds aliased copies (node_modules/lp) and a nested host's store peers itself. Two vex_consumed tests from #738 assumed that set never held aliases, so main's CI went red after both merged. The tests now feed the alias-free set explicitly to keep covering alias expansion, and also check the resolver's own set reaches the same copies with no duplicates. No production code changes. Assisted-by: Claude Code:claude-opus-5-5 (cherry picked from commit 40dac07)
|
BugBot review Generated by Claude Code |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 00d3eaa. Configure here.
|
I don't think this failure comes from this PR:
I couldn't reproduce it locally: the sandbox can't reach Generated by Claude Code |
|
Burn-down agent: labeled Ready for review.
Generated by Claude Code |
Brings in the vex_consumed alias test fix (#849) that main's red test/test-release/coverage jobs were waiting on. Co-Authored-By: Claude <noreply@anthropic.com>
Brings in the vex_consumed alias test fix (#849) that main's red test/test-release/coverage jobs were waiting on. Co-Authored-By: Claude <noreply@anthropic.com>
Brings in the vex_consumed alias test fix (#849) that main's red test/test-release/coverage jobs were waiting on. Co-Authored-By: Claude <noreply@anthropic.com>
Brings in the vex_consumed alias test fix (#849) that main's red test/test-release/coverage jobs were waiting on. Co-Authored-By: Claude <noreply@anthropic.com>
Brings in the vex_consumed alias test fix (#849) that main's red test/test-release/coverage jobs were waiting on. Co-Authored-By: Claude <noreply@anthropic.com>
Resolves the vendored Gemfile rewrite conflict with #849 (#847). The merged rewrite keeps #847's handling of positional version arguments (`*V`, constants, method calls) and reads the tail through the shared option reader. That reader now classifies positional arguments as version constraints, never sources, and flags `**opts` splats and hash literals as dynamic. Hosted mode still refuses dynamic options, while vendored mode keeps them after `path:` as #847 intends. Assisted-by: Claude Code:claude-opus-5-5
Brings in the vex_consumed alias test fix (#849) that the red test/test-release/coverage jobs were waiting on. Co-Authored-By: Claude <noreply@anthropic.com>
LLM Description written by Claude Code:claude-opus-5-5
Fixes #847
Summary
In vendored mode, vendoring a gem declared with a splat, constant or method-call version (
gem "rack", *V,gem "rack", VERSION,gem "rack", ENV.fetch(…)) produced a Gemfile that nobundlecommand could parse, whilevendorexited 0 and VEX attestednot_affected. The rewrite now produces a Gemfile that installs frozen and loads the vendored copy.Root cause
plan_gemfile_edit(vendor/gem.rs) rewrites a declaration asgem name, version, path: rel, <opts>.<opts>comes fromgem_line_trailing_options, which keeps everything from the first non-quoted argument on. So a positional constraint expression landed after thepath:keyword, and Ruby rejects a positional argument after a keyword one.Fix
split_kept_tailsplits the kept tail at its first keyword argument (key:,"key":, a=>pair or**splat). Commas,=>and#only count outside strings and brackets, andConst::Pathis not a label.require: false,group:) and a trailing comment still followpath:. Revert restores the original line verbatim from the ledger.path:. I tried keeping them first: the real Bundler then seesrack (= 3.2.7, ~> 3.1)against the lock'srack (= 3.2.7)!and refuses every frozen install.gem "x", OPTSwith a hash constant) as options. The line grammar can't tell that apart from a version constant, so it's dropped like any other positional, and revert still restores it.Also contains a cherry-pick of #851 (
00d3eaa, test-only). Main is red since #605 on twovex_consumedtests, and that cherry-pick is needed fortest/coverageto pass here. It becomes a no-op once #851 merges.Test evidence
*V,ENV.fetch(…),CONST, require: false, mixed=>/**/Const::Pathvendor::gem::tests::test_rewrite_drops_positional_constraints(unit)path: "…", *RV)vendor::gem::tests::split_kept_tail_grammar(unit)e2e_vendor_gem_build::gem_vendor_drops_positional_constraintsThere was an error parsing Gemfile: syntax error, unexpected *.socket/vendor/gem/, revert byte-identicalCommands run locally:
cargo test -p socket-patch-core --all-features --lib -- vendor::gem: 127 passed.cargo test -p socket-patch-cli --all-features --test e2e_vendor_gem_build -- --ignored: 8/8 passed.cargo clippy --workspace --all-features -- -D warnings: clean.rustfmt --checkon the changed files: clean. A workspace-widecargo fmt --checkwith this sandbox's rustfmt also flags files already on main, which this PR leaves alone.cargo test --workspace --all-features --no-fail-fast: everything passes except tests that need a read-only directory to reject writes. Those can't fail here because the sandbox runs as root (uid 0); they cover the npm/pypi/vlt write-failure paths, which this PR doesn't touch.🤖 Generated with Claude Code
https://claude.ai/code/session_01MxWLuzeHjHPXgKnhQWVncJ
Note
Medium Risk
Changes Gemfile rewrite logic for all gem vendoring; mistakes could break valid declarations or drop needed options, though coverage is strong and revert preserves originals.
Overview
Fixes #847: vendored gem Gemfile rewrites no longer append splats, constants, or method-call version arguments after the inserted
path:keyword (invalid Ruby).split_kept_tailinvendor/gem.rssplits the kept tail at the first real keyword argument and drops leading positional constraints (same as quoted pins), whilerequire:,group:,=>pairs,**splats, and trailing comments still followpath:.Adds unit coverage (
test_rewrite_drops_positional_constraints,split_kept_tail_grammar) and an ignored host e2e (gem_vendor_drops_positional_constraints) that checks frozenbundle install, vendored load path, and byte-identical revert.Test-only: two
vex_consumedhosted-npm tests are updated for post-#605 resolver behavior (alias expansion exercised with an alias-free installed map; resolver output asserted separately).Reviewed by Cursor Bugbot for commit 00d3eaa. Configure here.
Generated by Claude Code