From 78bc464bbd0a8667dad67809356e34c78f2a5cd8 Mon Sep 17 00:00:00 2001 From: woksin Date: Tue, 25 Aug 2026 22:16:26 +0200 Subject: [PATCH 1/2] chore: add job timeouts and pull request concurrency to workflows Jobs had no timeout-minutes, so a hung job could hold one of the organization's 20 shared hosted-runner slots for the 6 hour default - a contributor to the 2026-08-25 runner starvation. Verification workflows triggered by pull requests also lacked a concurrency group, so rapid successive pushes stacked duplicate runs instead of cancelling stale ones. Rules applied: quick checks 15 min, builds 30 min, publish/release 60 min, integration/benchmarks 120 min; cancel-in-progress only on pull-request verification workflows, never on publish, release or deploy workflows. --- .github/workflows/auto-approve-publish-deployments.yml | 1 + .github/workflows/build.yml | 6 ++++++ .github/workflows/cleanup-pr-artifacts.yml | 4 ++++ .github/workflows/deploy-production.yml | 1 + .github/workflows/documentation.yml | 1 + .github/workflows/eval.yml | 1 + .github/workflows/publish.yml | 4 ++++ .github/workflows/verify-semver-label.yml | 1 + 8 files changed, 19 insertions(+) diff --git a/.github/workflows/auto-approve-publish-deployments.yml b/.github/workflows/auto-approve-publish-deployments.yml index 921dfb3..f12cb01 100644 --- a/.github/workflows/auto-approve-publish-deployments.yml +++ b/.github/workflows/auto-approve-publish-deployments.yml @@ -17,6 +17,7 @@ permissions: jobs: approve: runs-on: ubuntu-latest + timeout-minutes: 60 steps: - name: Approve pending npm/nuget deployments env: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7438495..9a3344a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,5 +1,9 @@ name: Build +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + env: DOTNET_VERSION: "10.0.x" @@ -25,6 +29,7 @@ on: jobs: dotnet-build: runs-on: ubuntu-latest + timeout-minutes: 30 steps: - uses: actions/checkout@v4 @@ -45,6 +50,7 @@ jobs: # push; Publish still does the multi-arch build) so a broken image fails on the pull request instead. docker-build: runs-on: ubuntu-latest + timeout-minutes: 30 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/cleanup-pr-artifacts.yml b/.github/workflows/cleanup-pr-artifacts.yml index 448a2d3..3874a79 100644 --- a/.github/workflows/cleanup-pr-artifacts.yml +++ b/.github/workflows/cleanup-pr-artifacts.yml @@ -1,5 +1,9 @@ name: Cleanup PR Artifacts +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + on: pull_request: types: [closed] diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml index 5335186..41c1649 100644 --- a/.github/workflows/deploy-production.yml +++ b/.github/workflows/deploy-production.yml @@ -35,6 +35,7 @@ jobs: # (the cluster's control plane accepts connections from anywhere, so a hosted runner reaches it; # the DOTNET_INSTALL_DIR workaround below is then unnecessary but harmless). runs-on: [self-hosted, linux, cratis] + timeout-minutes: 60 # If the `production` environment carries required reviewers, the deploy pauses for approval — # which is the desired behavior for a production rollout, but it will not proceed unattended. environment: production diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 0fe1b96..e684329 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -18,6 +18,7 @@ permissions: jobs: trigger: runs-on: ubuntu-latest + timeout-minutes: 15 steps: - name: Trigger Documentation Build uses: peter-evans/repository-dispatch@v3 diff --git a/.github/workflows/eval.yml b/.github/workflows/eval.yml index f81d2d4..acb6f7d 100644 --- a/.github/workflows/eval.yml +++ b/.github/workflows/eval.yml @@ -40,6 +40,7 @@ jobs: github.event_name == 'workflow_dispatch' || contains(github.event.pull_request.labels.*.name, 'eval') runs-on: ubuntu-latest + timeout-minutes: 30 # Postgres + pgvector, matching docker-compose.yml (pgvector/pgvector:pg17, db/user/pass all `prompter`). services: diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ab0b14f..cf0897f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -25,6 +25,7 @@ permissions: jobs: release: runs-on: ubuntu-latest + timeout-minutes: 60 outputs: version: ${{ steps.release.outputs.version }} publish: ${{ steps.release.outputs.should-publish }} @@ -44,6 +45,7 @@ jobs: if: needs.release.outputs.publish == 'true' needs: [release] runs-on: ubuntu-latest + timeout-minutes: 60 steps: - uses: actions/checkout@v4 @@ -81,6 +83,7 @@ jobs: if: needs.release.outputs.publish == 'true' needs: [release] runs-on: ubuntu-latest + timeout-minutes: 60 outputs: ready: ${{ steps.check.outputs.ready }} @@ -121,6 +124,7 @@ jobs: # and failing on those would make this job noise that everyone learns to ignore. if: always() && needs.release.result == 'success' && contains(fromJSON('["no-label", "error"]'), needs.release.outputs.reason) runs-on: ubuntu-latest + timeout-minutes: 60 needs: [release] steps: diff --git a/.github/workflows/verify-semver-label.yml b/.github/workflows/verify-semver-label.yml index cb60751..1b4407b 100644 --- a/.github/workflows/verify-semver-label.yml +++ b/.github/workflows/verify-semver-label.yml @@ -29,6 +29,7 @@ permissions: jobs: verify: runs-on: ubuntu-latest + timeout-minutes: 15 steps: - name: Require exactly one semantic version label From bbb2348065c6e4d88300c72d2a8588032abed93f Mon Sep 17 00:00:00 2001 From: woksin Date: Tue, 25 Aug 2026 22:30:04 +0200 Subject: [PATCH 2/2] chore: retrigger checks (semver label was added after the gate first ran)