|
| 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