Skip to content

Commit b9e0eac

Browse files
ty19880929claude
andcommitted
docs(claude): record release procedure and version-sync gotcha
Captures lessons from the v0.3.1 release: pyproject.toml and deeptrade/__init__.py both hold the version and must be bumped together, plus a tag-move recovery procedure for failed-but- unpublished release attempts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 9bcc60e commit b9e0eac

1 file changed

Lines changed: 30 additions & 0 deletions

File tree

CLAUDE.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,36 @@ uv run mypy deeptrade # CI runs mypy on `deeptrade/` only, not `te
2222

2323
CI (`.github/workflows/ci.yml`) runs ruff → mypy → pytest → `python -m build` on Ubuntu/Python 3.11. A pre-existing decision in `mypy.ini` disables `no-untyped-def` for tests; do not enable it project-wide. Tagging `v*` triggers `release.yml` (PyPI Trusted Publisher via OIDC + GitHub Release).
2424

25+
## Releasing
26+
27+
The package version lives in **two** places and they MUST be bumped together:
28+
29+
- `pyproject.toml::project.version` — what hatchling stamps onto the wheel filename and PyPI metadata. **This is the source of truth for the build.**
30+
- `deeptrade/__init__.py::__version__` — runtime introspection only.
31+
32+
Bumping only `__init__.py` will silently produce a wheel with the previous version in its filename and tank the release at PyPI upload (HTTP 400 "File already exists" — PyPI filenames are immutable, you cannot overwrite). Always grep both files before tagging:
33+
34+
```bash
35+
grep -n version pyproject.toml deeptrade/__init__.py
36+
```
37+
38+
Release sequence:
39+
1. Bump both files + update `CHANGELOG.md` in the same commit (or two adjacent commits).
40+
2. Push to main and verify CI green.
41+
3. `git tag -a vX.Y.Z -m "..."` then `git push origin vX.Y.Z` — the tag push is what triggers `release.yml`.
42+
43+
If the upload fails after a tag push (version mismatch, accidental rebuild), recover by moving the tag rather than burning a version number:
44+
45+
```bash
46+
# fix the underlying issue, commit it
47+
git tag -d vX.Y.Z # delete local
48+
git push origin :refs/tags/vX.Y.Z # delete remote
49+
git tag -a vX.Y.Z -m "..." # re-create on the fix commit
50+
git push origin main && git push origin vX.Y.Z
51+
```
52+
53+
This is safe **only** while the failed release hasn't shipped anything to PyPI (i.e. upload was rejected, not partially succeeded). If any artifact made it through, skip to the next patch version instead.
54+
2555
## Architecture
2656

2757
### Top-level CLI is a custom click.Group, not a static command tree

0 commit comments

Comments
 (0)