From 9a017c17e104f1fdb8e6c82cb9379cb814504766 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 9 Oct 2026 04:46:51 +0000 Subject: [PATCH] Retry sbt vendored e2e warm-up on Central blips The vendored sbt capstones warm each test's build with a real `sbt update` that fetches commons-text 1.10.0 and commons-lang3 3.12.0 from Maven Central (the image's warm seed does not carry them). A single Central CDN blip there ("Error downloading org.apache.commons:commons-text:1.10.0 / Not found") fails every test of the leg, as on main run 37878372896 (sbt 1.13.0 / jdk 17 / vendored, 8/8 tests failed in the warm-up). Retry only the warm-up, only when sbt reported a fetch error (Coursier, Ivy or the launcher's boot), up to 3 attempts with a 10s/20s backoff, each from a fresh home and with the build outputs removed so a miss Coursier or Ivy remembered cannot replay. Any other failure still returns at once, and the third failure still fails a required leg. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01GR8ArpeUutP3FQ6V9eb8Gd --- .../tests/e2e_sbt_vendor_build.rs | 4 +- .../tests/sbt_vendor_build_common/mod.rs | 45 +++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/crates/socket-patch-cli/tests/e2e_sbt_vendor_build.rs b/crates/socket-patch-cli/tests/e2e_sbt_vendor_build.rs index 874e195c0..57933cbbb 100644 --- a/crates/socket-patch-cli/tests/e2e_sbt_vendor_build.rs +++ b/crates/socket-patch-cli/tests/e2e_sbt_vendor_build.rs @@ -109,8 +109,8 @@ fn setup( ) .unwrap(); } - let home = SbtHome::new(&root); - let out = sbt.run(&proj, &home, &[], &["sbtVersion", "update"]); + let mut home = SbtHome::new(&root); + let out = sbt_vendor_build_common::warm_up(&sbt, &proj, &mut home); if !ok(&out) { let why = format!("warm-up `sbt update` failed:\n{}", dump(&out)); sbt_vendor_build_common::skip(suite, &why); diff --git a/crates/socket-patch-cli/tests/sbt_vendor_build_common/mod.rs b/crates/socket-patch-cli/tests/sbt_vendor_build_common/mod.rs index ff219916b..c656aee16 100644 --- a/crates/socket-patch-cli/tests/sbt_vendor_build_common/mod.rs +++ b/crates/socket-patch-cli/tests/sbt_vendor_build_common/mod.rs @@ -258,6 +258,51 @@ pub fn dump(out: &Output) -> String { ) } +/// How an sbt run reports a failed fetch from Maven Central: Coursier +/// (`Error downloading g:a:v` / `not found: https://…`), Ivy on 0.13 / +/// 1.0–1.2 (`Server access error`, `unresolved dependency`) and the +/// launcher booting a line (`Error retrieving required libraries`). +const FETCH_ERRORS: &[&str] = &[ + "Error downloading", + "download error", + "Server access error", + "unresolved dependency", + "Error retrieving required libraries", +]; + +/// The warm-up `sbt sbtVersion update` in `proj`, the one run that +/// fetches from Maven Central (the fixture's GAs are not in the image's +/// warm seed). On a CI runner Central blips: a CDN 404 or reset for an +/// artifact it serves, which failed every test of a leg at once +/// (`Error downloading org.apache.commons:commons-text:1.10.0 / Not +/// found`). A run that failed with a fetch error is retried from a fresh +/// home (Coursier and Ivy can remember a miss) and no build outputs, after +/// a backoff; any other failure returns at once. +pub fn warm_up(sbt: &Sbt, proj: &Path, home: &mut SbtHome) -> Output { + const ATTEMPTS: u64 = 3; + let mut attempt = 1; + loop { + let out = sbt.run(proj, home, &[], &["sbtVersion", "update"]); + let text = format!( + "{}{}", + String::from_utf8_lossy(&out.stdout), + String::from_utf8_lossy(&out.stderr) + ); + if ok(&out) || attempt == ATTEMPTS || !FETCH_ERRORS.iter().any(|e| text.contains(e)) { + return out; + } + eprintln!("warm-up `sbt update` attempt {attempt} failed fetching; retrying"); + std::thread::sleep(Duration::from_secs(10 * attempt)); + let dir = home.root.parent().unwrap().to_path_buf(); + std::fs::remove_dir_all(&home.root).unwrap(); + for rel in BUILD_OUTPUT_DIRS.iter().chain(&["project/target"]) { + let _ = std::fs::remove_dir_all(proj.join(rel)); + } + *home = SbtHome::new(&dir); + attempt += 1; + } +} + /// Directories sbt writes that a checkout never carries. pub const BUILD_OUTPUT_DIRS: &[&str] = &["target", ".bsp", ".bloop", ".metals", "project/project"];