Fix gem rewrite deleting a ;-joined declaration (#826) - #875
Mikola Lysenko (mikolalysenko) wants to merge 5 commits into
Conversation
Assisted-by: Claude Code:claude-opus-5-5
A Gemfile line like `gem "a", "1"; gem "b", "2"` had its second declaration deleted when socket-patch redirected or vendored gem "a", because the rewrite replaces the whole line and the safety check did not know that `;` starts a new statement. The next frozen `bundle install` then failed. Such lines are now refused with a warning and left untouched. A declaration ending in a bare `;` (optionally followed by a comment) was refused as "continues on the next line" since #637. It is complete, so it is rewritten again, without the `;`. Fixes #826 Assisted-by: Claude Code:claude-opus-5-5
The `;`-joined fixture had no version argument, so the old check already refused it as "unexpected tokens" and the test passed without the fix. Use `gem "x", "v"; gem "y", "v"` from #826, which the old code rewrote and lost the second gem. Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
main moved the Gemfile option reader into formats::gem::gemfile, so the `;` terminator handling moves with it: trailing_options and the new source_option both drop a bare statement terminator. Otherwise a complete `gem "x", "1";` line was refused as an unreadable, source-selecting tail. Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
|
[agent]
I ported the fix from #878 here in 5b9953d. It becomes a no-op once #878 lands. With it, Generated by Claude Code |
|
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 5b9953d. Configure here.
|
[agent] CI status on 5b9953d. None of these look like this PR's failures:
Generated by Claude Code |
|
Burn-down agent: labeled Ready for review at
Generated by Claude Code |
LLM Description written by Claude Code:claude-opus-5-5
Fixes #826
Summary
A Gemfile line holding two
;-joined declarations (gem "colorize", "0.8.1"; gem "rainbow", "3.1.1") lost its second gem when socket-patch redirected (hosted) or vendored the first one. Hosted scan exited 0, and the next frozenbundle installfailed. Such lines are now refused with the existingredirect_gem_unrecognized_declarationwarning (hosted) or anot editablerefusal (vendored), and nothing is written.The PR also fixes the opposite regression from #637. A complete declaration ending in a bare
;(gem "colorize", "0.8.1";, optionally followed by# comment) was refused as "continues on the next line". It is rewritten again, and the;is dropped from the kept options.Root cause
Both Gemfile rewriters replace the declaration's whole physical line, and both gate that on the shared
gem_line_tail_blocks_edit(crates/socket-patch-core/src/patch/redirect/mod.rs; vendored calls it throughrest_blocks_edit). The gate had no notion of a top-level;statement terminator:;, it looked like ordinary code, so the gate accepted the tail and the line rewrite deleted the second statement.;as a dangling continuation.Fix
gem_line_tail_blocks_edit: a;outside strings and brackets ends the statement. If anything other than more;s, whitespace or a#comment follows, it refuses with "another statement follows the declaration on its line". Otherwise scanning stops there, so the end-of-code checks see the declaration without its terminator.formats::gem::gemfile(the shared option reader both rewriters use since Fix gem source-option guard missing git sources (#652) #731):trailing_optionsandsource_optionfirst drop that terminator via the newwithout_statement_end, keeping a trailing comment and any;inside strings or the comment. Without that,"1.0";would read as a positional argument and be kept, and a bare; # ctail would fail closed as an unreadable, source-selecting option.There is one fix point for both modes. The npm/pypi/gem wrappers have no Gemfile logic and need no change.
Ported from #878 (commit 5b9953d):
mainis red onutils::digest::tests::production_digests_go_through_the_helpers, because #646 added inline digest calls that #865's guard rejects. This port routes them throughutils::digestand becomes a no-op once #878 lands.Test evidence
;-joined second declaration deleted (hosted)patch::redirect::tests::gemfile_semicolon_joined_declarations_fail_closedrainbowgone)patch::redirect::tests::gem_line_tail_semicolon_statementsvendor::gem::tests::plan_gemfile_edit_semicolon_statements;refused (hosted)patch::redirect::tests::gemfile_trailing_semicolon_declaration_rewritesformats::gem::gemfile::tests::trailing_options_drop_the_statement_terminator;-joined refused, project still installse2e_redirect_gem_build::gem_hosted_semicolon_joined_declarations_are_refused_and_still_install;redirects, fresh checkout installs patched bytese2e_redirect_gem_build::gem_hosted_trailing_semicolon_declaration_redirects_and_installsCommands run locally on the merge with main
9c43dfc(Linux, Ruby 3.3.6, Bundler 4.0.17, toolchain 1.93.1):cargo test -p socket-patch-core --all-features --lib: 5251 passed. The 4 failures (relax_loop_must_not_traverse_symlinked_root,an_unremovable_hidden_lock_keeps_every_store_entry,wire_write_failure_maps_error_and_leaves_lock_untouched,wire_failure_rolls_back_already_written_files) all rely on permission bits, which have no effect when the container runs as root.SOCKET_PATCH_BUNDLER_E2E_REQUIRED=1 cargo test -p socket-patch-cli --all-features --test e2e_redirect_gem_build --test e2e_vendor_gem_build -- --ignored: 18 + 8 passed.cargo clippy --workspace --all-features -- -D warnings: clean.cargo fmt --all -- --check: clean for every hunk in this PR.mainitself has pre-existing rustfmt diffs elsewhere, which CI doesn't check, and this PR leaves them alone.CI on 5b9953d: all 12 workflows are green. The macOS/ubuntu/Windows jobs that were cancelled without ever getting a runner, and the one Bun Windows
workspace-nested vendoredcell, passed on a single re-run. Bugbot is clean at 5b9953d. On 2026-10-06 one Bunnative (macos-latest, 0.8.1)job had sat queued with no runner since the previous re-run. A second re-run of that workflow passed, and every check on 5b9953d is now green.Follow-ups
None for #826. Parenthesized calls ending in
);(gem("x", "1");) are still refused. That fails closed with a warning, and nobody has reported it.🤖 Generated with Claude Code
https://claude.ai/code/session_017ZTJ2BGpGJYPCaQvncWGPm