feat: add PyPI publishing workflow and readme metadata#2915
Conversation
- Add readme = "README.md" to pyproject.toml for PyPI project description - Add manual publish-pypi.yml workflow using trusted publishers (OIDC) - Update release.yml install instructions to prefer PyPI The publish workflow is manually triggered after a release, checks out the specified tag, verifies version consistency, builds with uv, and publishes using trusted publishing (no API tokens required). Prerequisites before first use: - Take ownership of the specify-cli PyPI project (#2908) - Create a 'pypi' environment in repo settings - Configure trusted publisher on PyPI for this repo/workflow Closes #2908 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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
- Add actions: read permission (required for artifact upload/download) - Move version check after uv install and use uv run python (ensures Python >=3.11 with tomllib is available regardless of runner image) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
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. |
PyPI does not host images from the repository, so relative paths like ./media/logo.webp render as broken images. Switch to absolute github.com/ghraw 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>
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.
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>
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.
- 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>
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.
- 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>
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.
- 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>
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.
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>
There was a problem hiding this comment.
⚠️ Not ready to approve
The updated install instructions should consistently use uv tool install specify-cli@latest (per the PR/issue goal) to avoid stale installs due to uv caching.
Copilot's findings
- Files reviewed: 5/5 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.
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>
There was a problem hiding this comment.
⚠️ Not ready to approve
A few user-facing instructions and workflow safety/clarity issues should be addressed to keep release docs reproducible and the publish workflow fail-fast on missing artifacts.
Copilot's findings
- Files reviewed: 5/5 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.
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>
There was a problem hiding this comment.
⚠️ Not ready to approve
README/docs currently present PyPI installs as “official” even though the PR/issue context indicates PyPI ownership/publishing may not be in place at merge time, which can mislead users into installing an unaffiliated package.
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.
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>
There was a problem hiding this comment.
⚠️ Not ready to approve
There are moderate documentation/consistency issues (doc contradicts itself, and release-notes snippet doesn’t match the PR’s stated “prefer @latest” guidance) that should be resolved before approval.
Copilot's findings
- Files reviewed: 5/5 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.
| > [!NOTE] | ||
| > The `specify-cli` package is also available on [PyPI](https://pypi.org/project/specify-cli/), published by the [github/spec-kit](https://github.com/github/spec-kit) maintainers. PyPI publishing is performed after each GitHub release and may lag briefly. Source installs from the GitHub repository are always available immediately. |
| Or from [PyPI](https://pypi.org/project/specify-cli/) (may lag briefly after release): | ||
|
|
||
| \`\`\`bash | ||
| uv tool install 'specify-cli==${VERSION_NO_V}' | ||
| \`\`\` |
| fi | ||
|
|
||
| - name: Checkout release tag | ||
| uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 |

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 PyPI.github/workflows/release.yml— updates install instructions to preferuv tool install specify-cli@latestPublish Workflow Design
workflow_dispatch) — run after the release workflow completesv0.10.1)pyproject.tomlversionid-token: writepermissionpypiGitHub environment for deployment gatingPrerequisites Before First Use
specify-cliPyPI project (pending attestation 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.