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
ci: switch to tag-driven releases via hatch-vcs (#97)
## Problem
The release workflow has **never succeeded**. It bumps the version in
`src/swgoh_comlink/version.py`, commits it, and runs `git push origin
main --follow-tags`. Branch protection on `main` rejects that push:
```
remote: error: GH006: Protected branch update failed for refs/heads/main.
- Changes must be made through a pull request.
- 10 of 10 required status checks are expected.
! [remote rejected] main -> main (protected branch hook declined)
```
With `enforce_admins: true` and PRs required, **nothing** can push a
commit directly to `main`. Worse, `--follow-tags` succeeds in pushing
the *tag* even when the branch push is rejected, so each failed run
leaves an **orphaned tag** pointing at a bump commit that never landed
(this happened today with `v2.1.0`; I deleted it).
## Fix: tag-driven versioning
Derive the version from the Git tag instead of committing it, so the
release never touches `main`.
- **`pyproject.toml`** — use `hatch-vcs` as the version source; write
the resolved value to a git-ignored `_version.py` via the vcs build
hook. Removed the dead `[tool.semantic_release]` block (it referenced
the old `version.py` mechanism).
- **`version.py`** — read `__version__` from the generated
`_version.py`, falling back to `importlib.metadata`, then a sentinel for
unbuilt trees.
- **`.gitignore`** — ignore the generated `_version.py`.
- **`release.yml`** — rewritten as **build → publish → tag**:
1. Compute the next version from the latest tag + a `bump` input
(`patch`/`minor`/`major`).
2. Build with `SETUPTOOLS_SCM_PRETEND_VERSION` so no tag is needed yet.
3. Publish to PyPI via trusted publishing.
4. **Only after a successful publish**, create the tag + GitHub Release
(`gh release create --target <sha> --generate-notes`). A failed run
therefore never leaves an orphaned tag, and no step pushes to `main`.
- **`CONTRIBUTING.md`** — documents the new process.
## Behavior change
The repo no longer carries a committed version string, and
`CHANGELOG.md` is no longer regenerated into the repo on release —
release notes are auto-generated into the **GitHub Release body**
instead. The existing `CHANGELOG.md` remains as historical record.
## Verification (local)
- `hatch version` resolves `2.0.8.dev7+g…` from existing tags ✅
- `uv sync` runs the vcs build hook → generates `_version.py`; runtime
chain (`version.py` → `__init__` → client repr) reports the derived
version ✅
- `SETUPTOOLS_SCM_PRETEND_VERSION=2.1.0 hatch build` produces correctly
named `swgoh_comlink-2.1.0.tar.gz` / `…-py3-none-any.whl` ✅
- Full unit suite: **598 passed** ✅
- `ruff check src/` clean; workflow YAML parses; job graph is build →
pypi-publish → tag-release ✅
## After merge
Trigger the release from the Actions tab → "comlink-python release" →
choose `bump`. It will publish to PyPI and tag `vX.Y.Z` on the `main`
commit.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|`version.py`|Single`__version__` string, managed by hatch during releases|
194
+
|`version.py`|Exposes`__version__`, derived from the Git tag at build time via hatch-vcs|
195
195
196
196
---
197
197
@@ -506,13 +506,13 @@ Before opening an issue, check the [existing issues](https://github.com/swgoh-ut
506
506
507
507
Releases are handled by the maintainer through the GitHub Actions `release.yml` workflow. Contributors don't need to manage versioning or releases, but here's how it works for reference:
508
508
509
-
1. The release workflow is triggered manually (`workflow_dispatch`)
510
-
2.[Hatch](https://hatch.pypa.io/) bumps the version in `src/swgoh_comlink/version.py`
511
-
3.[git-changelog](https://pawamoy.github.io/git-changelog/) regenerates `CHANGELOG.md`from commit history
512
-
4. The new version is tagged and pushed
513
-
5.The package is built with `hatch build` and published to [PyPI](https://pypi.org/project/swgoh-comlink/)
509
+
1. The release workflow is triggered manually (`workflow_dispatch`) with a `bump` input (`patch`, `minor`, or `major`)
510
+
2.The next version is computed from the latest release tag and the chosen bump level
511
+
3.The package is built — [hatch-vcs](https://github.com/ofek/hatch-vcs) derives the version from that target tag, so **no version string is committed to the repo**
512
+
4. The distributions are published to [PyPI](https://pypi.org/project/swgoh-comlink/) via trusted publishing
513
+
5.Only after a successful publish is the Git tag created and a GitHub Release cut, with release notes auto-generated from the commit/PR history
514
514
515
-
This is why conventional commit messages matter — they become the release notes automatically.
515
+
Because the version comes from the Git tag (not a committed file), the release workflow never pushes to the protected `main` branch. This is why conventional commit messages matter — they become the auto-generated release notes.
0 commit comments