Skip to content

Gate publish.yml on tags and move PyPI to Trusted Publishing #272

Description

@jaysonsantos

Part of the repository modernization effort.

Warning: this issue needs manual PyPI and GitHub settings changes

Register the Trusted Publisher on PyPI before you merge. A merge without that step breaks the next release.

Background

Risk 1: a floating action pin next to a PyPI token

.github/workflows/publish.yml:8 reads uses: actions/checkout@master.

This pins a mutable branch, not a tag or a commit SHA. Any commit that lands on the upstream default branch runs here at once. No review gate stands between the upstream repository and this job.

The same job holds secrets.PYPI_API_TOKEN at .github/workflows/publish.yml:38. A bad upstream commit can read that token.

Risk 2: the workflow runs on every push

.github/workflows/publish.yml:2 reads on: push. No branch filter and no tag filter exist.

Every commit on every branch runs checkout, Python setup, apt install pandoc, the README rewrite, and a full build. Only the last step checks the ref:

if: startsWith(github.ref, 'refs/tags')

The build output is thrown away on every non-tag push.

Other findings

  • .github/workflows/publish.yml:10 uses actions/setup-python@v3.
  • The workflow sets no permissions: block and no timeout-minutes:.
  • pypa/gh-action-pypi-publish@release/v1 at line 37 is correct. PyPA documents release/v1 as an intended rolling branch for this action. Do not change it.
  • Tags v0.31.3, v0.31.4, and v0.32.0 exist in git. No GitHub release exists for any of them. The newest GitHub release is v0.31.2.

Proposed workflow

name: Publish to PyPI

on:
  push:
    tags:
      - "v*"
  workflow_dispatch:

permissions:
  contents: read

jobs:
  build-and-publish:
    name: Build and publish to PyPI
    runs-on: ubuntu-latest
    timeout-minutes: 10
    environment: pypi
    permissions:
      id-token: write
    steps:
      - uses: actions/checkout@v7

      - name: Set up Python
        uses: actions/setup-python@v7
        with:
          python-version: "3.12"

      - name: Install pandoc
        run: sudo apt-get update && sudo apt-get install -y pandoc

      - name: Build PyPI long description from README and CHANGELOG
        run: |
          cat README.rst > to-pypi.rst
          echo "" >> to-pypi.rst
          pandoc -s --to rst -o /dev/stdout CHANGELOG.md | tee -a to-pypi.rst
          mv to-pypi.rst README.rst

      - name: Install build
        run: python -m pip install build

      - name: Build sdist and wheel
        run: python -m build --sdist --wheel --outdir dist/ .

      - name: Publish to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1

The password: input is gone. Trusted Publishing uses the OIDC token from id-token: write.

The tag filter v* matches the commitizen tag format. .cz.yaml sets tag_format: v$version.

Warning: keep the pandoc step

The pandoc step appends the changelog to the PyPI long description. README.rst is a symlink to the Sphinx source docs/intro.rst. The two files use different formats. reStructuredText and Markdown do not concatenate under one content type.

Keep the pandoc step. Do not convert README.rst to Markdown. That conversion breaks the Sphinx build.

The step rewrites README.rst inside the CI checkout only. It never commits the change back.

Manual steps

  1. On pypi.org, open the project. Go to Publishing. Add a trusted publisher. Set Owner jaysonsantos, Repository python-binary-memcached, Workflow publish.yml, Environment pypi.
  2. In GitHub repository settings, open Environments. Create an environment named pypi.
  3. Merge this issue.
  4. Push a tag. Confirm the publish succeeds.
  5. Delete the PYPI_API_TOKEN repository secret.

Acceptance criteria

  • The workflow triggers on push: tags: ["v*"] and on workflow_dispatch. It no longer triggers on every push.
  • The workflow uses actions/checkout@v7 and actions/setup-python@v7.
  • The job sets permissions: id-token: write and environment: pypi. The top level sets permissions: contents: read.
  • The password: input to pypa/gh-action-pypi-publish is gone.
  • The job sets timeout-minutes:.
  • A trusted publisher is registered on pypi.org.
  • A pypi environment exists in GitHub repository settings.
  • One tag push publishes to PyPI with the new flow.
  • The PYPI_API_TOKEN secret is deleted after that run succeeds.

Files to change

.github/workflows/publish.yml. The manual steps change PyPI and GitHub settings only.

Follow-up

Missing GitHub releases for v0.31.3, v0.31.4, and v0.32.0 are out of scope here. Open a separate issue to add a release step, or to create the missing releases by hand.

Activity

  1. added
    ciContinuous integration and release automation
    on Sep 8, 2026
  2. jaysonsantos commented on Sep 8, 2026

    @jaysonsantos
    OwnerAuthor

    Tracked in #277.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    ciContinuous integration and release automationmodernizationRepo modernization effort

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions