From 582583c5b94103a66b5f74c9f60684ca6bf44e05 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 8 Nov 2026 15:38:02 +0000 Subject: [PATCH] Harden the workflows and the template workflows Live workflows: - renovate.json: give the Node that renovate.yml runs on its own group and turn off its automerge. The ci check never runs Renovate, so it cannot catch a Node bump that breaks it (the Nov 3-7 outage). - ci.yml, renovate.yml: pin uv to 0.13.7 on setup-uv. Renovate tracks the input, so uv updates arrive as PRs after minimumReleaseAge instead of reaching CI the day they ship. - ci.yml: workflow_dispatch for manual runs, concurrency that cancels superseded PR runs, a 15-minute timeout, persist-credentials: false. Templates (skill/assets/ci.yml, mutation.yml; setup-standards.sh regenerated with vendor_templates and template_history): - permissions: contents: read, a timeout, persist-credentials: false; the CI template also gets the same concurrency group. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01QnzrxP1xjsiBQJYkciuDBX --- .github/workflows/ci.yml | 12 ++++++++++ .github/workflows/renovate.yml | 2 ++ python-code-standards/skill/assets/ci.yml | 11 ++++++++++ .../skill/assets/mutation.yml | 7 ++++++ .../skill/assets/setup-standards.sh | 22 ++++++++++++++++++- renovate.json | 8 +++++++ 6 files changed, 61 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0d5af33..eda1d31 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,10 +4,17 @@ on: push: branches: [main] pull_request: + workflow_dispatch: permissions: contents: read +# A newer push to the same pull request supersedes the run in progress; runs on +# main always finish, so every merged commit keeps its result. +concurrency: + group: ci-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: # Required check for Renovate's auto-merge: a dependency PR merges only when # every step passes on both systems. The commands are the README's @@ -19,6 +26,7 @@ jobs: matrix: os: [ubuntu-latest, windows-latest] runs-on: ${{ matrix.os }} + timeout-minutes: 15 defaults: run: shell: bash @@ -27,10 +35,14 @@ jobs: SOURCES: skill/tools skill/assets/conformance.py tests evals/grade.py evals/design_spec_grade.py steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Install uv uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3 with: + # Pinned so a uv release reaches CI only through a Renovate PR, after minimumReleaseAge. + version: 0.13.7 enable-cache: true - name: Sync the environment from uv.lock diff --git a/.github/workflows/renovate.yml b/.github/workflows/renovate.yml index 31e44c0..f3000d2 100644 --- a/.github/workflows/renovate.yml +++ b/.github/workflows/renovate.yml @@ -32,6 +32,8 @@ jobs: - name: Install uv uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3 + with: + version: 0.13.7 - name: Run Renovate env: diff --git a/python-code-standards/skill/assets/ci.yml b/python-code-standards/skill/assets/ci.yml index 00e0fd0..62a7250 100644 --- a/python-code-standards/skill/assets/ci.yml +++ b/python-code-standards/skill/assets/ci.yml @@ -5,13 +5,24 @@ on: branches: [main] pull_request: +permissions: + contents: read + +# A newer push to the same pull request supersedes the run in progress; runs on +# main always finish, so every merged commit keeps its result. +concurrency: + group: verify-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: verify: runs-on: ubuntu-latest + timeout-minutes: 30 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 # diff-cover compares against the base branch + persist-credentials: false - name: Install uv uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3 diff --git a/python-code-standards/skill/assets/mutation.yml b/python-code-standards/skill/assets/mutation.yml index bbb14b7..e58bc2f 100644 --- a/python-code-standards/skill/assets/mutation.yml +++ b/python-code-standards/skill/assets/mutation.yml @@ -8,11 +8,18 @@ on: - cron: "17 3 * * *" workflow_dispatch: +permissions: + contents: read + jobs: mutation: runs-on: ubuntu-latest + # A cap, not an estimate: raise it if a full run outgrows it. + timeout-minutes: 120 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Install uv uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3 diff --git a/python-code-standards/skill/assets/setup-standards.sh b/python-code-standards/skill/assets/setup-standards.sh index bf69557..52aa291 100644 --- a/python-code-standards/skill/assets/setup-standards.sh +++ b/python-code-standards/skill/assets/setup-standards.sh @@ -413,13 +413,24 @@ on: branches: [main] pull_request: +permissions: + contents: read + +# A newer push to the same pull request supersedes the run in progress; runs on +# main always finish, so every merged commit keeps its result. +concurrency: + group: verify-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: verify: runs-on: ubuntu-latest + timeout-minutes: 30 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 # diff-cover compares against the base branch + persist-credentials: false - name: Install uv uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3 @@ -1809,11 +1820,18 @@ on: - cron: "17 3 * * *" workflow_dispatch: +permissions: + contents: read + jobs: mutation: runs-on: ubuntu-latest + # A cap, not an estimate: raise it if a full run outgrows it. + timeout-minutes: 120 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Install uv uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3 @@ -2809,6 +2827,7 @@ cat >"$WORK/hashes.json" <<'HASHEOF' "1b72d5ee3708b47ee2f712c77e1df9d275245df3818b133a294c10037a6f4189", "3c619d62a2f3c3ac5dd598d52c18b541ebe5eac8f6c7e47b536f303a6210b32a", "3d74634f38daf5e61b5dba5444b97088e5de351d227419732ad5f270a5a0aa04", + "558d21d0cdc66dc41db7553bf80a553d3014f840dd718aaa5d2422aaa0779407", "ef7ddbb3b1623a416fbdf6c7ec1a8b7b6e285eee6ace5048a2533e7450c8fd64", "ff87d7a0ef4670a3a95db28c5a4144291df149a8d836de5989b7d101d93188ea" ], @@ -2816,7 +2835,8 @@ cat >"$WORK/hashes.json" <<'HASHEOF' "0111fa893e4fdf60771a6b29e541669e3f5191ec49e49f059fc0c3a6c643f922", "9083970bdf2c798fd5710f8ad2d480bca6b783b5abb2e7bd1702a5192e591f52", "ab9e2e0e59a1269f37b56a1cd4466fc952f2796fed1777b20a6b0d75fce76a55", - "b626fc6c38fb861793c397fcb36b9890760041fa4e8acc23e960b686b708df91" + "b626fc6c38fb861793c397fcb36b9890760041fa4e8acc23e960b686b708df91", + "e3c58bae28c4ec86fa078d0bf29307b22d2bcb4fe772ea1f8befc8eef1829b2b" ], ".gitignore": [ "abd5c51f23b8e555fb48b5c4b28103b9a7fc7ecceacd9ff6cbb73e1ac814af21", diff --git a/renovate.json b/renovate.json index 9cde2c1..15042f2 100644 --- a/renovate.json +++ b/renovate.json @@ -38,6 +38,14 @@ { "groupName": "pytest", "matchPackageNames": ["pytest", "pytest-cov"] }, { "groupName": "ruff", "matchPackageNames": ["ruff", "astral-sh/ruff-pre-commit"] }, { "groupName": "github-actions", "groupSlug": "github-actions", "matchManagers": ["github-actions"] }, + { + "description": "The ci workflow never runs Renovate, so a passing check cannot vouch for the Node that renovate.yml runs it on; merge those bumps by hand", + "matchManagers": ["github-actions"], + "matchDepNames": ["node"], + "groupName": "node", + "groupSlug": "node", + "automerge": false + }, { "description": "setup-standards.sh embeds the skill's templates and their digests; refresh both", "matchFileNames": ["python-code-standards/skill/assets/**"],