From 20c0002f8ec57eec56172391af967a15a2a61aa7 Mon Sep 17 00:00:00 2001 From: kingchenc Date: Thu, 11 Jun 2026 03:27:05 +0200 Subject: [PATCH] ci: cap job runtimes and auto-retry the flaky Node test step (#252) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem A Node test job wedged on a macOS runner: the **Run Node tests** step (`node --test`) hung for **over an hour** while the identical tests passed in ~1.5 min on every other runner (Node 20 macOS same run: 1m46s; Node 18 macOS on a sibling PR: 1m34s). It is a one-off stuck process — the macOS analogue of the documented `setup-node` Windows CDN flake — not a real Node 18 vs 20 difference. No job in `ci.yml` set any `timeout-minutes`, so a hung step runs toward GitHub's **6h default** before the platform kills it. ## Changes - **Job-level `timeout-minutes: 20` backstop on all 15 jobs.** The slowest real job is ~5 min, so 20 min is generous headroom (survives cold-cache spikes) while turning a 6h hang into a 20-min fail. The clock counts execution time only — queued/waiting time does not count against it. - **`Run Node tests` wrapped in `nick-fields/retry` (SHA-pinned, v4.0.0):** a hung attempt is killed after 6 min and retried once (`timeout_minutes: 6`, `max_attempts: 2`), so a one-off wedge self-heals without a manual re-run. Normal run is well under a minute; worst case 2×6 min stays under the 20-min job backstop. ## Notes - New SHA-pinned third-party action (`nick-fields/retry@ad98453…` = v4.0.0) — satisfies the SHA-pin convention; `zizmor` runs on this PR. - `ci.yml` validated as well-formed YAML; all 15 jobs confirmed to carry a job-level timeout. --- .github/workflows/ci.yml | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 951bf23f..357aabbc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,6 +34,7 @@ jobs: rust: name: Rust ${{ matrix.os }} runs-on: ${{ matrix.os }} + timeout-minutes: 20 # backstop: cap a wedged job instead of GitHub's 6h default (slowest real job ~5 min) strategy: fail-fast: false matrix: @@ -104,6 +105,7 @@ jobs: examples-smoke: name: Examples (syntax smoke) runs-on: ubuntu-latest + timeout-minutes: 20 # backstop: cap a wedged job instead of GitHub's 6h default (slowest real job ~5 min) steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -193,6 +195,7 @@ jobs: clippy-bindings: name: Clippy bindings runs-on: ubuntu-latest + timeout-minutes: 20 # backstop: cap a wedged job instead of GitHub's 6h default (slowest real job ~5 min) steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -276,6 +279,7 @@ jobs: msrv: name: ${{ matrix.name }} runs-on: ubuntu-latest + timeout-minutes: 20 # backstop: cap a wedged job instead of GitHub's 6h default (slowest real job ~5 min) strategy: fail-fast: false matrix: @@ -324,6 +328,7 @@ jobs: coverage: name: Coverage runs-on: ubuntu-latest + timeout-minutes: 20 # backstop: cap a wedged job instead of GitHub's 6h default (slowest real job ~5 min) steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -365,6 +370,7 @@ jobs: supply-chain: name: Supply-chain (cargo-deny) runs-on: ubuntu-latest + timeout-minutes: 20 # backstop: cap a wedged job instead of GitHub's 6h default (slowest real job ~5 min) steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -383,6 +389,7 @@ jobs: fuzz-smoke: name: Fuzz (smoke) runs-on: ubuntu-latest + timeout-minutes: 20 # backstop: cap a wedged job instead of GitHub's 6h default (slowest real job ~5 min) steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -436,6 +443,7 @@ jobs: python: name: Python ${{ matrix.python-version }} on ${{ matrix.os }} runs-on: ${{ matrix.os }} + timeout-minutes: 20 # backstop: cap a wedged job instead of GitHub's 6h default (slowest real job ~5 min) strategy: fail-fast: false matrix: @@ -517,6 +525,7 @@ jobs: wasm: name: WASM build runs-on: ubuntu-latest + timeout-minutes: 20 # backstop: cap a wedged job instead of GitHub's 6h default (slowest real job ~5 min) steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -560,6 +569,7 @@ jobs: node: name: Node ${{ matrix.node-version }} on ${{ matrix.os }} runs-on: ${{ matrix.os }} + timeout-minutes: 20 # backstop: cap a wedged job instead of GitHub's 6h default (slowest real job ~5 min) strategy: fail-fast: false matrix: @@ -618,13 +628,22 @@ jobs: # exist yet for win32-x64-msvc). run: npx napi build --platform --release + # The Node test process has wedged on macOS runners (the step hung for + # 1h+ while the same tests passed in ~1.5 min elsewhere). Wrap it so a + # hung attempt is killed after 6 min and retried once, rather than + # running up to the job-level backstop. A normal run is under a minute. - name: Run Node tests - working-directory: bindings/node - run: node --test __tests__/ + uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4.0.0 + with: + timeout_minutes: 6 + max_attempts: 2 + command: cd bindings/node && node --test __tests__/ + shell: bash c-abi: name: C ABI on ${{ matrix.os }} runs-on: ${{ matrix.os }} + timeout-minutes: 20 # backstop: cap a wedged job instead of GitHub's 6h default (slowest real job ~5 min) strategy: fail-fast: false matrix: @@ -680,6 +699,7 @@ jobs: csharp: name: C# on ${{ matrix.os }} runs-on: ${{ matrix.os }} + timeout-minutes: 20 # backstop: cap a wedged job instead of GitHub's 6h default (slowest real job ~5 min) strategy: fail-fast: false matrix: @@ -730,6 +750,7 @@ jobs: go: name: Go on ${{ matrix.os }} runs-on: ${{ matrix.os }} + timeout-minutes: 20 # backstop: cap a wedged job instead of GitHub's 6h default (slowest real job ~5 min) strategy: fail-fast: false matrix: @@ -852,6 +873,7 @@ jobs: r: name: R on ${{ matrix.os }} runs-on: ${{ matrix.os }} + timeout-minutes: 20 # backstop: cap a wedged job instead of GitHub's 6h default (slowest real job ~5 min) strategy: fail-fast: false matrix: @@ -923,6 +945,7 @@ jobs: java: name: Java on ${{ matrix.os }} runs-on: ${{ matrix.os }} + timeout-minutes: 20 # backstop: cap a wedged job instead of GitHub's 6h default (slowest real job ~5 min) strategy: fail-fast: false matrix: