Skip to content

Retry setup-ruby once so release 504s can't fail Ruby legs - #2041

Merged
Mikola Lysenko (mikolalysenko) merged 2 commits into
mainfrom
ci-janitor/setup-ruby-retry
Nov 9, 2026
Merged

Mikola Lysenko (mikolalysenko) merged 2 commits into
mainfrom
ci-janitor/setup-ruby-retry

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Nov 9, 2026 •

Copy link
Copy Markdown
Collaborator

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:

Job Step that 504'd Covered by
lint-ecosystems install.sh SHA256SUMS fetch #2040
test (windows-latest, 1) vexctl download #2039
hosted-e2e setup-ruby (ruby-3.2.10 from ruby/ruby-builder) this PR
e2e (ubuntu, gem legs, 3.4) setup-ruby (ruby-3.4.9) this PR
e2e-full (ubuntu, gem legs, 3.3) setup-ruby this PR

The 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 with continue-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.py requires 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 shared e2e-steps anchor (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-ok is 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.
  • actionlint 1.7.7 and zizmor (offline) report the same findings on ci.yml before and after (37 pre-existing anchor parse complaints / 54 low findings); no new ones.
  • The step's own if: conditions are unchanged; the new steps only run when attempt 1's outcome is not success.

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 in ci.yml.

This applies in the shared e2e-steps anchor (all gem/bundler e2e legs) and in hosted-e2e, so short GitHub release-CDN outages longer than setup-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

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
@mikolalysenko Mikola Lysenko (mikolalysenko) added the ci-janitor Opened by the CI janitor routine (flakes, redundant tests, CI perf) label Nov 9, 2026
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

bugbot run


Generated by Claude Code

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Create PR

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.

Comment thread .github/workflows/ci.yml Resolved

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

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
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

bugbot run


Generated by Claude Code

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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.

@mikolalysenko Mikola Lysenko (mikolalysenko) added the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Nov 9, 2026
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Ready for review at fe2db1c.


Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Final review brief

What it does: Splits both Setup Ruby steps (e2e gem/bundler legs and hosted-e2e) into two attempts. Attempt 1 is capped at 4 min and continue-on-error; attempt 2 runs with identical inputs only if attempt 1 didn't succeed, so a short release-CDN 504 (as in nightly run 42568829044) can't fail Ruby legs.

Risk: low. CI-only, ci.yml; same retry shape as #2040/#2042. A real setup failure still fails the leg on attempt 2.

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 continue-on-error; nothing references the old step names; branch is up to date with main; CHANGELOG.md untouched; scripts/tests/test_merge_queue_fail_fast.py 11/11 pass locally. CI: ci-ok and clippy green on fe2db1c; Bugbot's one finding on 40c541f was fixed in fe2db1c and its thread resolved; Bugbot re-reviewed fe2db1c with no findings.

Changes I made: none.

Auto-merge (squash) is armed, so approving sends it straight to the merge queue.


Generated by Claude Code

@mikolalysenko
Mikola Lysenko (mikolalysenko) added this pull request to the merge queue Nov 9, 2026
Merged via the queue into main with commit 53286d4 Nov 9, 2026
51 checks passed
@mikolalysenko
Mikola Lysenko (mikolalysenko) deleted the ci-janitor/setup-ruby-retry branch November 9, 2026 19:45
Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Nov 9, 2026
All three merged to main while #2038 was in the merge queue and ship
in 5.1.0.

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-janitor Opened by the CI janitor routine (flakes, redundant tests, CI perf) Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants