You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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>
Copy file name to clipboardExpand all lines: CLAUDE.md
+30Lines changed: 30 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -22,6 +22,36 @@ uv run mypy deeptrade # CI runs mypy on `deeptrade/` only, not `te
22
22
23
23
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).
24
24
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.**
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
+
25
55
## Architecture
26
56
27
57
### Top-level CLI is a custom click.Group, not a static command tree
0 commit comments