Skip to content

Commit d64800a

Browse files
HumanBean17claude
andcommitted
add publish-pip skill for manual PyPI releases
Encoded runbook for the manual (no-CI, no-tag) PyPI release flow: version bump in pyproject.toml, .venv build + twine upload, and the local gotchas hit during the 0.6.6 publish (build/twine absent from a fresh venv, the 'import build' false positive, SSL_CERT_FILE=certifi for PyPI API verification, clean dist before upload, verify the built wheel version before the permanent upload). Co-Authored-By: Claude <noreply@anthropic.com>
1 parent c92d9fb commit d64800a

1 file changed

Lines changed: 107 additions & 0 deletions

File tree

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
---
2+
name: publish-pip
3+
description: Use when the user asks to publish or release the java-codebase-rag pip package to PyPI, bump + publish a new version, or cut a PyPI release. Also use when a manual publish is failing on missing build/twine tooling or an SSL verification error.
4+
disable-model-invocation: true
5+
---
6+
7+
# Publish Pip Package
8+
9+
Manual, from-worktree release of `java-codebase-rag` to PyPI. There is **no CI
10+
release and no git tag** — the version lives only in `pyproject.toml`, and
11+
releases are built + uploaded with the venv `build` / `twine` tools. PyPI uploads
12+
are **permanent**: a version can be yanked but never overwritten, so verify the
13+
version *before* uploading.
14+
15+
## When to use
16+
17+
- user says "publish/release the package", "bump version and publish",
18+
"cut a PyPI release"
19+
- a prior publish failed partway (tool missing, SSL error, wrong version)
20+
21+
Do **not** use this for installing dev deps (`pip install -e ".[dev]"`) or for
22+
adding a runtime dependency to `pyproject.toml`.
23+
24+
## Prerequisites
25+
26+
- `.venv` at repo root. Use **only** `.venv/bin/python`, `.venv/bin/pip`,
27+
`.venv/bin/twine` — the system Python shadows the venv CLI.
28+
- `~/.pypirc` present with the PyPI upload token. Never print it; twine reads it
29+
automatically.
30+
31+
## Workflow
32+
33+
1. **Bump version** — the single source is `pyproject.toml` (`version = "X.Y.Z"`,
34+
near line 7). Read the current value, increment per request (patch = `Z+1`).
35+
2. **Ensure tooling**`build` and `twine` are not runtime deps and may be
36+
absent from a fresh worktree venv:
37+
```bash
38+
.venv/bin/python -m build --version # real PyPA build, else "No module named build"
39+
.venv/bin/twine --version
40+
.venv/bin/pip install build twine # if either is missing
41+
```
42+
⚠️ Don't use `import build` to confirm the tool — it can succeed by resolving
43+
to a local `build/` namespace dir or a stale install even when the PyPA tool
44+
is absent. Always check via `-m build --version`.
45+
3. **Clean old artifacts** — re-uploading an existing PyPI version is rejected,
46+
and you must never mix stale files into `dist/`:
47+
```bash
48+
rm -rf dist build *.egg-info
49+
```
50+
4. **Build** sdist + wheel:
51+
```bash
52+
.venv/bin/python -m build
53+
```
54+
Expect `dist/java_codebase_rag-<ver>-py3-none-any.whl` and `.tar.gz`.
55+
5. **Verify the built version** before upload (catches a forgotten bump):
56+
```bash
57+
.venv/bin/python -c "import zipfile,glob; w=glob.glob('dist/*.whl')[0]; z=zipfile.ZipFile(w); m=[n for n in z.namelist() if n.endswith('METADATA')][0]; print([l for l in z.read(m).decode().splitlines() if l.startswith('Version')][0])"
58+
```
59+
6. **Upload** (permanent — confirm the version is right first):
60+
```bash
61+
.venv/bin/twine upload dist/*
62+
```
63+
twine prints the live URL on success:
64+
`https://pypi.org/project/java-codebase-rag/<ver>/`.
65+
7. **Verify on PyPI** via the JSON API. ⚠️ Python's `urllib`/`requests` SSL
66+
verification fails locally (missing CA bundle) — set `SSL_CERT_FILE`:
67+
```bash
68+
CERT=$(.venv/bin/python -c "import certifi; print(certifi.where())")
69+
SSL_CERT_FILE="$CERT" .venv/bin/python -c "import urllib.request,json; d=json.load(urllib.request.urlopen('https://pypi.org/pypi/java-codebase-rag/json')); print('latest:', d['info']['version'])"
70+
```
71+
8. **Commit + push the version bump** so the repo matches what was published
72+
(commit convention: `bump version to X.Y.Z`). `dist/`, `build/`, and
73+
`*.egg-info` are gitignored — do not commit them.
74+
75+
## Quick reference
76+
77+
| Step | Command |
78+
|------|---------|
79+
| Bump | edit `pyproject.toml` `version` |
80+
| Tooling | `.venv/bin/pip install build twine` |
81+
| Clean | `rm -rf dist build *.egg-info` |
82+
| Build | `.venv/bin/python -m build` |
83+
| Verify wheel | read `Version:` from `dist/*.whl` METADATA |
84+
| Upload | `.venv/bin/twine upload dist/*` |
85+
| Verify live | `SSL_CERT_FILE="$(.venv/bin/python -m certifi)"` + pypi JSON API |
86+
| Commit | `bump version to X.Y.Z` |
87+
88+
## Common mistakes
89+
90+
- **Re-uploading an existing version** → PyPI returns 400. Bump first; clean `dist/`.
91+
- **`import build` succeeds but `python -m build` fails**`import` resolved to
92+
a local `build/` namespace dir or stale module, not the PyPA tool. `pip install
93+
build`, then confirm with `-m build --version`.
94+
- **PyPI verification SSL error** (`CERTIFICATE_VERIFY_FAILED`) →
95+
`SSL_CERT_FILE=$(.venv/bin/python -c "import certifi;print(certifi.where())")`.
96+
- **Forgot to bump / uploaded the wrong version** → permanent. Always run the
97+
METADATA version check (step 5) before `twine upload`.
98+
- **Used system `python` / `twine`** → wrong env / missing credentials. Always
99+
`.venv/bin/`.
100+
- **Left the version bump uncommitted** → repo drifts from PyPI. Commit + push.
101+
102+
## Notes
103+
104+
- Release `0.6.6` (erase fix, PR #348) established this runbook; the gotchas
105+
above were all hit for real during that publish.
106+
- If you publish from an unmerged feature branch, PyPI will be ahead of `master`
107+
until the branch merges — call that out.

0 commit comments

Comments
 (0)