From d35506ceaa827f7d98d119893852599126ff472c Mon Sep 17 00:00:00 2001 From: jan-kubica Date: Sun, 30 Aug 2026 21:09:12 +0200 Subject: [PATCH 1/2] fix: preserve PyPI trusted publisher identity --- .github/workflows/release-policy.yml | 2 +- .github/workflows/release.yml | 22 +++++++++++++++------- scripts/release-workflow.test.ts | 5 ----- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/.github/workflows/release-policy.yml b/.github/workflows/release-policy.yml index 4ad3f8b..d8ced8f 100644 --- a/.github/workflows/release-policy.yml +++ b/.github/workflows/release-policy.yml @@ -18,6 +18,6 @@ permissions: jobs: enforce: name: Enforce release boundaries - uses: stella/.github/.github/workflows/release-policy.yml@437d9d78c38114a15c368daed7d3d1b36fd6b8fc + uses: stella/.github/.github/workflows/release-policy.yml@0f814e1a0c6c7401778e661209553b6e15f8d92a permissions: contents: read diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0a28843..add6aeb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -325,25 +325,36 @@ jobs: name: Publish stella-stdnum to PyPI needs: [verify, build-wheels] if: github.ref == 'refs/heads/main' && (needs.verify.outputs.publish == 'true') - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest permissions: + contents: read id-token: write # Required only for PyPI trusted publishing. steps: - - name: Validate and publish exact Python wheel set - uses: stella/.github/.github/actions/pypi-publish-hardened@437d9d78c38114a15c368daed7d3d1b36fd6b8fc + - name: Prepare exact Python wheel set + uses: stella/.github/.github/actions/pypi-publish-hardened@0f814e1a0c6c7401778e661209553b6e15f8d92a with: expected-version: ${{ needs.verify.outputs.version }} project-name: stella-stdnum distribution-name: stella_stdnum wheel-contract: >- {"python-wheel-x86_64-unknown-linux-gnu":["manylinux_2_17_x86_64","manylinux2014_x86_64"],"python-wheel-aarch64-unknown-linux-gnu":["manylinux_2_17_aarch64","manylinux2014_aarch64"],"python-wheel-x86_64-apple-darwin":["macosx_10_12_x86_64"],"python-wheel-aarch64-apple-darwin":["macosx_11_0_arm64"],"python-wheel-x86_64-pc-windows-msvc":["win_amd64"]} + - name: Publish wheels to PyPI + uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2 + with: + packages-dir: dist + skip-existing: true + - name: Verify published PyPI files + uses: stella/.github/.github/actions/pypi-publish-hardened/verify@0f814e1a0c6c7401778e661209553b6e15f8d92a + with: + expected-version: ${{ needs.verify.outputs.version }} + project-name: stella-stdnum github-release: name: Finalize release needs: [verify, pack-native, pack-portable, publish-pypi] if: github.ref == 'refs/heads/main' && (needs.verify.outputs.publish == 'true') - uses: stella/.github/.github/workflows/npm-version-finalize.yml@437d9d78c38114a15c368daed7d3d1b36fd6b8fc + uses: stella/.github/.github/workflows/npm-version-finalize.yml@0f814e1a0c6c7401778e661209553b6e15f8d92a with: package-files: | packages/stdnum/package.json @@ -355,12 +366,9 @@ jobs: packages/stdnum-win32-x64-msvc/package.json artifact-pattern: npm-tarball-* publish-to-npm: true - update-changelog: false permissions: contents: write # Required to create the release tag and GitHub release. id-token: write # Required only for npm trusted publishing. secrets: RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }} RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} - CHANGELOG_APP_ID: ${{ secrets.CHANGELOG_APP_ID }} - CHANGELOG_APP_PRIVATE_KEY: ${{ secrets.CHANGELOG_APP_PRIVATE_KEY }} diff --git a/scripts/release-workflow.test.ts b/scripts/release-workflow.test.ts index 91cd8bf..aea674e 100644 --- a/scripts/release-workflow.test.ts +++ b/scripts/release-workflow.test.ts @@ -78,17 +78,12 @@ describe("release workflow semantics", () => { expect(jobs["github-release"]).toContain( "publish-to-npm: true", ); - expect(jobs["github-release"]).toContain( - "update-changelog: false", - ); const forwardedSecrets = [ ...jobs["github-release"].matchAll( /^ ([A-Z][A-Z0-9_]+):\s+\$\{\{ secrets\.\1 \}\}$/gm, ), ].map((match) => match[1]); expect(forwardedSecrets.toSorted(byName)).toEqual([ - "CHANGELOG_APP_ID", - "CHANGELOG_APP_PRIVATE_KEY", "RELEASE_APP_ID", "RELEASE_APP_PRIVATE_KEY", ]); From bbac4e1504ca97f1cbf8dc2e28b16bfa828679a3 Mon Sep 17 00:00:00 2001 From: jan-kubica Date: Sun, 30 Aug 2026 21:11:03 +0200 Subject: [PATCH 2/2] test: bind direct PyPI publish sequence --- scripts/pypi-wheel-set.test.ts | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/scripts/pypi-wheel-set.test.ts b/scripts/pypi-wheel-set.test.ts index e8e3a85..73b876c 100644 --- a/scripts/pypi-wheel-set.test.ts +++ b/scripts/pypi-wheel-set.test.ts @@ -27,12 +27,31 @@ const expectedContract = { }; const assertCallerContract = (source: string) => { - expect(source).toContain( - "expected-version: ${{ needs.verify.outputs.version }}", + expect(source).toMatch( + /^ uses: stella\/\.github\/\.github\/actions\/pypi-publish-hardened@[0-9a-f]{40}$/m, ); - expect(source).toContain("project-name: stella-stdnum"); - expect(source).toContain( - "distribution-name: stella_stdnum", + expect(source).toMatch( + /^ uses: pypa\/gh-action-pypi-publish@[0-9a-f]{40}/m, + ); + expect(source).toMatch( + /^ uses: stella\/\.github\/\.github\/actions\/pypi-publish-hardened\/verify@[0-9a-f]{40}$/m, + ); + expect( + source.match( + /^ expected-version: \$\{\{ needs\.verify\.outputs\.version \}\}$/gm, + ), + ).toHaveLength(2); + expect( + source.match( + /^ project-name: stella-stdnum$/gm, + ), + ).toHaveLength(2); + expect(source).toMatch( + /^ distribution-name: stella_stdnum$/m, + ); + expect(source).toMatch(/^ packages-dir: dist$/m); + expect(source).toMatch( + /^ skip-existing: true$/m, ); const contract = source.match( @@ -53,7 +72,7 @@ const assertCallerContract = (source: string) => { ); }; -test("binds the shared publisher to the exact stdnum wheel set", () => { +test("binds the publisher to the exact stdnum wheel set", () => { assertCallerContract(workflow); });