From fa19cdb3b9419312f782d220c1601499d988e2d3 Mon Sep 17 00:00:00 2001 From: Phil Date: Sat, 25 Jul 2026 19:04:32 -0400 Subject: [PATCH] ci(release): push the version bump with a deploy key, and restrict who can release The 1.19 dispatch failed at "push code to main" with GH013: the ruleset "Main edits only by PR" requires changes to arrive by pull request, and the workflow pushes the version bump directly. Nothing was lost -- the reordering means the build and the upload had not run, so PyPI never saw 1.19 and no tag or release was created. The ruleset already lists DeployKey as a bypass actor, so checking out over SSH with a write deploy key is enough; the ruleset itself needs no change. The key lives as an environment secret of build_and_publish, and only jobs declaring that environment can read it, so the bypass is confined to this workflow. Adding github-actions[bot] to the bypass list would have been simpler and much broader: every workflow in the repository with contents: write could then push to main unreviewed. Note this is a long-lived credential and a write deploy key may push to any branch here. What is scoped is who can read it, not what it can do. Dispatch is also restricted to the maintainer. Any collaborator with write access can start a workflow_dispatch, and this one publishes to PyPI, where a version number cannot be reused once taken. The check fails rather than skipping the job: a dispatch that quietly does nothing looks too much like one that worked. Requires, before the next dispatch: - a write deploy key registered on the repository - its private half stored as RELEASE_DEPLOY_KEY in the build_and_publish environment Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/python-publish.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 6de21e4..01f53de 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -20,7 +20,23 @@ jobs: runs-on: ubuntu-latest environment: build_and_publish steps: + # Anyone with write access can dispatch a workflow, and this one publishes + # irreversibly to PyPI. Fail loudly rather than skipping, so a dispatch that + # does nothing cannot be mistaken for a release that worked. + - name: Restrict releases to the maintainer + if: github.actor != 'VonAlphaBisZulu' + run: | + echo "::error::Releases may only be dispatched by VonAlphaBisZulu (dispatched by: ${{ github.actor }})" + exit 1 + # Checked out over SSH with a write deploy key. The ruleset protecting main + # requires changes to arrive by pull request and lists DeployKey as a bypass + # actor, so the version-bump commit can only be pushed with this key. The key + # is an environment secret of build_and_publish, so no other workflow can + # read it -- unlike a bypass for github-actions[bot], which would apply to + # every workflow in the repository. - uses: actions/checkout@v4 + with: + ssh-key: ${{ secrets.RELEASE_DEPLOY_KEY }} - name: Print version number run: echo ${{ github.event.inputs.version }} - name: set up Python