|
2 | 2 |
|
3 | 3 | ## One-Time Setup: PyPI Trusted Publishing |
4 | 4 |
|
5 | | -Before the first release, configure PyPI Trusted Publishing: |
| 5 | +Already configured for this project — these steps are kept for reference when forking or recovering the publisher state. |
6 | 6 |
|
7 | 7 | 1. Go to https://pypi.org/manage/account/publishing/ |
8 | | -2. Add a new pending publisher: |
| 8 | +2. Add a publisher (pending or active): |
9 | 9 | - **PyPI project name**: `python-docs-mcp-server` |
10 | | - - **Owner**: your GitHub username or org |
| 10 | + - **Owner**: your GitHub username or org (this repo: `ayhammouda`) |
11 | 11 | - **Repository**: `python-docs-mcp-server` |
12 | 12 | - **Workflow name**: `release.yml` |
13 | 13 | - **Environment name**: `pypi` |
14 | 14 | 3. In the GitHub repo, go to Settings > Environments |
15 | 15 | 4. Create an environment named `pypi` |
16 | 16 | 5. (Optional) Add environment protection rules: |
17 | | - - Required reviewers (recommended for first release) |
| 17 | + - Required reviewers (recommended) |
18 | 18 | - Deployment branches: only `main` tags |
19 | 19 |
|
20 | | -## Notes |
| 20 | +## Coverage notes |
21 | 21 |
|
22 | | -**Runtime coverage:** The release workflow builds and tests against Python 3.13 only. |
23 | | -Python 3.12 is covered by the CI workflow (`ci.yml`) which runs a 2x2 matrix |
24 | | -(3.12/3.13 x ubuntu/macos) on every push to `main`. Since tags are created |
25 | | -from commits that have already passed CI, 3.12 compatibility is verified before |
26 | | -the release workflow runs. This is an accepted trade-off to keep the release |
27 | | -artifact pipeline simple (single Python version produces the wheel). |
| 22 | +**Runtime coverage:** the release workflow builds and tests against Python 3.13 only. Python 3.12 is covered by the CI workflow (`ci.yml`), which runs a 2×2 matrix (3.12/3.13 × ubuntu/macos) on every push to `main`. Since tags are created from commits that have already passed CI, 3.12 compatibility is verified before the release workflow runs. This is an accepted trade-off to keep the release artifact pipeline simple (single Python version produces the wheel). |
28 | 23 |
|
29 | | -**Documentation coverage:** The full docs index target is Python documentation |
30 | | -versions 3.10 through 3.14. |
| 24 | +**Documentation coverage:** the full docs index target is Python documentation versions 3.10 through 3.14. |
31 | 25 |
|
32 | | -## Creating a Release |
| 26 | +--- |
33 | 27 |
|
34 | | -1. Ensure all tests pass on main: |
35 | | - ```bash |
36 | | - uv run pytest --tb=short -q |
37 | | - uv run ruff check src/ tests/ |
38 | | - uv run pyright src/ |
39 | | - ``` |
| 28 | +## Release Checklist |
40 | 29 |
|
41 | | -2. Verify the version in `pyproject.toml` is correct: |
42 | | - ```bash |
43 | | - grep '^version' pyproject.toml |
44 | | - # Should show: version = "0.1.2" |
45 | | - ``` |
46 | | - |
47 | | -3. Create and push the tag: |
48 | | - ```bash |
49 | | - git tag -a v0.1.2 -m "Release v0.1.2" |
50 | | - git push origin v0.1.2 |
51 | | - ``` |
52 | | - |
53 | | -4. Monitor the release workflow at: |
54 | | - https://github.com/<owner>/python-docs-mcp-server/actions/workflows/release.yml |
55 | | - |
56 | | -5. Verify the package on PyPI: |
57 | | - https://pypi.org/project/python-docs-mcp-server/0.1.2/ |
58 | | - |
59 | | -## Post-Release Verification |
60 | | - |
61 | | -After the package is published: |
62 | | - |
63 | | -```bash |
64 | | -# In a fresh environment: |
65 | | -uvx python-docs-mcp-server --version |
66 | | -# Should print: 0.1.1 |
67 | | - |
68 | | -# Or via pipx: |
69 | | -pipx run python-docs-mcp-server --version |
70 | | -# Should print: 0.1.1 |
71 | | -``` |
72 | | - |
73 | | -## v0.1.1 Release Checklist |
74 | | - |
75 | | -Complete these steps in order. Each step has a checkbox -- do not skip ahead. |
| 30 | +Replace `X.Y.Z` below with the version you are releasing (e.g. `0.1.7`). Complete each step in order; do not skip ahead. The release workflow at `.github/workflows/release.yml` runs four jobs in sequence on every `v*` tag push: `build` → `publish` (PyPI) → `publish-mcp-registry` → `github-release`. |
76 | 31 |
|
77 | 32 | ### Pre-Release Verification |
78 | 33 |
|
79 | | -- [ ] All CI tests green on main: check https://github.com/<owner>/python-docs-mcp-server/actions/workflows/ci.yml |
| 34 | +- [ ] All CI tests green on `main`: https://github.com/ayhammouda/python-docs-mcp-server/actions/workflows/ci.yml |
80 | 35 | - [ ] Local test suite passes: |
81 | 36 | ```bash |
82 | 37 | uv run pytest --tb=short -q |
| 38 | + uv run ruff check src/ tests/ |
| 39 | + uv run pyright src/ |
83 | 40 | ``` |
84 | | -- [ ] Version in `pyproject.toml` is `0.1.1`: |
| 41 | +- [ ] All four version-bearing files agree on `X.Y.Z` (the release workflow's `Verify tag matches package version` step enforces this; matching here saves a CI round-trip): |
85 | 42 | ```bash |
86 | 43 | grep '^version' pyproject.toml |
| 44 | + python3 -c 'import json; d = json.load(open("server.json")); print("server.json:", d["version"], "/ packages[0]:", d["packages"][0]["version"])' |
| 45 | + grep '^name = "python-docs-mcp-server"' -A1 uv.lock | head -2 |
87 | 46 | ``` |
88 | 47 | - [ ] `server.json` validates against the live MCP Registry schema. MCP Registry enforces stricter limits than PyPI (e.g. `description ≤ 100 chars`); validating before the tag prevents a half-published release (PyPI succeeds while MCP Registry rejects, as happened on v0.1.5 → recovered in v0.1.6): |
89 | 48 | ```bash |
90 | 49 | curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher |
91 | 50 | ./mcp-publisher validate server.json |
92 | 51 | ``` |
93 | | - Must report `✅ server.json is valid`. If validation fails, shorten the `server.json` `description` on `main` before tagging (the `pyproject.toml` description is unbounded by this constraint and can stay longer). |
| 52 | + Must report `✅ server.json is valid`. If validation fails, shorten `server.json` `description` on `main` before tagging (the `pyproject.toml` description is unbounded by this constraint and can stay longer). |
94 | 53 | - [ ] Integration tests from `.github/INTEGRATION-TEST.md` are complete and signed off |
95 | | -- [ ] Doctor subcommand passes: |
| 54 | +- [ ] `Doctor` subcommand passes: |
96 | 55 | ```bash |
97 | 56 | uv run python-docs-mcp-server doctor |
98 | 57 | ``` |
99 | | - |
100 | | -### PyPI Trusted Publishing Setup (one-time) |
101 | | - |
102 | | -- [ ] PyPI pending publisher configured at https://pypi.org/manage/account/publishing/: |
103 | | - - PyPI project name: `python-docs-mcp-server` |
104 | | - - Owner: `<your-github-username>` |
105 | | - - Repository: `python-docs-mcp-server` |
106 | | - - Workflow name: `release.yml` |
107 | | - - Environment name: `pypi` |
108 | | -- [ ] GitHub environment `pypi` created in repo Settings > Environments |
| 58 | +- [ ] `CHANGELOG.md` has a dated entry for `[X.Y.Z]` |
109 | 59 |
|
110 | 60 | ### Tag and Release |
111 | 61 |
|
112 | | -- [ ] Create the annotated tag: |
| 62 | +- [ ] Create the annotated tag (replace `X.Y.Z` everywhere): |
113 | 63 | ```bash |
114 | | - git tag -a v0.1.1 -m "Release v0.1.1 |
115 | | -
|
116 | | - First public release of python-docs-mcp-server. |
| 64 | + git tag -a vX.Y.Z -m "Release vX.Y.Z |
117 | 65 |
|
118 | | - A read-only, version-aware MCP retrieval server over Python |
119 | | - standard library documentation (3.10 through 3.14). |
| 66 | + <one-line summary of what shipped> |
120 | 67 |
|
121 | | - Installable via: uvx python-docs-mcp-server" |
| 68 | + See CHANGELOG.md for the full entry." |
122 | 69 | ``` |
123 | 70 | - [ ] Push the tag to trigger the release workflow: |
124 | 71 | ```bash |
125 | | - git push origin v0.1.1 |
| 72 | + git push origin vX.Y.Z |
| 73 | + ``` |
| 74 | +- [ ] Watch the workflow run to completion: |
| 75 | + ```bash |
| 76 | + gh run watch $(gh run list --workflow=release.yml --limit 1 --json databaseId -q '.[0].databaseId') --exit-status |
126 | 77 | ``` |
127 | | -- [ ] Monitor the workflow run: https://github.com/<owner>/python-docs-mcp-server/actions/workflows/release.yml |
128 | | -- [ ] Verify all three jobs pass: `build` -> `publish` -> `github-release` |
| 78 | +- [ ] All four jobs green: `build` → `publish` → `publish-mcp-registry` → `github-release` |
129 | 79 |
|
130 | | -### Post-Release Verification (SHIP-06) |
| 80 | +### Post-Release Verification |
131 | 81 |
|
132 | | -- [ ] Package visible on PyPI: https://pypi.org/project/python-docs-mcp-server/0.1.2/ |
133 | | -- [ ] Attestation visible on PyPI package page (look for "Provenance" badge) |
134 | | -- [ ] Fresh install test: |
| 82 | +- [ ] PyPI listing exists with attestation badge: `https://pypi.org/project/python-docs-mcp-server/X.Y.Z/` |
| 83 | +- [ ] Fresh install test from a clean shell (cache-busted so the new version actually resolves): |
135 | 84 | ```bash |
136 | | - # Clear any cached version |
137 | 85 | uv cache clean python-docs-mcp-server 2>/dev/null || true |
138 | | - |
139 | | - # Install and verify version |
140 | | - uvx python-docs-mcp-server --version |
141 | | - # Expected output: 0.1.1 |
| 86 | + uvx --refresh python-docs-mcp-server@X.Y.Z --version |
| 87 | + # Expected: python-docs-mcp-server X.Y.Z |
142 | 88 | ``` |
143 | | -- [ ] Full README flow test (from a clean environment): |
| 89 | +- [ ] Full install flow: |
144 | 90 | ```bash |
145 | | - # Step 1: Install |
146 | | - uvx python-docs-mcp-server --version |
147 | | - # Should print 0.1.1 |
148 | | - |
149 | | - # Step 2: Build index |
150 | 91 | uvx python-docs-mcp-server build-index --versions 3.10,3.11,3.12,3.13,3.14 |
151 | | - # Should complete successfully |
152 | | - |
153 | | - # Step 3: Doctor check |
154 | 92 | uvx python-docs-mcp-server doctor |
155 | | - # All checks should PASS |
156 | 93 | ``` |
157 | | -- [ ] Slow E2E workflow passes: |
158 | | - - Run GitHub Actions workflow `Slow E2E` |
159 | | - - Confirm Python 3.13 and Python 3.14 jobs both pass |
160 | | - - Confirm each job installs the built wheel, runs |
161 | | - `build-index --versions 3.10,3.11,3.12,3.13,3.14`, `doctor`, and |
162 | | - `validate-corpus` |
163 | | - |
164 | | -### Post-PyPI Launch Pack Cleanup |
165 | | - |
166 | | -- [ ] Remove every temporary PyPI pre-release block from `README.md`: |
167 | | - - Mechanical pass: delete every region from `<!-- PRE-PYPI:` to `<!-- /PRE-PYPI -->` (inclusive). Each block now encloses its surrounding heading + lead-in sentence + code, so a single pass produces a clean README. |
168 | | - - Reference command: |
169 | | - `perl -0777 -i -pe 's/<!-- PRE-PYPI:.*?<!-- \/PRE-PYPI -->\n*//gs' README.md` |
170 | | - - Make the published package commands (`uvx python-docs-mcp-server ...`) the |
171 | | - primary install, build-index, MCP client, `doctor`, and `validate-corpus` |
172 | | - examples |
173 | | -- [ ] Verify `README.md` has no temporary pre-release install artifacts: |
| 94 | + Both must complete successfully. |
| 95 | +- [ ] MCP Registry shows `X.Y.Z` as the `isLatest` entry: |
174 | 96 | ```bash |
175 | | - rg -n 'PRE[-]PYPI|Before PyPI publishing|Until the first PyPI|After PyPI publishing|git\\+https://github.com/.*/python-docs-mcp-server' README.md |
| 97 | + curl -s 'https://registry.modelcontextprotocol.io/v0.1/servers?search=python-docs-mcp-server' | \ |
| 98 | + python3 -c "import json,sys; d=json.load(sys.stdin); print(next((e['server']['version'] for e in d['servers'] if e['_meta']['io.modelcontextprotocol.registry/official']['isLatest']), 'no latest'))" |
176 | 99 | ``` |
177 | | - The command should return no output. |
178 | | -- [ ] Review `docs/launch/` so no public launch copy still asks users to install |
179 | | - from GitHub source after the PyPI package is available. Intentional historical |
180 | | - pre-release drafts may remain, but they must stay clearly labeled as |
181 | | - pre-PyPI-only. |
182 | | -- [ ] Submit `server.json` to https://registry.modelcontextprotocol.io/ via the |
183 | | - `mcp-publisher` CLI after the PyPI smoke test passes; verify the registry |
184 | | - listing appears and points at version 0.1.1 |
185 | | -- [ ] Use the post-PyPI draft in `docs/launch/show-hn.md` for the HN submission |
186 | | -- [ ] Use `docs/launch/reddit-posts.md` for the r/Python and r/LocalLLaMA |
187 | | - submissions after the PyPI release smoke test passes |
188 | | -- [ ] Commit and push the README cleanup before public launch posts go out |
189 | | - |
190 | | -- [ ] Claude Desktop test with published package: |
191 | | - Configure `mcpServers` with `uvx python-docs-mcp-server` and verify |
192 | | - "what is asyncio.TaskGroup" returns a correct hit |
| 100 | +- [ ] GitHub Release exists at `https://github.com/ayhammouda/python-docs-mcp-server/releases/tag/vX.Y.Z` with wheel + sdist attached and auto-generated notes |
| 101 | +- [ ] **Slow E2E workflow** passes (manually triggered after publish): |
| 102 | + - Run GitHub Actions workflow `Slow E2E` (`.github/workflows/e2e.yml`) |
| 103 | + - Python 3.13 and 3.14 jobs both pass |
| 104 | + - Each job installs the built wheel and runs `build-index --versions 3.10,3.11,3.12,3.13,3.14`, `doctor`, and `validate-corpus` |
| 105 | +- [ ] Claude Desktop manual smoke test: |
| 106 | + Configure `mcpServers` with `uvx python-docs-mcp-server` and verify a known query (e.g. "what is asyncio.TaskGroup") returns a correct hit. |
193 | 107 |
|
194 | 108 | ### Release Complete |
195 | 109 |
|
196 | | -- [ ] GitHub Release exists with attached artifacts |
197 | | -- [ ] PyPI page shows 0.1.1 with attestation |
198 | | -- [ ] README install instructions verified end-to-end |
199 | | -- [ ] README no longer contains temporary pre-PyPI GitHub-source install blocks |
200 | | -- [ ] Slow E2E workflow passed for the release candidate |
201 | | -- [ ] Tag v0.1.1 exists in git |
202 | | - |
203 | | -**Release date**: _______________ |
204 | | -**Released by**: _______________ |
| 110 | +- [ ] All four `release.yml` jobs green for `vX.Y.Z` |
| 111 | +- [ ] PyPI page shows `X.Y.Z` with the "Provenance" attestation badge |
| 112 | +- [ ] MCP Registry `isLatest` entry points at `X.Y.Z` |
| 113 | +- [ ] GitHub Release `vX.Y.Z` exists with both dist artifacts attached |
| 114 | +- [ ] `CHANGELOG.md` `[X.Y.Z]` entry is committed on `main` |
0 commit comments