From cb52fb7edca297cb1c2e03b517f84dbe5bfe9a4d Mon Sep 17 00:00:00 2001 From: aldbr Date: Wed, 11 Nov 2026 15:52:51 +0100 Subject: [PATCH 1/3] ci: run release-please from the deployment workflow with GITHUB_TOKEN Drop MY_RELEASE_PLEASE_TOKEN, a long-lived token readable from any branch that could push tags and therefore publish to PyPI. release-please now runs as the first job of deployment.yml with the job token, and the build/publish jobs run when it reports release_created, building the new tag (hatch-vcs unchanged). This is the layout diracx-web already uses: a single workflow, because nothing done with GITHUB_TOKEN (including the release tag) starts another workflow. The workflow now only runs on pushes to the default branch, so the pypi environment must allow that branch instead of v* tags. Assisted-by: Claude:claude-opus-5-5 --- .github/workflows/deployment.yml | 30 ++++++++++++++++++++++++++-- .github/workflows/release-please.yml | 20 ------------------- 2 files changed, 28 insertions(+), 22 deletions(-) delete mode 100644 .github/workflows/release-please.yml diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 2a680e0..e913475 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -1,18 +1,44 @@ name: Publish Python 🐍 distribution 📦 to PyPI -on: push +on: + push: + branches: + - main permissions: contents: read jobs: + # Opens/updates the release PR; once that PR is merged, creates the tag and the + # GitHub release. It uses the job's own short-lived token, not a stored PAT. + # Nothing done with that token starts another workflow, so publishing happens + # in this workflow, gated on release_created (same layout as diracx-web). + release-please: + runs-on: ubuntu-latest + permissions: + contents: write + issues: write + pull-requests: write + outputs: + release_created: ${{ steps.release.outputs.release_created }} + tag_name: ${{ steps.release.outputs.tag_name }} + steps: + - uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0 + id: release + with: + token: ${{ github.token }} + release-type: simple + build: name: Build distribution 📦 + needs: release-please runs-on: ubuntu-latest - if: startsWith(github.ref, 'refs/tags/') # only publish to PyPI on tag pushes + if: needs.release-please.outputs.release_created == 'true' steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: + # Build the release tag itself so hatch-vcs reads the release version. + ref: ${{ needs.release-please.outputs.tag_name }} persist-credentials: false - name: Set up Python uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0 diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml deleted file mode 100644 index 6b268c1..0000000 --- a/.github/workflows/release-please.yml +++ /dev/null @@ -1,20 +0,0 @@ -on: - push: - branches: - - main - -permissions: - contents: write - issues: write - pull-requests: write - -name: release-please - -jobs: - release-please: - runs-on: ubuntu-latest - steps: - - uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0 - with: - token: ${{ secrets.MY_RELEASE_PLEASE_TOKEN }} - release-type: simple From b595f8c11f124469c9d42a0aba359e51e1426ef3 Mon Sep 17 00:00:00 2001 From: aldbr Date: Wed, 11 Nov 2026 16:46:38 +0100 Subject: [PATCH 2/3] ci: serialize release runs and allow re-publishing a tag - concurrency group so two quick merges cannot race to create the same release (queued runs are kept, one may wait on pypi approval) - workflow_dispatch with a tag input to rebuild and publish an existing release when the publish failed after the release was created - drop the explicit token input (it is the action's default) - install only build and readme_renderer; python -m build gets hatchling and hatch-vcs from build-system.requires in its isolated env - rename the workflow to "Release"; it now runs on every push to main Assisted-by: Claude:claude-opus-5-5 --- .github/workflows/deployment.yml | 46 ++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index e913475..5f9d3e8 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -1,19 +1,36 @@ -name: Publish Python 🐍 distribution 📦 to PyPI +name: Release on: push: branches: - main + # Rebuild and publish an existing release tag, for when a publish failed after + # release-please had already created the release. Run it from main: the pypi + # environment only accepts deployments from main. + workflow_dispatch: + inputs: + tag: + description: Existing release tag to publish (e.g. v0.1.0) + required: true + +# One release run at a time, so two quick merges cannot both try to create the +# same release. Queued runs are not cancelled: one may be waiting on the pypi +# environment approval. +concurrency: + group: release + cancel-in-progress: false permissions: contents: read jobs: # Opens/updates the release PR; once that PR is merged, creates the tag and the - # GitHub release. It uses the job's own short-lived token, not a stored PAT. - # Nothing done with that token starts another workflow, so publishing happens - # in this workflow, gated on release_created (same layout as diracx-web). + # GitHub release. It uses the action's default token (the job's GITHUB_TOKEN), + # not a stored PAT. Nothing done with that token starts another workflow, so + # publishing happens in this workflow, gated on release_created (same layout + # as diracx-web). release-please: + if: github.event_name == 'push' runs-on: ubuntu-latest permissions: contents: write @@ -26,19 +43,23 @@ jobs: - uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0 id: release with: - token: ${{ github.token }} release-type: simple build: name: Build distribution 📦 needs: release-please runs-on: ubuntu-latest - if: needs.release-please.outputs.release_created == 'true' + # !cancelled() lets this run when release-please was skipped (manual run). + if: >- + !cancelled() && ( + needs.release-please.outputs.release_created == 'true' || + github.event_name == 'workflow_dispatch' + ) steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: # Build the release tag itself so hatch-vcs reads the release version. - ref: ${{ needs.release-please.outputs.tag_name }} + ref: ${{ inputs.tag || needs.release-please.outputs.tag_name }} persist-credentials: false - name: Set up Python uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0 @@ -46,13 +67,7 @@ jobs: python-version: "3.15" - name: Installing dependencies run: | - python -m pip install \ - build \ - python-dateutil \ - pytz \ - readme_renderer[md] \ - requests \ - setuptools_scm + python -m pip install build "readme_renderer[md]" - name: Validate README for PyPI run: | python -m readme_renderer README.md -o /tmp/README.html @@ -67,6 +82,9 @@ jobs: publish: name: Publish distribution 📦 to PyPI needs: build + # Without this, a manual run skips publish too: the skipped release-please + # job further up the chain would count against it. + if: ${{ !cancelled() && needs.build.result == 'success' }} runs-on: ubuntu-latest environment: name: pypi From 3894b77ade84e73a574b717c4ef4933b5876217f Mon Sep 17 00:00:00 2001 From: aldbr Date: Wed, 11 Nov 2026 16:51:10 +0100 Subject: [PATCH 3/3] ci: trim release workflow comments Assisted-by: Claude:claude-opus-5-5 --- .github/workflows/deployment.yml | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 5f9d3e8..8287aa6 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -4,18 +4,14 @@ on: push: branches: - main - # Rebuild and publish an existing release tag, for when a publish failed after - # release-please had already created the release. Run it from main: the pypi - # environment only accepts deployments from main. + # Re-publish an existing tag after a failed publish. Run from main (pypi env). workflow_dispatch: inputs: tag: description: Existing release tag to publish (e.g. v0.1.0) required: true -# One release run at a time, so two quick merges cannot both try to create the -# same release. Queued runs are not cancelled: one may be waiting on the pypi -# environment approval. +# Don't cancel: a queued run may be waiting on pypi approval. concurrency: group: release cancel-in-progress: false @@ -24,11 +20,7 @@ permissions: contents: read jobs: - # Opens/updates the release PR; once that PR is merged, creates the tag and the - # GitHub release. It uses the action's default token (the job's GITHUB_TOKEN), - # not a stored PAT. Nothing done with that token starts another workflow, so - # publishing happens in this workflow, gated on release_created (same layout - # as diracx-web). + # Tags created with GITHUB_TOKEN trigger no workflows, so publish happens here. release-please: if: github.event_name == 'push' runs-on: ubuntu-latest @@ -49,7 +41,6 @@ jobs: name: Build distribution 📦 needs: release-please runs-on: ubuntu-latest - # !cancelled() lets this run when release-please was skipped (manual run). if: >- !cancelled() && ( needs.release-please.outputs.release_created == 'true' || @@ -58,7 +49,7 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: - # Build the release tag itself so hatch-vcs reads the release version. + # hatch-vcs needs the tag to compute the version. ref: ${{ inputs.tag || needs.release-please.outputs.tag_name }} persist-credentials: false - name: Set up Python @@ -82,15 +73,14 @@ jobs: publish: name: Publish distribution 📦 to PyPI needs: build - # Without this, a manual run skips publish too: the skipped release-please - # job further up the chain would count against it. + # A skipped release-please upstream would otherwise skip this job too. if: ${{ !cancelled() && needs.build.result == 'success' }} runs-on: ubuntu-latest environment: name: pypi url: https://pypi.org/p/intercede permissions: - id-token: write # IMPORTANT: mandatory for trusted publishing + id-token: write # trusted publishing steps: - name: Download the distribution packages uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2