From 28a5f0496967f4e690ad3b7b46f33d30fc99c231 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Oct 2026 20:38:43 +0000 Subject: [PATCH] Retry the yarn-classic corepack probe require_yarn_classic probed `corepack yarn@ --version` once and, under SOCKET_PATCH_YARN_E2E_REQUIRED=1, panicked on any failure. A single DNS timeout on a Depot host therefore failed the yarn-classic 1.22.22 merge-queue leg on 2026-10-22, evicted #1653 and forced the green group behind it to rebuild (~32 queue-min, ~94 job-min). Retry the probe after 2 s and 5 s before treating the release as unavailable, and keep the last attempt's stderr in the SKIP/panic message instead of discarding it. A missing corepack binary is not retried, a wrong release still panics at once, and a release that stays unfetchable still fails the REQUIRED leg. Refs #1671 Co-Authored-By: Claude Opus 5.5 (1M context) --- .../tests/common/yarn_classic_vex.rs | 62 ++++++++++++------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/crates/socket-patch-cli/tests/common/yarn_classic_vex.rs b/crates/socket-patch-cli/tests/common/yarn_classic_vex.rs index 7a75a9eb8..fc6d912fe 100644 --- a/crates/socket-patch-cli/tests/common/yarn_classic_vex.rs +++ b/crates/socket-patch-cli/tests/common/yarn_classic_vex.rs @@ -35,7 +35,7 @@ use std::ffi::OsString; use std::path::Path; -use std::process::{Command, Stdio}; +use std::process::Command; use crate::vex_e2e_common::{ assert_absent, assert_attested, assert_not_attested, binary, run_vex, statements_for, @@ -112,36 +112,52 @@ pub fn skip(suite: &str, why: &str) { /// probed from a neutral temp dir (an ancestor `packageManager` field would /// make corepack refuse) with the suites' cache isolation applied by /// `isolate`. On `false` the caller returns after [`skip`]. +/// +/// A failed fetch is retried (after 2 s, then 5 s) before it counts as +/// unavailable: one DNS blip on a CI host otherwise fails the leg under +/// [`REQUIRED_ENV`]`=1` and evicts its merge-queue entry (#1671). pub fn require_yarn_classic(suite: &str, isolate: impl Fn(&mut Command)) -> bool { let spec = yarn_classic(); let want = yarn_classic_version(); let probe = tempfile::tempdir().expect("tempdir"); - let mut cmd = Command::new("corepack"); - cmd.args([spec.as_str(), "--version"]) - .current_dir(probe.path()) - .env("COREPACK_ENABLE_DOWNLOAD_PROMPT", "0"); - isolate(&mut cmd); - let got = cmd - .stderr(Stdio::null()) - .output() - .ok() - .filter(|o| o.status.success()) - .map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string()); - match got { - Some(v) if v == want => true, - Some(v) => { + let mut last_err = String::new(); + for (attempt, backoff) in [0, 2, 5].into_iter().enumerate() { + std::thread::sleep(std::time::Duration::from_secs(backoff)); + let mut cmd = Command::new("corepack"); + cmd.args([spec.as_str(), "--version"]) + .current_dir(probe.path()) + .env("COREPACK_ENABLE_DOWNLOAD_PROMPT", "0"); + isolate(&mut cmd); + let out = match cmd.output() { + Ok(out) => out, + Err(e) => { + // corepack itself is missing: retrying cannot help. + last_err = format!("cannot run corepack: {e}"); + break; + } + }; + if out.status.success() { + let got = String::from_utf8_lossy(&out.stdout).trim().to_string(); + if got == want { + return true; + } // A wrong release is never a skip: the leg would silently test // another yarn. - panic!("{suite}: `corepack {spec} --version` reported {v:?}, expected {want}"); - } - None => { - skip( - suite, - &format!("`corepack {spec}` unavailable (corepack absent or not fetchable)"), - ); - false + panic!("{suite}: `corepack {spec} --version` reported {got:?}, expected {want}"); } + last_err = format!( + "attempt {} exited {}: {}", + attempt + 1, + out.status, + String::from_utf8_lossy(&out.stderr).trim() + ); + eprintln!("{suite}: `corepack {spec} --version` failed, {last_err}"); } + skip( + suite, + &format!("`corepack {spec}` unavailable (corepack absent or not fetchable; {last_err})"), + ); + false } // ── manifest-less VEX ─────────────────────────────────────────────────