diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6effb8fa..48345a07 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -124,6 +124,37 @@ jobs: # widening of this job. python: runs-on: ubuntu-latest + # Two numbers, because this job has two workloads and one flat cap has to + # be wrong for one of them. + # + # `sdk_parity` (declared above, `type: boolean, default: false`) is the + # whole difference: it switches on the planner byte-parity and + # planner-binding suites, which SKIP without an alp-sdk checkout -- 282 of + # 1163 tests. `release.yml:235` passes true; nothing else does. + # + # PR / merge_group 6-11 min measured across the last 25 successful + # runs (min 6m; p100 11m, run 32174278683) + # release tag 23 min run 31834718564, `v0.6.0-rc1` + # (1422s = 23.7m) + # + # A flat cap taken from the PR measurement passes every PR and then fails + # the tag, which is the most expensive place in this repo to be wrong. A + # flat 60 avoids that but leaves the >99% of runs that are PRs held for an + # hour against an 11-minute p100. The expression gets both: `inputs` is + # null on `pull_request`/`push`/`merge_group`, so `inputs.sdk_parity` is + # falsy there and this reads 30; on the `workflow_call` from release.yml it + # reads 60. + # + # 30 rather than 20 on the PR leg: p100 is 11m and trending up (the same + # trend visible on the release leg, 633s -> 1422s), so 20 is 1.8x headroom + # on a number that has moved. Both legs stay far under GitHub's own + # 360-minute default, which is what a hang catcher has to beat. + # + # NOTE for tan-cli#854, which generalises `parity.yml`'s timeout gate to + # every PR-triggered workflow: this value is a STRING expression, not an + # int. That gate's `isinstance(timeout, int)` assertion has to learn about + # expressions or it will red on this line. + timeout-minutes: ${{ inputs.sdk_parity && 60 || 30 }} steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: @@ -364,6 +395,9 @@ jobs: # Node's own test runner, so there is nothing to install. shim: runs-on: ubuntu-latest + # Under a minute on every run sampled, PR and release tag alike. 10 is a + # hang catcher for the pip install, not a budget. + timeout-minutes: 10 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: @@ -392,6 +426,8 @@ jobs: # measures. wheel-floor: runs-on: ubuntu-latest + # Under a minute on every run sampled. Same reasoning as `shim` above. + timeout-minutes: 10 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: @@ -465,6 +501,12 @@ jobs: workflow-security: name: zizmor · workflow security runs-on: ubuntu-latest + # This job's `name:` IS the fifth required context, and it was the one + # tan-cli#812 left unbounded while closing the other four (all of which + # come from parity.yml). A wedged run here holds the PR for GitHub's + # 360-minute default. Measured under a minute: pip install plus a + # hermetic `--no-online-audits` pass over .github/workflows/. + timeout-minutes: 10 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: diff --git a/.github/workflows/version-identity.yml b/.github/workflows/version-identity.yml index 6059b53a..ae79128c 100644 --- a/.github/workflows/version-identity.yml +++ b/.github/workflows/version-identity.yml @@ -41,6 +41,10 @@ jobs: not-a-released-version: name: TAN_VERSION is not an existing tag's runs-on: ubuntu-latest + # A `fetch-depth: 0` checkout and one tag comparison -- measured under a + # minute (run 32046563751). The only unbounded job left in a + # PR-triggered workflow after ci.yml's four above (tan-cli#844). + timeout-minutes: 10 steps: # fetch-depth: 0 — the whole point. `fetch-tags: true` with a shallow # fetch would also do, but 0 cannot be silently defeated by a future diff --git a/changelog.d/844.fixed.md b/changelog.d/844.fixed.md new file mode 100644 index 00000000..b615081f --- /dev/null +++ b/changelog.d/844.fixed.md @@ -0,0 +1,31 @@ +- **No job in a PR-triggered workflow runs unbounded any more.** tan-cli#812 + bounded `parity.yml`, which produces four of the five required contexts; the + fifth — `zizmor · workflow security`, the `name:` of `ci.yml`'s + `workflow-security` job — was still inheriting GitHub's 360-minute job + default, along with `ci.yml`'s `python`, `shim` and `wheel-floor` and + `version-identity.yml`'s `not-a-released-version`. A wedged leg on that fifth + context held the PR for six hours instead of failing in bounded time. + + `ci.yml`: `python` 30 on a PR and 60 on the release call, `shim` 10, + `wheel-floor` 10, `workflow-security` 10. `version-identity.yml`: + `not-a-released-version` 10. + +- **`ci.yml`'s `python` job carries two caps, because it has two workloads.** + The same job measures 6-11 minutes on a `pull_request`/`merge_group` run + (last 25 successful runs; p100 11m, run `32174278683`) and **23.7 minutes** + on a release tag (run `31834718564`, `v0.6.0-rc1`, 1422s), because + `release.yml:235` calls this workflow with `sdk_parity: true` and that + switches on the planner byte-parity and planner-binding suites which SKIP + without an alp-sdk checkout — 282 of 1163 tests. + + A flat cap taken from the PR measurement passes every PR and then fails the + tag, the most expensive place in this repo to be wrong; a flat 60 avoids that + but holds the >99% of runs that are PRs for an hour against an 11-minute + p100. `timeout-minutes: ${{ inputs.sdk_parity && 60 || 30 }}` gets both: + `inputs` is null on `pull_request`/`push`/`merge_group`, so the expression + reads 30 there and 60 on the `workflow_call` from `release.yml`. 30 rather + than 20 on the PR leg because the p100 is 11m and rising, the same trend the + release leg shows (633s → 1422s). + + Note for whoever generalises `parity.yml`'s timeout gate (tan-cli#854): this + value is a string expression, not an int.