diff --git a/.github/workflows/backend-release.yml b/.github/workflows/backend-release.yml index 3d2576a8..4efe4665 100644 --- a/.github/workflows/backend-release.yml +++ b/.github/workflows/backend-release.yml @@ -28,7 +28,10 @@ jobs: contents: write outputs: version: ${{ steps.ver.outputs.version }} - created: ${{ steps.check.outputs.exists == 'false' }} + # The publish jobs run only for a release this run created. A release + # that already existed was either made by hand (its release event + # published it) or made by an earlier run: re-run that run's failed jobs. + created: ${{ steps.check.outputs.release == 'missing' }} steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: @@ -45,20 +48,30 @@ jobs: echo "version=$VERSION" >> "$GITHUB_OUTPUT" echo "Resolved version: $VERSION" - - name: Check if tag already exists + - name: Check what already exists id: check env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} VERSION: ${{ steps.ver.outputs.version }} run: | + # The tag and the release are checked separately: a run that pushed + # the tag and then failed to create the release must still create + # it (and publish) when re-run. if git ls-remote --tags --exit-code origin "refs/tags/$VERSION" >/dev/null 2>&1; then - echo "exists=true" >> "$GITHUB_OUTPUT" - echo "Tag $VERSION already exists on origin — skipping." + echo "tag=exists" >> "$GITHUB_OUTPUT" + echo "Tag $VERSION already exists on origin." else - echo "exists=false" >> "$GITHUB_OUTPUT" + echo "tag=missing" >> "$GITHUB_OUTPUT" + fi + if gh release view "$VERSION" >/dev/null 2>&1; then + echo "release=exists" >> "$GITHUB_OUTPUT" + echo "Release $VERSION already exists — nothing to publish." + else + echo "release=missing" >> "$GITHUB_OUTPUT" fi - name: Create and push tag - if: steps.check.outputs.exists == 'false' + if: steps.check.outputs.tag == 'missing' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} VERSION: ${{ steps.ver.outputs.version }} @@ -74,7 +87,7 @@ jobs: "$VERSION" - name: Create GitHub release - if: steps.check.outputs.exists == 'false' + if: steps.check.outputs.release == 'missing' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} VERSION: ${{ steps.ver.outputs.version }} diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml index c5c559cc..6f480f8a 100644 --- a/.github/workflows/pypi-publish.yml +++ b/.github/workflows/pypi-publish.yml @@ -13,9 +13,13 @@ on: workflow_call: inputs: version: - description: Version the archives must carry (the release tag) + description: Version the archives must carry (empty skips the check) type: string - required: true + default: '' + target: + description: Package index to publish to (pypi or testpypi) + type: string + default: pypi workflow_dispatch: inputs: target: @@ -61,8 +65,8 @@ jobs: if-no-files-found: error publish-pypi: - # A release event, a call from backend-release, or a manual run aimed at PyPI. - if: github.event_name == 'release' || inputs.version != '' || inputs.target == 'pypi' + # A release event, or a call or manual run aimed at PyPI (the call default). + if: github.event_name == 'release' || inputs.target == 'pypi' needs: build runs-on: ubuntu-latest environment: @@ -79,7 +83,7 @@ jobs: - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2 publish-testpypi: - if: github.event_name == 'workflow_dispatch' && inputs.target == 'testpypi' + if: inputs.target == 'testpypi' needs: build runs-on: ubuntu-latest environment: @@ -96,3 +100,6 @@ jobs: - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2 with: repository-url: https://test.pypi.org/legacy/ + # TestPyPI is a rehearsal target: a version that is already there + # must not fail the run. + skip-existing: true diff --git a/.github/workflows/tmp-release-chain-rehearsal.yml b/.github/workflows/tmp-release-chain-rehearsal.yml new file mode 100644 index 00000000..e453df04 --- /dev/null +++ b/.github/workflows/tmp-release-chain-rehearsal.yml @@ -0,0 +1,22 @@ +name: TEMPORARY release-chain rehearsal (remove before merge) + +# Exercises the exact mechanism backend-release uses to publish: a same-repo +# reusable-workflow call of pypi-publish.yml with trusted publishing, aimed at +# TestPyPI. PyPI verifies the job_workflow_ref claim against the registered +# filename, which for a reusable workflow is the called file, so this run +# either proves the call path or shows the publisher error before 0.20.0. + +on: + pull_request: + branches: [main] + +permissions: {} + +jobs: + pypi-testpypi: + uses: $/.github/workflows/pypi-publish.yml + with: + target: testpypi + permissions: + contents: read + id-token: write diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index a7ff1b31..ca565e08 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -22,4 +22,4 @@ jobs: persist-credentials: false - name: Run zizmor 🌈 - uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2 + uses: zizmorcore/zizmor-action@70fb788f84895a7701f5643d103d587e460b5c99 # v0.6.3