Skip to content

ci: publish Python SDK with trusted publishing#4

Merged
Fermionic-Lyu merged 3 commits into
mainfrom
codex/add-pypi-publish
Jul 16, 2026
Merged

ci: publish Python SDK with trusted publishing#4
Fermionic-Lyu merged 3 commits into
mainfrom
codex/add-pypi-publish

Conversation

@Fermionic-Lyu

@Fermionic-Lyu Fermionic-Lyu commented Jul 15, 2026

Copy link
Copy Markdown
Member

What changed

  • add tag-driven PyPI Trusted Publishing via OIDC with no stored API token
  • separate unprivileged test/build work from the OIDC-enabled publish job
  • require the v* tag to exactly match project.version
  • build, check, and smoke-test wheel/sdist artifacts before publication
  • add package validation to normal CI and test Python 3.14
  • update official Actions and document the release process
  • add PyPI source and issue metadata links

Release behavior

.github/workflows/publish.yml only runs on v* tag pushes. The publish job has job-scoped id-token: write, no GitHub environment, and no username/password/token input. PyPI digital attestations remain enabled by default. Duplicate versions fail instead of being skipped.

Validation

  • Python 3.11: 85 unit tests, compileall, wheel + sdist build, twine check, isolated wheel import/version check
  • Python 3.14: 85 unit tests
  • actionlint for both workflows
  • matching and mismatching tag/version cases

PyPI setup after merge

Configure the existing insforge project Trusted Publisher with organization InsForge, repository insforge-python, workflow filename publish.yml, and no environment name.


Summary by cubic

Enables PyPI Trusted Publishing via OIDC for the Python SDK on v* tags. Adds build/validation/smoke tests, Python 3.14 in CI, auto GitHub Releases for stable versions, and pins the PyPI publish action.

  • New Features

    • Tag-driven release workflow .github/workflows/publish.yml using OIDC; no stored PyPI token; attestations enabled; auto GitHub Release for stable X.Y.Z tags (prereleases skip).
    • Enforce tag equals project.version (e.g., vX.Y.Z; PEP 440 prereleases supported); duplicate versions fail.
    • Pre-publish build (wheel/sdist), unit tests, twine check, and wheel smoke import/version; CI adds Python 3.14, a package job, and updates to actions/checkout@v7 / actions/setup-python@v6.
    • Pin pypa/gh-action-pypi-publish to v1.14.0 (commit SHA); add project.urls (Source, Issues); update README with tag-based release steps.
  • Migration

    • In PyPI, configure Trusted Publisher for project insforge to organization InsForge, repository insforge-python, workflow publish.yml, with no environment name.

Written for commit 6664980. Summary will update on new commits.

Review in cubic

@Fermionic-Lyu
Fermionic-Lyu marked this pull request as ready for review July 15, 2026 23:44

@jwfing jwfing left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Adds a tag-driven PyPI Trusted Publishing (OIDC) workflow with a build/publish split, pre-publish validation, and CI coverage for Python 3.14 — a clean, well-scoped release-infra change with no blocking issues.

Requirements context

The repo uses docs/superpowers/{plans,specs}/, but the only documents there (2026-03-28-python-sdk-design.md, 2026-03-28-python-sdk.md) describe the SDK architecture and testing strategy — they contain no release/publishing/CI requirements (grep for publish|pypi|release|twine|workflow returned nothing). No matching spec/plan for release infrastructure — this PR is assessed against its own description and GitHub Actions / PyPA trusted-publishing best practice.

Note: I verified every pinned action version against the live GitHub releases API — checkout@v7 (v7.0.0), setup-python@v6 (v6.3.0), upload-artifact@v7 (v7.0.1), download-artifact@v8 (v8.0.1), and pypa/gh-action-pypi-publish@v1.14.0 are all real and current. The upload-artifact@v7download-artifact@v8 pairing is intentional and compatible: download-artifact@v8's release notes document it was built to consume upload-artifact@v7 (direct-upload / Content-Type-aware decompress), and this workflow uses the default zipped path, so the handoff works.

Findings

Critical

(none)

Suggestion

Security — SHA-pin the third-party publish action (.github/workflows/publish.yml:79)
pypa/gh-action-pypi-publish@v1.14.0 is pinned to a mutable version tag. This is the single most sensitive step in the pipeline — it mints the OIDC token and uploads to PyPI. First-party actions/* are lower risk, but for the publish action, supply-chain best practice is to pin to a full commit SHA (with the version in a trailing comment), e.g. uses: pypa/gh-action-pypi-publish@<sha> # v1.14.0. Non-blocking, but recommended for the upload step specifically.

Security — consider a protected GitHub Environment on the publish job (.github/workflows/publish.yml:65-70)
The publish job intentionally runs with no environment: (per the PR description). Trusted Publishing works fine without one, but PyPA recommends binding the publish job to a dedicated environment (e.g. pypi) with required-reviewer / deployment protection rules, and configuring the Trusted Publisher to require that environment. As written, any maintainer who can push a v* tag triggers an unattended publish with no second gate. Adding an environment is a low-cost defense-in-depth improvement. Deliberate choice by the author, so flagging as a suggestion only.

Information

  • Functionality — tag/version guard fails closed (.github/workflows/publish.yml:26-34): the exact string comparison "$RELEASE_TAG" != "v$package_version" correctly rejects mismatches, including PEP 440 edge cases (e.g. tag v1.0.0 vs version 1.0 fails safely). tomllib is stdlib on the pinned Python 3.11 — good. Minor: the open(...) handle isn't closed, negligible in a one-shot script.
  • Software engineering — no unit tests apply to workflow YAML; validation is appropriately done through the pipeline itself (compileall, twine check, isolated wheel smoke-import) and, per the PR body, actionlint. Reasonable coverage for this change type.
  • Software engineering — duplication between the new package job in ci.yml:34-61 and the build job in publish.yml:16-63 (build → twine check → smoke test). This is acceptable — the release build should be self-contained — but the shared steps could later be factored into a reusable/composite workflow if they drift.
  • Security (positive) — strong least-privilege posture: workflow-level permissions: contents: read, and id-token: write scoped only to the isolated publish job (job-level permissions: replaces the top-level set, so publish gets only id-token: write, which is correct since it never checks out source). No stored PyPI token; attestations left enabled by default. This matches PyPA's recommended split-build/publish pattern.
  • Performance — no concerns; adding the 3.14 matrix entry and a package job is negligible CI cost, concurrency with cancel-in-progress: false correctly avoids interrupting an in-flight publish.

Verdict

approved (informational — a human still gives the explicit GitHub approval). Zero Critical findings; the two Suggestions are supply-chain hardening on an already-sound trusted-publishing setup.

jwfing
jwfing previously approved these changes Jul 15, 2026

@jwfing jwfing left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - approved.

@jwfing jwfing left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - approved.

@Fermionic-Lyu
Fermionic-Lyu merged commit 80f5f24 into main Jul 16, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants