Skip to content

Commit d04d9df

Browse files
Fix vendored Gemfile line endings on CRLF (#1821) (#1822)
* Start fix for #1821 Assisted-by: Claude Code:claude-opus-5-5 * Keep a CRLF Gemfile CRLF when vendoring gems Vendoring a gem into a Gemfile with CRLF line endings wrote the rewired `gem` line, or the appended managed block, with bare LF endings, so the committed Gemfile ended up with mixed line endings. Reverting a Gemfile that was re-checked-out in the other style also put the original line back in its old style, and a managed block whose endings changed was no longer found. New and appended lines now take the line ending of the line they replace (or the file's), a re-vendor recognises its managed fence in a CRLF file, and revert restores lines in the file's current style. Fixes #1821 Assisted-by: Claude Code:claude-opus-5-5 * Test vendoring a CRLF Gemfile end to end Adds an e2e case with a real bundler: vendoring over a CRLF Gemfile keeps every line CRLF, a frozen install accepts the result, and revert restores the Gemfile and lock byte for byte. Refs #1821 Assisted-by: Claude Code:claude-opus-5-5 * Keep old vendored Gemfile lines CRLF on revert A Gemfile vendored by an older release has an LF wired line beside a CRLF original in the ledger. Revert now keeps the original's ending unless the wired line's ending changed since vendor wrote it, so such files still come back byte for byte. A hand-restored original line also counts as reverted whatever its line ending. Refs #1821 Assisted-by: Claude Code:claude-opus-5-5 --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent dd51a3e commit d04d9df

2 files changed

Lines changed: 304 additions & 19 deletions

File tree

‎crates/socket-patch-cli/tests/e2e_vendor_gem_build.rs‎

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1941,6 +1941,87 @@ fn gem_vendor_drops_positional_constraints() {
19411941
}
19421942
}
19431943

1944+
/// #1821: vendoring over a CRLF Gemfile keeps every line CRLF (the rewired
1945+
/// `gem` line used to end in a bare LF), a real frozen install accepts the
1946+
/// result, and revert restores the Gemfile byte for byte.
1947+
#[test]
1948+
#[ignore = "host capstone: shells out to a real bundler >= 1.17; the unpinned `test` job \
1949+
skips it, the e2e job runs it with a pinned toolchain via --ignored"]
1950+
fn gem_vendor_keeps_a_crlf_gemfile_crlf() {
1951+
for gemfile in [
1952+
"source \"https://rubygems.org\"\r\n\r\ngem \"rack\", \"~> 3.1\"\r\n",
1953+
"source \"https://rubygems.org\"\r\n\r\ngem \"rack\", \"~> 3.1\", require: false # pinned\r\n",
1954+
] {
1955+
let Some((_tmp, proj, _bundler, _purl)) = staged_rack_project("crlf gemfile") else {
1956+
return;
1957+
};
1958+
std::fs::write(proj.join("Gemfile"), gemfile).unwrap();
1959+
let relock = bundle(&proj, &["install"], false);
1960+
assert!(
1961+
relock.status.success(),
1962+
"{gemfile:?} installs before vendor (test premise):\n{}",
1963+
String::from_utf8_lossy(&relock.stderr)
1964+
);
1965+
let lock_before = std::fs::read(proj.join("Gemfile.lock")).unwrap();
1966+
1967+
let (code, stdout, stderr) = run_socket(
1968+
&proj,
1969+
&[
1970+
"vendor",
1971+
"--json",
1972+
"--offline",
1973+
"--cwd",
1974+
proj.to_str().unwrap(),
1975+
],
1976+
);
1977+
assert_eq!(
1978+
code, 0,
1979+
"{gemfile:?}: vendor failed.\nstdout:\n{stdout}\nstderr:\n{stderr}"
1980+
);
1981+
let env = parse_json_envelope(&stdout);
1982+
assert_eq!(env["summary"]["applied"], 1, "{gemfile:?}: {env}");
1983+
let wired = std::fs::read_to_string(proj.join("Gemfile")).unwrap();
1984+
assert!(
1985+
wired.contains(".socket/vendor/gem/")
1986+
&& wired.matches("\r\n").count() == wired.matches('\n').count()
1987+
&& wired.matches("\r\n").count() == wired.matches('\r').count(),
1988+
"{gemfile:?}: the vendored Gemfile must stay CRLF throughout: {wired:?}"
1989+
);
1990+
1991+
let install = bundle(&proj, &["install"], true);
1992+
assert!(
1993+
install.status.success(),
1994+
"{gemfile:?}: frozen install of the rewritten Gemfile failed:\n{wired}\n{}",
1995+
String::from_utf8_lossy(&install.stderr)
1996+
);
1997+
1998+
let (code, stdout, stderr) = run_socket(
1999+
&proj,
2000+
&[
2001+
"vendor",
2002+
"--revert",
2003+
"--json",
2004+
"--cwd",
2005+
proj.to_str().unwrap(),
2006+
],
2007+
);
2008+
assert_eq!(
2009+
code, 0,
2010+
"{gemfile:?}: revert failed.\nstdout:\n{stdout}\nstderr:\n{stderr}"
2011+
);
2012+
assert_eq!(
2013+
std::fs::read_to_string(proj.join("Gemfile")).unwrap(),
2014+
gemfile,
2015+
"revert restores the Gemfile byte-identical"
2016+
);
2017+
assert_eq!(
2018+
std::fs::read(proj.join("Gemfile.lock")).unwrap(),
2019+
lock_before,
2020+
"revert restores Gemfile.lock byte-identical"
2021+
);
2022+
}
2023+
}
2024+
19442025
/// The gem library a #985 project builds: its gemspec declares rack as a
19452026
/// development dependency. Writes `Gemfile` (the given body), the gemspec
19462027
/// and its lib, then installs unfrozen.

0 commit comments

Comments
 (0)