From a49f75982b1ce788c5f1108bb24bafd8a23a1699 Mon Sep 17 00:00:00 2001 From: Torsten Mahr Date: Tue, 25 Aug 2026 00:47:13 +0200 Subject: [PATCH] fix(ci): restore the workflow_dispatch re-run path in the release pipeline PR #52 added `startsWith(github.ref, 'refs/tags/')` to publish, create-github-release and post-release-validation. That guard is wrong for this workflow: workflow_dispatch takes a *required* `tag` input and every job checks it out via `inputs.tag || github.ref`, so a dispatch is the documented way to re-run the pipeline for an existing tag. On a dispatch `github.ref` is `refs/heads/main`, so the guard was always false. The upload was skipped silently and the run still went green -- failure that looks like success. v2.0.0 needed three attempts, which is exactly when that re-run path matters. Duplicate uploads were never the risk this guard protected against: validate-release already exits 1 via "Check if tag already exists in releases". The least-privilege permissions work from #52 is kept untouched. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/workflows/release.yml | 22 ++++++++++------------ CHANGELOG.md | 7 +++++++ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7121f39..5fecd8b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -426,12 +426,13 @@ jobs: name: Publish to PyPI runs-on: ubuntu-latest needs: [validate-release, quality-gates, security-scan, build-package, generate-changelog] - # Only ever upload from a tag push. A manual `workflow_dispatch` run is a - # dry run of build + quality gates: PyPI rejects re-uploading an existing - # version, so a dispatch that reached this job would always fail. - if: | - startsWith(github.ref, 'refs/tags/') - && needs.validate-release.outputs.is-prerelease == 'false' + # `workflow_dispatch` takes a required `tag` input and every job checks that + # tag out, so a dispatch is a re-run of the pipeline for an existing tag -- + # not a dry run. Guarding on `github.ref` would therefore always be false on + # a dispatch (it is `refs/heads/main`) and silently skip the upload. + # A duplicate upload is already prevented by the "Check if tag already + # exists in releases" step in `validate-release`. + if: needs.validate-release.outputs.is-prerelease == 'false' environment: pypi # No API tokens: authentication happens via short-lived OIDC credentials. permissions: @@ -471,11 +472,8 @@ jobs: needs: [validate-release, build-package, generate-changelog, publish] # `always()` so prereleases (where `publish` is skipped) still get a GitHub # release, but never when a required upstream job actually failed. - # Tag-only for the same reason as `publish`: a manual dispatch must not try - # to create a release for a ref that is not a tag. if: | always() - && startsWith(github.ref, 'refs/tags/') && needs.validate-release.result == 'success' && needs.build-package.result == 'success' && needs.generate-changelog.result == 'success' @@ -597,9 +595,9 @@ jobs: name: Post-Release Validation runs-on: ubuntu-latest needs: [validate-release, create-github-release, publish, update-docs] - # Tag-only: on a manual dispatch there is no release to validate, so this - # would always fail with "GitHub release missing". - if: always() && startsWith(github.ref, 'refs/tags/') + # Runs for dispatch re-runs too: `validate-release` resolves the tag from the + # required `tag` input, so there is always a release to validate. + if: always() steps: - name: Validate release completion run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d122d2..c77ca10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Fixed +- Release workflow: `publish`, `create-github-release` and `post-release-validation` no + longer require `github.ref` to be a tag. `workflow_dispatch` takes a required `tag` + input and every job checks that tag out, so a dispatch is a re-run of the pipeline for + an existing tag; on a dispatch `github.ref` is `refs/heads/main`, so the guard was + always false and silently skipped the upload while the run still reported success. + Duplicate uploads remain prevented by the existing "Check if tag already exists in + releases" step in `validate-release`. - CI: removed the `paths:` filter from the `pull_request` trigger of `ci-gates.yml` and `fast-tests.yml`. These workflows provide the required status checks `Unit Tests & Coverage`, `Integration Smoke Tests` and `Quick Security Scan`; with a path filter they