diff --git a/.github/workflows/claude-review.yml b/.github/workflows/claude-review.yml index 7ed6b0c91..3715e6137 100644 --- a/.github/workflows/claude-review.yml +++ b/.github/workflows/claude-review.yml @@ -97,7 +97,7 @@ jobs: - name: Review pull request id: claude-review - uses: anthropics/claude-code-action@bf38e86e58df9ebf3420326d019f955bb3be64dd # v1.0.225 + uses: anthropics/claude-code-action@8041f1d17e0e7d2f2cfe35002962e9f26237c0bb # v1.0.263 with: # Without this the action trades the job's OIDC token for a Claude # GitHub App installation token with write access to contents, pull diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 56a6ce7da..6226dae9b 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -31,11 +31,11 @@ jobs: uses: actions/checkout@v6 - name: Initialize CodeQL - uses: github/codeql-action/init@v4.38.0 + uses: github/codeql-action/init@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1 with: languages: ${{ matrix.language }} - name: Perform CodeQL Analysis - uses: github/codeql-action/init@v4.38.0 + uses: github/codeql-action/analyze@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1 with: category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/osv-scanner.yml b/.github/workflows/osv-scanner.yml index 3d009896d..b5afb96d0 100644 --- a/.github/workflows/osv-scanner.yml +++ b/.github/workflows/osv-scanner.yml @@ -19,7 +19,7 @@ permissions: jobs: osv-scan: name: OSV Vulnerability Scan - uses: google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@v2.6.0 + uses: google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@b1d267aea5211fa648d9d442fa64b90dbdcbdd36 # v2.7.0 with: fail-on-vuln: false scan-args: |- diff --git a/.github/workflows/release-plz.yml b/.github/workflows/release-plz.yml index 3e65aee13..f2e9a4e9d 100644 --- a/.github/workflows/release-plz.yml +++ b/.github/workflows/release-plz.yml @@ -176,8 +176,12 @@ jobs: git_tag_gpgsign: true - name: Run release-plz release - uses: release-plz/action@8e61445f0b34e7c6d985255f2f1f05a36eb350dc # v0.5 + uses: release-plz/action@b2baf303c463c223526d52a8a91b0777f052cf3f # v0.5.146 with: + # Pinned: the action default moved to CLI 0.4.1, whose changes touch + # the publish path (registry token vs Trusted Publishing lookup, + # post-publish wait). Revisit after reviewing a newer CLI. + version: "0.3.167" command: release # Required here and absent upstream: a composite action's steps do not # inherit the caller's working-directory, so without these release-plz @@ -298,8 +302,12 @@ jobs: git_tag_gpgsign: true - name: Run release-plz release (root workspace) - uses: release-plz/action@8e61445f0b34e7c6d985255f2f1f05a36eb350dc # v0.5 + uses: release-plz/action@b2baf303c463c223526d52a8a91b0777f052cf3f # v0.5.146 with: + # Pinned: the action default moved to CLI 0.4.1, whose changes touch + # the publish path (registry token vs Trusted Publishing lookup, + # post-publish wait). Revisit after reviewing a newer CLI. + version: "0.3.167" command: release manifest_path: Cargo.toml config: release-plz.toml diff --git a/scripts/__tests__/claude-review-workflow.test.mjs b/scripts/__tests__/claude-review-workflow.test.mjs index 631a9be21..3f4efda48 100644 --- a/scripts/__tests__/claude-review-workflow.test.mjs +++ b/scripts/__tests__/claude-review-workflow.test.mjs @@ -3,7 +3,7 @@ import { describe, expect, it } from 'vitest' import { readWorkflow } from './lib/workflows.mjs' const WORKFLOW = '.github/workflows/claude-review.yml' -const ACTION_SHA = 'bf38e86e58df9ebf3420326d019f955bb3be64dd' +const ACTION_SHA = '8041f1d17e0e7d2f2cfe35002962e9f26237c0bb' // v1.0.263 const gha = (expression) => `\${{ ${expression} }}` const workflow = readWorkflow(WORKFLOW) diff --git a/scripts/__tests__/codeql-workflow.test.mjs b/scripts/__tests__/codeql-workflow.test.mjs new file mode 100644 index 000000000..585a4af0a --- /dev/null +++ b/scripts/__tests__/codeql-workflow.test.mjs @@ -0,0 +1,31 @@ +import { describe, expect, it } from 'vitest' +import { readWorkflow } from './lib/workflows.mjs' + +/** + * CodeQL must run `init` and then `analyze`. + * + * `init` starts the CodeQL tracer and `analyze` builds the database, runs the + * queries and uploads the SARIF. A job with `init` twice and no `analyze` went + * green on every run while producing no code-scanning results at all: `init` + * accepts no `category` input, so the second step silently ignored it. A + * Dependabot bump carried the wrong action forward rather than catching it. + */ + +const WORKFLOW = '.github/workflows/codeql.yml' +const steps = readWorkflow(WORKFLOW).jobs.analyze.steps +const codeql = steps + .map((step) => String(step.uses ?? '')) + .filter((uses) => uses.startsWith('github/codeql-action/')) +const actionOf = (uses) => + uses.split('@')[0].replace('github/codeql-action/', '') +const refOf = (uses) => uses.split('@')[1] + +describe('CodeQL workflow', () => { + it('initialises CodeQL and then analyses, in that order', () => { + expect(codeql.map(actionOf)).toEqual(['init', 'analyze']) + }) + + it('pins init and analyze to the same codeql-action release', () => { + expect(new Set(codeql.map(refOf)).size).toBe(1) + }) +}) diff --git a/scripts/__tests__/release-plz-cli-pin.test.mjs b/scripts/__tests__/release-plz-cli-pin.test.mjs new file mode 100644 index 000000000..6d6645e68 --- /dev/null +++ b/scripts/__tests__/release-plz-cli-pin.test.mjs @@ -0,0 +1,38 @@ +import { describe, expect, it } from 'vitest' +import { readWorkflow } from './lib/workflows.mjs' + +/** + * Every release-plz step pins the release-plz CLI, and all of them pin the + * same version. + * + * Without a `version:` input `release-plz/action` installs its own default + * CLI, so a later action bump (or an edit that drops the input) silently moves + * the crates.io publish onto a CLI nobody has reviewed. The two publish lines — + * `eql-bindings` and the root stack-* crates — must also agree, or one of them + * publishes on a different CLI from the other. + */ + +const WORKFLOW = '.github/workflows/release-plz.yml' +const jobs = readWorkflow(WORKFLOW).jobs +const releasePlzSteps = Object.entries(jobs).flatMap(([job, { steps = [] }]) => + steps + .filter((step) => String(step.uses ?? '').startsWith('release-plz/action@')) + .map((step) => ({ job, name: step.name, version: step.with?.version })), +) + +describe('release-plz CLI pin', () => { + it('finds the release-plz steps it guards', () => { + expect(releasePlzSteps.length).toBeGreaterThanOrEqual(2) + }) + + it.each(releasePlzSteps)( + '$job › $name pins an exact CLI version', + ({ version }) => { + expect(String(version ?? '')).toMatch(/^\d+\.\d+\.\d+$/) + }, + ) + + it('pins the same CLI version in every release-plz step', () => { + expect(new Set(releasePlzSteps.map((step) => step.version)).size).toBe(1) + }) +}) diff --git a/scripts/lint-no-workflow-caching.mjs b/scripts/lint-no-workflow-caching.mjs index 2fbb7d88b..e70a4f87f 100644 --- a/scripts/lint-no-workflow-caching.mjs +++ b/scripts/lint-no-workflow-caching.mjs @@ -189,7 +189,8 @@ const AUDITED_ACTIONS = new Map([ // cargo-bins/cargo-binstall, release-plz/git-config) that this gate cannot // read; it opens local `./` composites only. So this is an audit of the // pinned version, not a standing property: re-read it when the pin moves. - // Read at 2eb1d8bc — all three download binaries, none touches the cache. + // Read at b2baf303 (v0.5.146) — all three download binaries, none touches + // the cache. ['release-plz/action', { cacheInput: null }], // ---- Docker, for the postgres-eql image ------------------------------