From c1b2396cfd0d53d8d344b322469b47a5bb571119 Mon Sep 17 00:00:00 2001 From: Michael Grosse Huelsewiesche Date: Fri, 25 Sep 2026 23:13:17 -0400 Subject: [PATCH] fix(ci): make the PyPI publish workflow actually runnable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three things independently prevented a release: - Both jobs were gated on `github.repository_owner == 'twilio'`, carried over from the reference implementation. This repo is under segmentio, so a published release skipped both jobs and reported success having uploaded nothing. The guard still blocks forks, just against the right owner. - The runner expression fell through to bare ubuntu-latest, which gets no runner in this org. - Tag validation required a v prefix; every tag here is bare (2.3.6, 2.3.5). Both forms are accepted now. RELEASING.md described a local twine upload and a bare tag push. Neither reaches the OIDC path, and `release: published` does not fire on a tag push at all, so it now says to cut a GitHub Release and to bump pyproject.toml — which is the file the tag is validated against. --- .github/workflows/publish.yml | 18 ++++++++++++------ RELEASING.md | 21 ++++++++++++++++----- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 115e69af..eff55315 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -10,8 +10,10 @@ env: jobs: test: name: Test - Python ${{ matrix.python-version }} - runs-on: ${{ github.repository_owner == 'twilio' && 'ubuntu-x64' || 'ubuntu-latest' }} - if: github.repository_owner == 'twilio' + runs-on: ubuntu-x64 + # Guards against forks publishing. Bare ubuntu-latest gets no runner in + # this org, so the fork branch of the old expression was unusable anyway. + if: github.repository_owner == 'segmentio' permissions: contents: read id-token: write @@ -34,8 +36,10 @@ jobs: deploy: name: Publish to PyPI needs: [test] - runs-on: ${{ github.repository_owner == 'twilio' && 'ubuntu-x64' || 'ubuntu-latest' }} - if: github.repository_owner == 'twilio' + runs-on: ubuntu-x64 + # Guards against forks publishing. Bare ubuntu-latest gets no runner in + # this org, so the fork branch of the old expression was unusable anyway. + if: github.repository_owner == 'segmentio' environment: production permissions: contents: read @@ -53,8 +57,10 @@ jobs: - name: Validate tag format and version match run: | TAG="${GITHUB_REF#refs/tags/}" - if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+ ]]; then - echo "::error::Release tag must be in the form v1.2.3 (got '$TAG')" + # This repo's tags carry no v prefix (2.3.6, 2.3.5, ...); accept both + # so the existing convention keeps working. + if [[ ! "$TAG" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+ ]]; then + echo "::error::Release tag must be X.Y.Z or vX.Y.Z (got '$TAG')" exit 1 fi VERSION="${TAG#v}" diff --git a/RELEASING.md b/RELEASING.md index e141a4ae..39549e64 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -1,9 +1,20 @@ Releasing ========= -1. Update `VERSION` in `segment/analytics/version.py` to the new version. -2. Update the `HISTORY.md` for the impending release. +Publishing happens in CI through PyPI Trusted Publishing (OIDC). There is no +PyPI token to hold locally, and `make release` is not the release path — it +uploads with a stored credential and skips provenance. + +1. Update the version in **both** `pyproject.toml` and + `segment/analytics/version.py`. The publish workflow validates the release + tag against `pyproject.toml` and fails if the two disagree. +2. Update `HISTORY.md`. 3. `git commit -am "Release X.Y.Z."` (where X.Y.Z is the new version) -4. `git tag -a X.Y.Z -m "Version X.Y.Z"` (where X.Y.Z is the new version). -5. `git push && git push --tags` -6. `make release`. +4. Open a PR and merge it to `master`. +5. Tag the merged commit and push it: + `git tag -a X.Y.Z -m "Version X.Y.Z" && git push --tags` +6. Create a **GitHub Release** for that tag. The workflow triggers on + `release: published`; pushing the tag by itself does not start it. + +The workflow then runs the test matrix, builds with `uv`, and uploads to PyPI +with `--trusted-publishing=always`.