Repository navigation
Fix vendored gems on CRLF Gemfile.lock (#1808) - #1811
Conversation
Assisted-by: Claude Code:claude-opus-5-5
A Gemfile.lock checked out with Windows line endings (Git for Windows autocrlf, or eol=crlf) made vendored gem mode fail with "Gemfile.lock has no GEM section", and the hosted-to-vendored eject dry run said the eject would succeed while the real run failed and rolled back. The lock is now edited and reverted in LF form and written back with its own CRLF endings, so the vendored pair edit, the in-sync re-run and --revert all work byte for byte. A lock that mixes CRLF and LF fails closed with a clear message. The eject and takeover dry runs now report a backend failure over the restored files, so they agree with the wet run. Fixes #1808 Assisted-by: Claude Code:claude-opus-5-5
Drop the generic eject dry-run preview change: the hosted restore already refuses a lock that mixes CRLF and LF before the vendored edit runs, so there was no reachable case to test it with. The CRLF eject test shows the dry and wet runs now agree. Note in docs/ecosystems.md that a CRLF Gemfile.lock is vendored and reverted in its own line ending and a mixed one fails closed. Assisted-by: Claude Code:claude-opus-5-5
A Gemfile.lock that mixes CRLF and LF now fails before any Gemfile check, so a user fixing a Gemfile declaration is not sent round twice. The forward edit and the revert read the lock through one helper, so they always agree on which locks are folded to LF. Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
Bundler 4.0.21 refuses a frozen install of any CRLF Gemfile.lock, even one socket-patch never edited, so the new CRLF vendor test failed on that CI leg. The test now checks the pristine CRLF lock first and only asserts a frozen install when bundler accepts the pristine one. It still checks that bundler takes the vendored lock unchanged. Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
|
Labeled Ready for review by the burn-down agent.
Slack announcement not sent this run (Slack posting unavailable); the next run should retry. Generated by Claude Code |
Final review briefWhat it does. Vendored gem mode failed with "Gemfile.lock has no GEM section" on a CRLF Risk: low. An LF lock passes through Look here
Verified. Read the full diff. Checked that Changes I made. None. Open questions. None blocking. The PR notes follow-ups: a CRLF Auto-merge is armed, so approving sends this straight to the merge queue. Generated by Claude Code |
Resolve the gem.rs import conflict with #1822, which writes Gemfile edits in the Gemfile's own line ending. The CRLF eject test now also checks that a CRLF Gemfile keeps CRLF on every line. Assisted-by: Claude Code:claude-opus-5-5
|
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 8486320. Configure here.
|
[agent] Re-enqueued once (final reviewer) after the 13:40 merge-queue eviction: main has since been merged into the branch, head 8486320 is approved, Generated by Claude Code |
LLM Description written by Claude Code:claude-opus-5-5
Fixes #1808
Summary
Vendored gem mode failed with
apply_failed("Gemfile.lock has no GEM section") on anyGemfile.lockwith CRLF line endings. Git for Windowscore.autocrlfandeol=crlfproduce such locks, and Bundler keeps CRLF on purpose. Because the hosted→vendored eject dry run never reaches that edit,vendor --dry-runandscan --mode vendored --dry-runpreviewedeject_planned(exit 0) while the real run failed and rolled back (exit 1).After this PR a CRLF lock vendors, re-runs (in-sync hot path) and
--reverts like an LF one. It's edited in LF form and written back in CRLF, so the result stays byte-exact. Dry and wet runs agree again.Root cause
vendor/gem.rsdoes its lock surgery (edit_lock, its section and entry helpers, and the revert splices) by splitting on\nand comparing lines exactly. On a CRLF lock every line keeps a trailing\r, soGEMandspecs:never match. Hosted mode already handles CRLF throughutils::line_endings. The vendored path didn't.Fix
lock_lf_form(new, shared): a CRLF lock is folded to LF and itsLineEndingsis remembered.gem_preludereads the lock through it, so every forward check sees LF text: the already-wired hot path,lock_checksum_in_syncandedit_lock. The edit is written back withLineEndings::restore.RevertFilesreads the lock through the same helper and writes it back in its own ending. Ledger records stay bare LF lines, the same as for an LF lock, so revert is byte-exact.apply_failedclass as the otheredit_lockshape failures) and writes nothing. The hosted restore already refuses such a lock the same way.Tests (red → green)
Each new test fails on
mainwith the issue's exact error and passes with the fix.vendor::gem::tests::crlf_lock_vendors_in_crlf_and_reverts_byte_exact(unit)vendor::gem::tests::crlf_checksums_lock_vendors_and_reverts_byte_exact(unit)vendor::gem::tests::mixed_line_ending_lock_fails_closed(unit)e2e_vendor_gem_build::gem_vendor_crlf_lock_fresh_checkout_and_revert(real bundler)vendordry and wet, frozen fresh-checkout install keeps the CRLF lock byte-identical, idempotent re-run, byte-exact reverte2e_redirect_gem_build::gem_hosted_pin_with_a_crlf_lock_ejects_cleanly(real bundler)vendoreject (CRLF lock only; CRLF pair) andscan --mode vendoredtakeover, dry and wet both exit 0, lock stays CRLFapply_failed+ rollback)Per-issue checklist:
apply_failed("Gemfile.lock has no GEM section") on any CRLF Gemfile.lock, and the hosted→vendoredvendor --dry-runeject still previewseject_planned#1808 symptom 1 (plain vendored, CRLF lock): unit tests above +gem_vendor_crlf_lock_fresh_checkout_and_revertapply_failed("Gemfile.lock has no GEM section") on any CRLF Gemfile.lock, and the hosted→vendoredvendor --dry-runeject still previewseject_planned#1808 symptom 2 (eject/takeover dry/wet parity on a CRLF lock):gem_hosted_pin_with_a_crlf_lock_ejects_cleanlyCommands run locally (Linux, ruby 3.3.6, bundler 4.0.18):
cargo fmt --all -- --check: okcargo clippy --workspace --all-features -- -D warnings: okcargo test -p socket-patch-cli --all-features --test e2e_vendor_gem_build --test e2e_redirect_gem_build -- --ignored: all pass on bundler 4.0.18 and 4.0.21 (4.0.21 refuses a frozen install of any CRLF lock, including a pristine one socket-patch never touched, so on that bundler the CRLF test checks a plain install keeps the vendored lock byte-identical instead)cargo test --workspace --all-features --no-fail-fast: all pass except 11 tests that also can't pass in this sandbox and don't touch gem code. 9 are write-failure tests that rely on read-only modes the container's root user ignores (in_process_redirect×3,repair×2, core libcopy_tree/vlt_heal/pypi_poetry/pypi_requirements). 2 aree2e_bun_lockbtests that need the livepatches-api.socket.dev, which the sandbox proxy blocks. CI is the authoritative run.npm/,pypi/,gem/) only dispatch to the binary, so they need no change./code-review high: findings not changed here
Gemfilegets its spliced line in LF. Since fixed on main by Fix vendored Gemfile line endings on CRLF (#1821) #1822, which this branch now merges. The CRLF eject e2e also asserts the Gemfile keeps CRLF.apply_failedinstead of a new dedicated code. That matches every otheredit_lockshape failure. A new code needs CLI_CONTRACT table changes.maindoes, so there's no behavior change for mixed locks beyond the fail-closed edit.Follow-up (not in this PR)
The eject/takeover dry-run preview (
takeover_dry_refusal) still ignores a failedDonefrom the backend. OnlyRefusedoutcomes are previewed. With CRLF fixed, no gem lock shape I could build reaches it: the hosted restore refuses mixed locks first. A generic preview of backend failures would touch every ecosystem, so it's left for a separate change.Note
Medium Risk
Changes Gemfile.lock read/write and revert logic for all vendored gem flows; behavior is well-tested but incorrect EOL handling could corrupt locks or break Bundler installs.
Overview
Fixes #1808: vendored RubyGems mode used to fail on CRLF
Gemfile.lockfiles (common on Git for Windowsautocrlfcheckouts) withapply_failed/ "Gemfile.lock has no GEM section", because lock surgery split on\nand matched section headers that still had trailing\r. Hosted mode already normalized line endings; vendored mode did not, which also made dry-run eject/takeover preview exit 0 while wet runs failed and rolled back.The vendored gem path now folds CRLF locks to LF for editing, remembers the original style via
lock_lf_form, and writes back withLineEndings::restoreon vendor and revert. Mixed CRLF/LF locks fail closed before any write. Docs note CRLF support in the gem vendored matrix cell.Coverage adds unit tests (CRLF vendor/revert, CHECKSUMS locks, mixed failure) and ignored e2e tests with real Bundler for plain
vendor, hosted→vendored eject, andscan --mode vendoredon CRLF locks (including Gemfile CRLF per #1821).Reviewed by Cursor Bugbot for commit 8486320. Configure here.
Generated by Claude Code