Repository navigation
Retry setup-ruby once so release 504s can't fail Ruby legs - #2041
Conversation
ruby/setup-ruby fetches the prebuilt Ruby from a github.com release and gives up after about a minute of its own retries. On 2026-11-09 a ~4 minute release-CDN 504 outage failed the nightly CI run on main (run 42568829044) in hosted-e2e and two gem e2e legs at that step. Mirror the existing setup-python pattern: cap attempt 1 at 4 minutes with continue-on-error, pause 60 s, then run attempt 2, which fails the job if the download is still broken. Applied to the shared e2e-steps anchor (e2e, e2e-extended, e2e-windows, e2e-macos, e2e-full) and hosted-e2e. Job names and the ci-ok gate are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011f9aeRYC9ragXuhk4udbwz
|
bugbot run Generated by Claude Code |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Pause step breaks CoE retry contract
- Updated test_no_job_tolerates_failure to allow an optional pause step between continue-on-error attempt 1 and attempt 2, permitting the Setup Ruby retry pattern with a 60-second pause.
Or push these changes by commenting:
@cursor push 667cfb5064
Preview (667cfb5064)
diff --git a/scripts/tests/test_merge_queue_fail_fast.py b/scripts/tests/test_merge_queue_fail_fast.py
--- a/scripts/tests/test_merge_queue_fail_fast.py
+++ b/scripts/tests/test_merge_queue_fail_fast.py
@@ -131,7 +131,12 @@
continue
self.assertRegex(step, r"^name: .* \(attempt 1\)\n", step[:80])
sid = re.search(r"(?m)^ id: (\S+)$", step).group(1)
- retry = steps[i + 1]
+ # Allow an optional pause step before attempt 2.
+ next_step = steps[i + 1]
+ if re.search(r"^name: .* \(pause before attempt 2\)\n", next_step):
+ retry = steps[i + 2]
+ else:
+ retry = next_step
self.assertRegex(retry, r"^name: .* \(attempt 2\)\n", retry[:80])
self.assertIn(f"steps.{sid}.outcome != 'success'", retry, retry[:80])
self.assertEqual(You can send follow-ups to the cloud agent here.
test_merge_queue_fail_fast.py requires a continue-on-error attempt 1 to be retried by the very next step, so the sleep step in between failed lint-ecosystems. Attempt 2 now follows attempt 1 directly and gets setup-ruby's own ~1 min of backoff retries again. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011f9aeRYC9ragXuhk4udbwz
|
bugbot run 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 fe2db1c. Configure here.
|
Ready for review at
Generated by Claude Code |
Final review briefWhat it does: Splits both Risk: low. CI-only, Look here:
Verified: read the full diff; attempt 2's inputs match attempt 1's in both jobs; attempt 2 is the very next step with no Changes I made: none. Auto-merge (squash) is armed, so approving sends it straight to the merge queue. Generated by Claude Code |

Problem
The nightly CI run on main on 2026-11-09 (run 42568829044) failed 5 jobs, all on github.com release-download 504s between 05:43 and 05:47 UTC:
install.shSHA256SUMS fetchThe same outage window also failed the coverage shards of #2018 (vexctl, covered by #2039). setup-ruby retries internally only about 55-65 s (
Waiting 12 seconds… Waiting 20 seconds… Took 65.25 seconds) before failing the step, so an outage of a few minutes fails every Ruby leg. These jobs also run in the merge queue, where a failure evicts the entry.Root cause
A single setup-ruby attempt that cannot outlast a short release-CDN outage.
Fix
Mirror the
Setup Python (attempt 1/2)pattern already in ci.yml: attempt 1 is capped at 4 min withcontinue-on-error: true; if it did not succeed, the very next step runs attempt 2 (with setup-ruby's own ~1 min of backoff again;test_merge_queue_fail_fast.pyrequires the retry to follow immediately, so there is no pause step), which fails the job if the download is still broken (a second failure is not a blip). Applied in the sharede2e-stepsanchor (e2e, e2e-extended, e2e-windows, e2e-macos, e2e-full) and in hosted-e2e. Inputs are identical across attempts. No job, matrix or required-check names change;ci-okis untouched. Success path cost: zero (the extra steps are skipped).Proof
python3 -B -m unittest discover -s scripts/tests: 411 tests OK (incl. the fail-fast retry contract).python3 scripts/check-tool-pins.py .github/workflows/ci.yml: all pinned.if:conditions are unchanged; the new steps only run when attempt 1's outcome is notsuccess.Tests moved or removed
None.
https://claude.ai/code/session_011f9aeRYC9ragXuhk4udbwz
Note
Low Risk
Workflow-only resilience change; Ruby version and bundler install steps are unchanged, with no application code touched.
Overview
CI now runs Setup Ruby in two back-to-back steps (attempt 1 with a 4-minute cap and
continue-on-error, attempt 2 only if the first did not succeed), matching the existing Setup Python retry pattern inci.yml.This applies in the shared
e2e-stepsanchor (all gem/bundler e2e legs) and in hosted-e2e, so short GitHub release-CDN outages longer thansetup-ruby's internal retries are less likely to fail Ruby legs and evict merge-queue entries. Successful runs pay no extra cost because attempt 2 is skipped.Reviewed by Cursor Bugbot for commit fe2db1c. Configure here.
Generated by Claude Code