Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ name: Publish
on:
push:
tags: ['v*']
workflow_dispatch: {}
# Packaging check for every PR targeting the release branch: build the
# sdist/wheel and publish to TestPyPI before a release tag is cut.
pull_request:
branches: [master]

permissions:
contents: write
Expand Down Expand Up @@ -44,7 +47,7 @@ jobs:

publish-testpypi:
needs: build
if: github.event_name == 'workflow_dispatch'
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- name: Download dist
Expand All @@ -53,11 +56,15 @@ jobs:
name: dist
path: dist/

- name: Publish to TestPyPI
- name: Publish to TestPyPI (Trusted Publishing / OIDC)
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
repository-url: https://test.pypi.org/legacy/
# No `password`: uses OIDC Trusted Publishing configured on
# TestPyPI for the pull_request subject claim.
# Concurrent PRs share the same package version and TestPyPI
# rejects re-uploads of existing files; skip instead of failing.
skip-existing: true

publish-pypi:
needs: build
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- The publish workflow now runs on every pull request targeting `master`: it
builds the sdist and wheel and publishes them to TestPyPI (`skip-existing`),
validating packaging before a release tag is cut.

## [5.0.0] - 2026-09-16

### Added
Expand Down
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include requirements/*.txt
10 changes: 8 additions & 2 deletions docs/release.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,14 @@ The ``publish`` workflow (``.github/workflows/publish.yml``) runs when a
``PYPI_API_TOKEN`` secret is supported as a fallback.
4. Creates a GitHub Release with the matching changelog section.

A manual **TestPyPI** run is available from *Actions → Publish → Run workflow*
(uses a ``TEST_PYPI_API_TOKEN`` secret) to validate before a real release.
On every pull request targeting ``master``, the same build step runs and the
artifacts are published to **TestPyPI** via Trusted Publishing (OIDC — the
publisher's subject claim must be
``repo:FormalLanguageConstrainedPathQuerying/CFPQ_Data:pull_request``), with
``skip-existing`` so concurrent PRs sharing a version do not collide. This is
a packaging check that fails the PR before a release tag is cut; since a tag
always points at a merged PR's head, it validates exactly the code that will
be released.

Prerequisites (one-time, owner action)
--------------------------------------
Expand Down
36 changes: 36 additions & 0 deletions tasks/detailed_plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,39 @@ a docs defect.
fails — no suppression.
- Note the behavior in the "Docs build and deploy" section of
`docs/developer.rst`.

### S7: Fix sdist packaging so `python -m build` succeeds

Added 2026-09-16 at the publish gate. The first real `python -m build` run
(publish workflow on tag `v5.0.0`) failed: `setup.py` reads
`requirements/*.txt`, but no MANIFEST.in existed, so the files were missing
from the sdist and the sdist→wheel step raised FileNotFoundError. This is the
first packaging build in the project's history — it was never exercised
before.

**Code:** `MANIFEST.in` (new: `include requirements/*.txt`)
**Tests:** skip — verified with an isolated `python -m build` (clean venv,
same as CI); sdist contains all four requirements files and the wheel builds.
**Docs:** none

### S8: Publish to TestPyPI on every PR targeting master

Added 2026-09-16 at the merge gate (user request: validate publishing
automatically before the human merge). The `publish` workflow gains a
`pull_request: branches: [master]` trigger; the `publish-testpypi` job runs on
PRs with `skip-existing: true` because concurrent PRs share one package
version and TestPyPI rejects re-uploads of existing files.

Final design (after the first PR run failed): TestPyPI uses **OIDC Trusted
Publishing**, not a token — the first run proved the pypi-publish action
silently falls back to OIDC when no token secret exists, and the user prefers
no long-lived secrets (consistent with the PyPI setup). The publisher's
subject claim is `repo:FormalLanguageConstrainedPathQuerying/CFPQ_Data:pull_request`
(the documented sub for pull_request events; confirmed in the failed run's
claims dump). The manual `workflow_dispatch` TestPyPI path was removed: its
sub claim cannot match the PR publisher, and the PR check fully covers
pre-release validation (a tag always points at a merged PR's head).

**Code:** `.github/workflows/publish.yml`
**Tests:** skip — workflow-only; the build step is the check itself
**Docs:** `docs/release.rst` (Package publishing section), `CHANGELOG.md`
Loading