From c9b1b776747cbec70e3a1ac69b3b632327661d03 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 28 Aug 2026 05:38:16 +0000 Subject: [PATCH] harden CI: pin release-pipeline actions to commit SHAs Pins every third-party action in the four release/publish workflows (publish, release-on-merge, release-canary, desktop-artifacts) to a full 40-character commit SHA with the semver it came from in a trailing comment. 25 uses: lines, no version changes, no permission changes. Includes pypa/gh-action-pypi-publish, which was on the moving release/v1 branch despite holding PyPI publish rights. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01K754j6t7Lvc2aLTJtnC3G4 --- .github/workflows/desktop-artifacts.yml | 24 ++++++++++++------------ .github/workflows/publish.yml | 10 +++++----- .github/workflows/release-canary.yml | 8 ++++---- .github/workflows/release-on-merge.yml | 8 ++++---- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/.github/workflows/desktop-artifacts.yml b/.github/workflows/desktop-artifacts.yml index 8d62c9a0e6..95b1e9f1ef 100644 --- a/.github/workflows/desktop-artifacts.yml +++ b/.github/workflows/desktop-artifacts.yml @@ -40,7 +40,7 @@ jobs: HAS_CERT: ${{ secrets.MACOS_CERT_P12_BASE64 != '' }} HAS_SIGN: ${{ secrets.MACOS_SIGN_IDENTITY != '' }} steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # Same rule as the Windows job: signing is optional everywhere except # a release tag. An unsigned .dmg is refused by Gatekeeper outright, @@ -51,7 +51,7 @@ jobs: run: | echo "::error::${{ github.ref_name }} would ship an UNSIGNED macOS build. Set MACOS_SIGN_IDENTITY + MACOS_CERT_P12_BASE64." exit 1 - - uses: actions/setup-python@v7 + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.11" @@ -218,7 +218,7 @@ jobs: desktop/sign_mac.sh "$dmg" done - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: clawmetry-macos path: dist/*.dmg @@ -264,7 +264,7 @@ jobs: HAS_AZ_SIGN: ${{ secrets.AZURE_CLIENT_ID != '' && secrets.AZ_SIGN_PROFILE != '' }} HAS_WIN_CERT: ${{ secrets.WINDOWS_CERT_PFX_BASE64 != '' && !(secrets.AZURE_CLIENT_ID != '' && secrets.AZ_SIGN_PROFILE != '') }} steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 # A tag build that quietly produces an UNSIGNED artifact is worse # than a failed build: it reaches users as "unknown publisher", and # on Windows an unsigned uninstaller stub is blocked by Smart App @@ -278,7 +278,7 @@ jobs: run: | echo "::error::${{ github.ref_name }} would ship an UNSIGNED Windows build. Set AZURE_CLIENT_ID + AZ_SIGN_PROFILE (Artifact Signing) or WINDOWS_CERT_PFX_BASE64." exit 1 - - uses: actions/setup-python@v7 + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.11" @@ -323,7 +323,7 @@ jobs: # every downstream step stay untouched. - name: Azure login (OIDC) if: env.HAS_AZ_SIGN == 'true' - uses: azure/login@v2 + uses: azure/login@7184910d9eb2b1c5e48f7073824a90609bb9b6d6 # v2.3.1 with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} @@ -504,7 +504,7 @@ jobs: $pfx = Join-Path $Env:RUNNER_TEMP "codesign.pfx" if (Test-Path $pfx) { Remove-Item $pfx -Force } - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: clawmetry-windows path: | @@ -515,8 +515,8 @@ jobs: name: Linux single-folder + AppImage runs-on: ubuntu-latest steps: - - uses: actions/checkout@v7 - - uses: actions/setup-python@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.11" @@ -666,7 +666,7 @@ jobs: - name: Smoke test the AppImage run: python3 desktop/smoke_test.py dist/clawmetry-linux.AppImage - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: clawmetry-linux path: | @@ -685,7 +685,7 @@ jobs: permissions: contents: write steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: path: artifacts @@ -701,7 +701,7 @@ jobs: # must keep the release draft rather than publish a "latest" whose # download links 404 (seen live on v0.12.674, 2026-08-10). - name: Attach to release and publish - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2 with: draft: false make_latest: "true" diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 94bfcdd854..3312ba3757 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -31,13 +31,13 @@ jobs: attestations: write steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: actions/setup-python@v7 + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.11" - - uses: actions/setup-node@v7 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: "20" @@ -97,7 +97,7 @@ jobs: # --repo vivekchand/clawmetry` works for anyone, including an auditor who # has no reason to take our word for what produced a wheel. - name: Attest build provenance - uses: actions/attest-build-provenance@v3 + uses: actions/attest-build-provenance@96278af6caaf10aea03fd8d33a09a777ca52d62f # v3.2.0 with: subject-path: "dist/*" @@ -125,7 +125,7 @@ jobs: # delete the PYPI_API_TOKEN secret and this fallback step. - name: Publish to PyPI (Trusted Publishing, attested) if: vars.PYPI_TRUSTED_PUBLISHING == 'true' - uses: pypa/gh-action-pypi-publish@release/v1 + uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2 with: attestations: true diff --git a/.github/workflows/release-canary.yml b/.github/workflows/release-canary.yml index c93baebc01..ef359529ef 100644 --- a/.github/workflows/release-canary.yml +++ b/.github/workflows/release-canary.yml @@ -51,8 +51,8 @@ jobs: outputs: version: ${{ steps.resolve.outputs.version }} steps: - - uses: actions/checkout@v7 - - uses: actions/setup-python@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.11" @@ -103,8 +103,8 @@ jobs: - os: ubuntu-latest python-version: "3.9" steps: - - uses: actions/checkout@v7 - - uses: actions/setup-python@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: ${{ matrix.python-version }} diff --git a/.github/workflows/release-on-merge.yml b/.github/workflows/release-on-merge.yml index 81b3ec1627..8aefea9df0 100644 --- a/.github/workflows/release-on-merge.yml +++ b/.github/workflows/release-on-merge.yml @@ -32,7 +32,7 @@ jobs: actions: write steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 # The MERGE COMMIT, not the branch ref: on pull_request-closed the @@ -41,7 +41,7 @@ jobs: # feature code absent). The merge_commit_sha is exact by definition. ref: ${{ github.event.pull_request.merge_commit_sha }} - - uses: actions/setup-python@v7 + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.11" @@ -193,7 +193,7 @@ jobs: # this; this path (the actual [RELEASE] publish) did not, so PyPI shipped # whatever dist happened to be committed — which lags source whenever a # v2 PR lands without rebuilding the bundle. - - uses: actions/setup-node@v7 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: "20" @@ -314,7 +314,7 @@ jobs: # lives in one file (tests/e2e/cloud-contract.mjs) and both # pipelines pick it up automatically. - name: Set up Node for cloud-contract spec - uses: actions/setup-node@v7 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: '20'