Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 47 additions & 4 deletions crates/socket-patch-cli/tests/e2e_redirect_gem_build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,11 @@ enum Driver {
/// run must redirect nothing and attest nothing. Bundler >= 4 only; the
/// fixture asserts the contract itself and yields `None`.
ScanVexCustomLockfile,
/// [`Driver::ScanVexCustomLockfile`] with the lock named by a
/// `lockfile "custom.lock"` call at the top of the Gemfile instead of
/// the config (#1477): bundler 4's DSL spelling, which outranks every
/// `BUNDLE_LOCKFILE` tier. Bundler >= 4 only.
ScanVexCustomLockfileDsl,
/// [`Driver::ScanVex`] on a `Gemfile` + `gems.rb` twin (#751): bundler
/// 1.x loads the `Gemfile` and >= 2 loads `gems.rb`, and the scan cannot
/// see which runs, so it must redirect and attest nothing and leave all
Expand Down Expand Up @@ -523,6 +528,9 @@ impl Driver {
"scan --mode hosted (config Gemfile.next, env BUNDLE_GEMFILE=Gemfile)"
}
Driver::ScanVexCustomLockfile => "scan --mode hosted (lockfile custom.lock)",
Driver::ScanVexCustomLockfileDsl => {
"scan --mode hosted (Gemfile `lockfile \"custom.lock\"` call)"
}
Driver::ScanVexTwin => "scan --mode hosted (Gemfile + gems.rb twin)",
Driver::ScanVexGroupBlock => "scan --mode hosted (gem in a group block)",
Driver::ScanVexSourceBlock => "scan --mode hosted (gem in a source block)",
Expand Down Expand Up @@ -641,7 +649,9 @@ async fn redirect_scanned_project(
})?;
// Drivers that only mean something on one bundler line.
let only = match driver {
Driver::ScanVexCustomLockfile if !bundler.at_least(4, 0) => {
Driver::ScanVexCustomLockfile | Driver::ScanVexCustomLockfileDsl
if !bundler.at_least(4, 0) =>
{
Some("custom lockfiles need bundler >= 4")
}
_ => None,
Expand Down Expand Up @@ -976,7 +986,7 @@ async fn redirect_scanned_project(
}
// The pre-redirect (registry) pair — what reverting the patch commit
// restores; the manifest-less VEX legs revert to it.
let pristine_gemfile = std::fs::read(proj.join(gemfile_name)).unwrap();
let mut pristine_gemfile = std::fs::read(proj.join(gemfile_name)).unwrap();
let pristine_lock = lock_before.clone().into_bytes();
// Drop the pristine materialization before redirecting: bundler never
// refetches an already-installed gem, so with it in place the scan
Expand Down Expand Up @@ -1010,8 +1020,18 @@ async fn redirect_scanned_project(
String::from_utf8_lossy(&cfg.stderr)
);
}
let custom_lockfile = driver == Driver::ScanVexCustomLockfile;
if custom_lockfile {
let custom_lockfile_dsl = driver == Driver::ScanVexCustomLockfileDsl;
if custom_lockfile_dsl {
// The Gemfile's own `lockfile "custom.lock"` call, as a bundler 4
// project writes it; the default lock stays behind as a leftover.
std::fs::copy(proj.join(lock_name), proj.join("custom.lock")).unwrap();
let mut gemfile = b"lockfile \"custom.lock\"\n".to_vec();
gemfile.extend_from_slice(&pristine_gemfile);
std::fs::write(proj.join(gemfile_name), &gemfile).unwrap();
pristine_gemfile = gemfile;
}
let custom_lockfile = driver == Driver::ScanVexCustomLockfile || custom_lockfile_dsl;
if driver == Driver::ScanVexCustomLockfile {
// `bundle config set --local lockfile custom.lock`; the default
// lock stays behind as a leftover bundler 4 ignores.
std::fs::copy(proj.join(lock_name), proj.join("custom.lock")).unwrap();
Expand Down Expand Up @@ -1064,6 +1084,7 @@ async fn redirect_scanned_project(
| Driver::ScanVexMirrorSourceEnv
| Driver::ScanVexMirrorAllEnv
| Driver::ScanVexCustomLockfile
| Driver::ScanVexCustomLockfileDsl
| Driver::ScanVexTwin
| Driver::ScanVexDualBoot
| Driver::ScanVexDualBootEnvGemfile
Expand Down Expand Up @@ -1287,6 +1308,7 @@ async fn redirect_scanned_project(
Driver::ScanVexDualBoot
| Driver::ScanVexDualBootEnvGemfile
| Driver::ScanVexCustomLockfile
| Driver::ScanVexCustomLockfileDsl
| Driver::ScanVexTwin
| Driver::ScanVexDuplicateDeclaration
| Driver::ScanVexEvalGemfile
Expand Down Expand Up @@ -2615,6 +2637,27 @@ async fn gem_hosted_bundler4_custom_lockfile_redirects_nothing() {
assert!(fx.is_none(), "the custom-lockfile driver asserts in place");
}

/// #1477: bundler 4's Gemfile `lockfile "custom.lock"` call makes bundler
/// read `custom.lock` just like the setting. The hosted scan used to pin
/// the leftover `Gemfile.lock`, rewrite the Gemfile, leave `custom.lock`
/// untouched and report no refusal, so every frozen install then failed;
/// it must refuse, write nothing, and still install frozen.
#[tokio::test(flavor = "multi_thread")]
#[ignore = "host capstone: shells out to a real ruby/gem/bundler (>= 4.0 for this arm); \
the unpinned `test` job skips it, an e2e job with a pinned toolchain runs it via --ignored"]
async fn gem_hosted_bundler4_gemfile_lockfile_call_redirects_nothing() {
let fx = redirect_scanned_project(
"custom-lockfile-dsl",
Spelling::Gemfile,
true,
true,
None,
Driver::ScanVexCustomLockfileDsl,
)
.await;
assert!(fx.is_none(), "the custom-lockfile driver asserts in place");
}

/// #751: bundler 1.x loads a twin's `Gemfile` and bundler >= 2 its
/// `gems.rb`. The hosted scan used to wire `gems.rb` and attest while
/// bundler 1.17 installed the unpatched gem from the `Gemfile`; since a
Expand Down
23 changes: 23 additions & 0 deletions crates/socket-patch-cli/tests/hosted_memory_engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1101,6 +1101,29 @@ async fn a_bundler4_custom_lockfile_is_refused() {
assert_gem_lock_unsupported(&output);
}

/// #1477: bundler 4's Gemfile `lockfile "custom.lock"` call names the
/// lock bundler reads just like the setting, so the project is refused the
/// same way, with nothing written.
#[tokio::test]
async fn a_bundler4_gemfile_lockfile_call_is_refused() {
let (server, input) = gem_server_and_input().await;
let mut files = input.clone();
files.insert("custom.lock".to_string(), input["Gemfile.lock"].clone());
let mut gemfile = b"lockfile \"custom.lock\"\n".to_vec();
gemfile.extend_from_slice(&input["Gemfile"]);
files.insert("Gemfile".to_string(), gemfile);
let output = run_engine(&server, build_input(&files, &[], options(false))).await;
let project = &output.projects[0];
assert!(project.error.is_none(), "{:?}", project.error);
assert!(project.redirected.is_empty(), "{:?}", project.redirected);
assert!(
changed_paths(&output).is_empty(),
"{:?}",
changed_paths(&output)
);
assert_gem_lock_unsupported(&output);
}

/// The run says the project's gems were not scanned, rather than finding
/// none: the lock inventory's `gem_lock_unsupported` diagnosis.
fn assert_gem_lock_unsupported(output: &HostedScanOutput) {
Expand Down
Loading
Loading