Skip to content

Commit abccd46

Browse files
arnav2claude
andauthored
docs: add release-process checklist + PyPI Trusted Publisher setup (#5)
* docs(readme): make hero banner full width Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: add release-process checklist for v0.2.0+ Captures the one-time setup (GitHub pypi environment + PyPI Trusted Publisher binding + branch protection) and the per-release checklist the maintainer should run through before tagging. The pypi environment was already created via API; the PyPI-side Trusted Publisher binding still has to be added manually on pypi.org (it's not in git — see step 2 in the doc). Once that's done, tagging vX.Y.Z auto-fires the release workflow end-to-end (build → GitHub Release → PyPI publish). Also documents common failure modes (yank, hotfix, redo-on-CI-red) and the rationale for Trusted Publishing over a long-lived API token. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 9cfd321 commit abccd46

2 files changed

Lines changed: 118 additions & 3 deletions

File tree

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
<p align="center">
2-
<img src="assets/readme-hero.png" alt="ks-xlsx-parser">
3-
</p>
1+
<img src="assets/readme-hero.png" alt="ks-xlsx-parser" width="100%">
42

53
<p align="center">
64
<a href="https://github.com/knowledgestack/ks-xlsx-parser"><img src="https://img.shields.io/badge/⭐%20Star%20on%20GitHub-Support%20the%20project-047857?style=for-the-badge&logo=github&logoColor=white" alt="Star on GitHub"></a>

docs/RELEASE_PROCESS.md

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# Release process
2+
3+
This document is the **operational** companion to [`.github/workflows/release.yml`](../.github/workflows/release.yml). The workflow is tag-triggered (`v*.*.*`); pushing such a tag builds wheel + sdist, attaches a `testBench-vX.Y.Z.zip`, creates a GitHub Release, and publishes to PyPI. **All three actions are partially or fully irreversible** — PyPI in particular does not allow re-publishing a version. Run through this checklist before tagging.
4+
5+
## One-time setup
6+
7+
These have to be done once per repository and persist across releases.
8+
9+
### 1. GitHub Environment: `pypi`
10+
11+
The release workflow's `pypi` job declares `environment: pypi`. That environment must exist on the repo, otherwise the OIDC token exchange with PyPI's Trusted Publisher endpoint fails.
12+
13+
```bash
14+
# Create the empty environment (idempotent)
15+
gh api -X PUT repos/knowledgestack/ks-xlsx-parser/environments/pypi
16+
```
17+
18+
Optional but recommended after creation: open https://github.com/knowledgestack/ks-xlsx-parser/settings/environments/pypi and add a **required reviewer** so a tag push needs explicit approval before the PyPI publish step runs. This is a safety net — once a tag is pushed the release workflow auto-fires; a reviewer gate gives you one last "are you sure?" before the irreversible PyPI publish.
19+
20+
### 2. PyPI Trusted Publisher binding
21+
22+
This **cannot** be done via API or a PR — it requires logging into pypi.org as a maintainer of `ks-xlsx-parser`.
23+
24+
1. Go to https://pypi.org/manage/project/ks-xlsx-parser/settings/publishing/
25+
2. Click **Add a new publisher****GitHub**
26+
3. Fill in:
27+
- **PyPI Project Name:** `ks-xlsx-parser`
28+
- **Owner:** `knowledgestack`
29+
- **Repository:** `ks-xlsx-parser`
30+
- **Workflow filename:** `release.yml`
31+
- **Environment name:** `pypi`
32+
4. Save.
33+
34+
Verify with:
35+
36+
```bash
37+
# Should list any publishers tied to the project (requires you to be logged in)
38+
open "https://pypi.org/manage/project/ks-xlsx-parser/settings/publishing/"
39+
```
40+
41+
If you're spinning up a new project that doesn't exist on PyPI yet, the publisher has to be configured as a **pending publisher** under your account first. Same form, accessible at https://pypi.org/manage/account/publishing/.
42+
43+
### 3. Branch protection on `main`
44+
45+
CI on a PR validates {ubuntu, macOS} × Python {3.10, 3.11, 3.12} before merge. Add a branch protection rule on `main` requiring those status checks to pass before a PR is mergeable:
46+
47+
```bash
48+
gh api -X PUT repos/knowledgestack/ks-xlsx-parser/branches/main/protection \
49+
-F required_status_checks[strict]=true \
50+
-F 'required_status_checks[contexts][]=tests (ubuntu-latest / py3.10)' \
51+
-F 'required_status_checks[contexts][]=tests (ubuntu-latest / py3.11)' \
52+
-F 'required_status_checks[contexts][]=tests (ubuntu-latest / py3.12)' \
53+
-F 'required_status_checks[contexts][]=tests (macos-latest / py3.10)' \
54+
-F 'required_status_checks[contexts][]=tests (macos-latest / py3.11)' \
55+
-F 'required_status_checks[contexts][]=tests (macos-latest / py3.12)' \
56+
-F 'required_status_checks[contexts][]=testBench round-trip (ubuntu / py3.12)' \
57+
-F enforce_admins=false \
58+
-F required_pull_request_reviews[required_approving_review_count]=1 \
59+
-F restrictions= 2>/dev/null
60+
```
61+
62+
Or set in the UI: https://github.com/knowledgestack/ks-xlsx-parser/settings/branches
63+
64+
## Per-release checklist
65+
66+
For every new version `X.Y.Z`:
67+
68+
1. **Decide the version number.** Follow [SemVer](https://semver.org/). Breaking API change → major bump. New feature, no breakage → minor. Bugfix only → patch.
69+
2. **Bump version in two places** (kept in sync to avoid drift):
70+
- `pyproject.toml``version = "X.Y.Z"`
71+
- `src/ks_xlsx_parser/__init__.py``__version__ = "X.Y.Z"`
72+
3. **Write the CHANGELOG entry** under a new `## [X.Y.Z] — YYYY-MM-DD` heading in [`CHANGELOG.md`](../CHANGELOG.md). Use the section labels documented at the top of that file (Added / Changed / Fixed / Performance / Docs / Internal / ⚠️ BREAKING).
73+
4. **(Optional but recommended) Write hand-curated release notes** at `docs/launch/RELEASE_NOTES_vX.Y.Z.md`. If present, the release workflow picks it up automatically as the GitHub Release body; otherwise GitHub auto-generates from commits.
74+
5. **Run `make test` locally** and verify all tests pass.
75+
6. **Open a PR**, wait for CI green on all matrix cells, get a review, merge to `main`.
76+
7. **Tag from `main`:**
77+
```bash
78+
git checkout main && git pull
79+
git tag -a vX.Y.Z -m "vX.Y.Z — <short tagline>"
80+
git push origin vX.Y.Z
81+
```
82+
8. **Watch the workflow.** https://github.com/knowledgestack/ks-xlsx-parser/actions — the `Release` workflow should run `build``github-release``pypi`. If the `pypi` job is gated on a reviewer, approve it in the Actions UI.
83+
9. **Verify post-release:**
84+
- PyPI: https://pypi.org/project/ks-xlsx-parser/X.Y.Z/ resolves and `pip install ks-xlsx-parser==X.Y.Z` works in a fresh venv.
85+
- GitHub Release: https://github.com/knowledgestack/ks-xlsx-parser/releases/tag/vX.Y.Z shows the release notes + wheel + sdist + `testBench-vX.Y.Z.zip`.
86+
- The `[Unreleased]` heading at the top of `CHANGELOG.md` is reset to "Nothing yet" for the next cycle (manual; do this in a follow-up PR).
87+
88+
## Common failure modes
89+
90+
**`pypi` job fails with "trusted publisher" error.** The PyPI Trusted Publisher binding is missing or pointing at the wrong workflow / environment. Fix at pypi.org (step 2 above). The GitHub Release will already exist — once the binding is correct, re-run the failed job via `gh run rerun --failed`.
91+
92+
**`pypi` job fails with "version already exists".** Someone published this version manually or a previous CI run partially succeeded. PyPI does not allow republishing. Bump to the next patch (`X.Y.Z+1`), update CHANGELOG / `RELEASE_NOTES`, delete the local tag, re-tag, force-delete the remote tag if it exists (`git push --delete origin vX.Y.Z`), and re-push.
93+
94+
**CI fails on a matrix cell after a tag is pushed.** The tag-trigger workflow doesn't depend on CI — it runs in parallel. If CI is red, delete the tag (`git push --delete origin vX.Y.Z`), fix the issue on a PR, then re-tag from the merged fix. The GitHub Release artifact from the doomed run can be deleted via `gh release delete vX.Y.Z`.
95+
96+
**Wheel doesn't include something it should.** Check `MANIFEST.in` / `pyproject.toml`'s `[tool.setuptools.packages.find]`. The `py.typed` marker, `*.pyi` stubs, and any data files (`.json`, `.xlsx` fixtures) only ship if explicitly declared.
97+
98+
## Hotfix / yank
99+
100+
If a published release has a critical bug:
101+
102+
```bash
103+
# Yank from PyPI (hides from `pip install` but doesn't delete — required for cache-poisoning safety)
104+
# UI: https://pypi.org/manage/project/ks-xlsx-parser/release/X.Y.Z/
105+
106+
# Tag a hotfix
107+
git checkout main
108+
# … apply fix, bump to X.Y.Z+1, update CHANGELOG …
109+
git tag -a vX.Y.Z+1 -m "vX.Y.Z+1 — hotfix for <issue>"
110+
git push origin vX.Y.Z+1
111+
```
112+
113+
Yanked versions remain installable if pinned explicitly; `pip install ks-xlsx-parser` without a version constraint skips them. This is the safe default for accidental release of broken code.
114+
115+
## Why we use Trusted Publishing instead of an API token
116+
117+
PyPI API tokens stored as repo secrets are forge-able by anyone with write access to the workflow file. Trusted Publishing uses GitHub OIDC: only a real run of the configured workflow on the configured environment can mint a publish token, and the token is short-lived. No long-lived secret lives in the repo. Trade-off: one-time PyPI-side setup (step 2) and the environment binding (step 1) are both required.

0 commit comments

Comments
 (0)