feat: add PyPI publishing workflow and readme metadata#2915
Conversation
There was a problem hiding this comment.
Pull request overview
Adds release infrastructure to publish specify-cli to PyPI and improve the package’s PyPI description rendering, aligning with the goal that uv tool install specify-cli@latest works reliably.
Changes:
- Add
readme = "README.md"topyproject.tomlso PyPI renders the README. - Introduce a manual GitHub Actions workflow to build and publish to PyPI via trusted publishing (OIDC).
- Update release notes generation to prefer
uv tool install specify-cli@latest, with a source-install fallback.
Show a summary per file
| File | Description |
|---|---|
pyproject.toml |
Adds README metadata for PyPI rendering. |
.github/workflows/publish-pypi.yml |
New manual build+publish workflow using artifacts + OIDC trusted publishing. |
.github/workflows/release.yml |
Updates generated release install instructions to prefer PyPI installs. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 3/3 changed files
- Comments generated: 3
|
Note that PyPI does not host image files, so relative image paths from your local repository (e.g., You can see this here: https://pypi.org/project/specify-cli/ , where the Spec Kit logo isn't rendered. |
There was a problem hiding this comment.
⚠️ Not ready to approve
The new publish workflow should force checkout of refs/tags/<tag> (not a potentially ambiguous ref) to avoid publishing from an unintended branch with the same name.
Copilot's findings
- Files reviewed: 4/4 changed files
- Comments generated: 1
Note
Your feedback helps us improve the quality of this feature.
Please use 👍 or 👎 to tell us whether this assessment is correct.
There was a problem hiding this comment.
⚠️ Not ready to approve
The release notes currently recommend an invalid uv tool install ...@latest command, and the publish workflow would benefit from pinning Python explicitly for deterministic builds/version parsing.
Copilot's findings
- Files reviewed: 4/4 changed files
- Comments generated: 3
Note
Your feedback helps us improve the quality of this feature.
Please use 👍 or 👎 to tell us whether this assessment is correct.
There was a problem hiding this comment.
⚠️ Not ready to approve
The new publish workflow likely won’t run as written (uv publish --trusted-publishing always), and the updated release notes can direct users to install an unaffiliated PyPI package prior to ownership transfer (supply-chain risk).
Copilot's findings
- Files reviewed: 4/4 changed files
- Comments generated: 6
Note
Your feedback helps us improve the quality of this feature.
Please use 👍 or 👎 to tell us whether this assessment is correct.
There was a problem hiding this comment.
⚠️ Not ready to approve
The updated docs/README wording implies PyPI availability/ownership immediately, but the PR itself describes prerequisites and a post-release publish step—documentation should be conditioned to avoid misleading installs right after merge.
Copilot's findings
- Files reviewed: 5/5 changed files
- Comments generated: 2
Note
Your feedback helps us improve the quality of this feature.
Please use 👍 or 👎 to tell us whether this assessment is correct.
There was a problem hiding this comment.
⚠️ Not ready to approve
The generated release notes use an invalid uv tool install version pin syntax (==), which is likely to fail when users follow the instructions.
Copilot's findings
- Files reviewed: 5/5 changed files
- Comments generated: 1
Note
Your feedback helps us improve the quality of this feature.
Please use 👍 or 👎 to tell us whether this assessment is correct.
PyPI does not host images from the repository, so relative paths like ./media/logo.webp render as broken images. Switch to absolute raw.githubusercontent.com URLs so images display on both GitHub and PyPI. Ref: pypi/warehouse#5246 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Convert remaining /media/ image path to absolute URL for PyPI - Pin release install to specific version (specify-cli==X.Y.Z) - Align setup-uv to v8.2.0 matching rest of CI Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Use job-level permissions: actions:write on build (for upload-artifact), actions:read on publish (for download-artifact) - Include both @latest and pinned version in release notes - Add note that PyPI may lag behind the GitHub release Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Build job needs contents:read for checkout (job-level perms replace workflow-level) - Clarify that PyPI publishing is manually triggered, not automatic Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move tag format validation before checkout and use refs/tags/ prefix to ensure we always check out a tag, not a branch with the same name. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Convert all relative .md links in README to absolute GitHub URLs for PyPI rendering compatibility - Fix release notes: use 'uv tool install specify-cli' (no @latest) - Pin Python 3.13 via uv python install for deterministic builds and use python3 directly instead of uv run Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Use actions/setup-python (pinned v6, Python 3.13) instead of uv python install for deterministic builds - Use python instead of python3 for setup-python compatibility - Remove unsupported --trusted-publishing always flag from uv publish (OIDC is auto-detected with id-token: write) - Update README install to lead with PyPI, source as fallback - Update installation guide: replace PyPI disclaimer with official package note, add PyPI as primary install method - Release notes: pin to exact version, clarify PyPI timing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- README: note source install is useful when PyPI version lags - Installation guide: explain PyPI follows GitHub releases and may lag briefly; source installs are always immediately available Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
uv tool install accepts PEP 508 specifiers when quoted. Add quotes around 'specify-cli==VERSION' so users can copy-paste directly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use @latest to force a fresh PyPI resolve (bypasses uv's cached tool version), matching the issue acceptance criteria. Source install remains as fallback when PyPI lags. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Release notes (versioned changelog) must always reference the specific release version, not @latest. Use 'specify-cli==VERSION' for reproducibility. Also clarify that PyPI publishing is 'performed after' (not 'follows') each release, making the manual nature clearer. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Until PyPI ownership is fully transferred and first publish is confirmed, source installs from GitHub remain the primary recommended method. PyPI install is listed as a convenient alternative. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Align actions/checkout to v7.0.0 (same SHA as test.yml/release.yml) - Remove assertion that PyPI is published by maintainers (ownership transfer still pending); keep as availability statement - Use 'once published for this release' wording in release notes - Convert remaining relative links in README to absolute URLs for PyPI rendering Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- docs/installation.md: qualify PyPI as available 'once official publishing is enabled' (ownership transfer still pending) - release.yml: use specify-cli@VERSION syntax (consistent with README/docs @latest form) - PR description updated to match Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The release.yml release notes template should not change in this PR. PyPI install instructions can be added to release notes in a future PR once publishing is confirmed working. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Do not mention PyPI in documentation until the first official PyPI release has been published. This PR only adds the workflow and readme metadata in pyproject.toml. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
38fdf33 to
2fec9c4
Compare
Add if-no-files-found: error to upload-artifact so a missing/empty dist/ directory fails the build job immediately rather than causing a confusing failure in the publish job. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update upload-artifact to v7.0.1 and download-artifact to v8.0.1, matching the pins used in the repo's gh-aw workflow lockfiles. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Summary
Adds infrastructure for publishing
specify-clito PyPI, addressing #2908.Changes
pyproject.toml— addsreadme = "README.md"so PyPI displays the project description.github/workflows/publish-pypi.yml— new manual workflow for publishing to PyPINo changes to existing workflows, README, or documentation. PyPI install instructions will be added to docs in a follow-up PR after the first successful publish.
Publish Workflow Design
workflow_dispatch) — run after the release workflow completesv0.10.1)refs/tags/prefix, and confirms tag matchespyproject.tomlversionid-token: writepermissionpypiGitHub environment for deployment gatingactions: write+contents: read) → publish (OIDC +actions: read)actions/setup-pythonfor deterministic buildsPrerequisites Before First Use
specify-cliPyPI project (pending in [Feature]: Take ownership of the specify-cli pypi.org project #2908)pypienvironment in repo settingsgithubspec-kitpublish-pypi.ymlpypiTesting
This workflow won't run until manually triggered with a valid tag, so it's safe to merge ahead of the PyPI ownership transfer.