Skip to content

Commit 1d880ce

Browse files
fix(release): derive version from git tag via hatch-vcs (#1503)
The version bump PR lands after the release tag, so version.py at each tag reports the previous release: pip install git+...@vX.Y.Z and pip show on a git install report the prior version, silently breaking version-gated code. PyPI wheels were unaffected (built from the sed'd tree). Make the git tag the single source of truth via hatch-vcs: - pyproject.toml: hatch-vcs version source + vcs build hook writing the generated src/datajoint/_version.py (git-ignored). - version.py: re-export the generated version, falling back to installed package metadata for source-tree runs. - post_draft_release_published.yaml: drop the version.py sed/commit and the follow-up bump PR; fetch full history+tags so the build resolves the tag. - RELEASE_MEMO.md: document the tag-driven flow (no manual bump, no PR).
1 parent 5c86fca commit 1d880ce

5 files changed

Lines changed: 45 additions & 42 deletions

File tree

‎.github/workflows/post_draft_release_published.yaml‎

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@ run-name: Post ${{ github.event.release.name }}
1212
jobs:
1313
pypi-release:
1414
permissions:
15-
# write permission is required to update version.py
15+
# write permission is required to upload build artifacts as release assets
1616
contents: write
17-
pull-requests: write
1817
# Use the oldest supported version to build, just in case there are issues
1918
# for our case, this doesn't matter that much, since the build is for 3.x
2019
strategy:
@@ -33,20 +32,10 @@ jobs:
3332
uses: actions/checkout@v4
3433
with:
3534
token: ${{ secrets.GITHUB_TOKEN }}
36-
# new release needs the updated version.py
37-
- name: Update version.py
38-
run: |
39-
VERSION=$(echo "${{ github.event.release.name }}" | grep -oP '\d+\.\d+\.\d+')
40-
sed -i "s/^__version__ = .*/__version__ = \"$VERSION\"/" src/datajoint/version.py
41-
cat src/datajoint/version.py
42-
# Commit the changes
43-
BRANCH_NAME="update-version-$VERSION"
44-
git switch -c $BRANCH_NAME
45-
git config --global user.name "github-actions"
46-
git config --global user.email "github-actions@github.com"
47-
git add src/datajoint/version.py
48-
git commit -m "Update version.py to $VERSION"
49-
echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV
35+
# Full history + tags so hatch-vcs can derive the version from the
36+
# release tag. The checked-out ref is the tag itself, so the build
37+
# reports exactly X.Y.Z — no version.py bump or follow-up PR needed.
38+
fetch-depth: 0
5039
- name: Set up Python ${{matrix.py_ver}}
5140
uses: actions/setup-python@v5
5241
with:
@@ -108,17 +97,6 @@ jobs:
10897
asset_path: ${{env.DJ_SDIST_PATH}}
10998
asset_name: pip-datajoint-${{ github.event.release.tag_name }}.tar.gz
11099
asset_content_type: application/gzip
111-
- name: Create Pull Request
112-
env:
113-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
114-
run: |
115-
git push origin ${{ env.BRANCH_NAME }}
116-
gh pr create \
117-
--title "[github-actions]Update version.py to ${{ github.event.release.name }}" \
118-
--body "This PR updates \`version.py\` to match the latest release: ${{ github.event.release.name }}" \
119-
--base master \
120-
--head ${{ env.BRANCH_NAME }} \
121-
--reviewer dimitri-yatsenko,drewyangdev,ttngu207
122100
- name: Post release notification to Slack
123101
if: ${{ env.TEST_PYPI == 'false' }}
124102
uses: slackapi/slack-github-action@v2.0.0

‎.gitignore‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ share/python-wheels/
2828
*.egg
2929
MANIFEST
3030

31+
# Version file generated at build time by hatch-vcs (see pyproject.toml)
32+
src/datajoint/_version.py
33+
3134
# PyInstaller
3235
# Usually these files are written by a python script from a template
3336
# before PyInstaller builds the exe, so as to inject date/other infos into it.

‎RELEASE_MEMO.md‎

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -82,20 +82,23 @@ Ensure PRs have appropriate labels before merging.
8282
- Set release name to `Release X.Y.Z`
8383
- Set tag to `vX.Y.Z`
8484
- Review and edit release notes
85-
4. **Publish the release**
85+
4. **Publish the release** — this creates the `vX.Y.Z` tag.
8686
5. Automation will:
87-
- Update `version.py` to `X.Y.Z`
88-
- Build and publish to PyPI
89-
- Create PR to merge version update back to master
87+
- Build from the tag and publish to PyPI
88+
- Attach the wheel/sdist as release assets
9089

9190
### Version Note
9291

93-
The release drafter computes version from the previous tag. You may need to **manually edit** the release name for major version changes.
92+
The release drafter computes the version from the previous tag. You may need to
93+
**manually edit** the release name/tag for major version changes.
9494

95-
The regex in `post_draft_release_published.yaml` extracts version from the release name:
96-
```bash
97-
VERSION=$(echo "${{ github.event.release.name }}" | grep -oP '\d+\.\d+\.\d+')
98-
```
95+
The package version is **derived from the git tag** at build time by
96+
[hatch-vcs](https://github.com/ofek/hatch-vcs) (see `[tool.hatch.version]` in
97+
`pyproject.toml`) — the tag is the single source of truth. There is **no
98+
`version.py` value to bump** and **no follow-up version PR**: publishing the
99+
`vX.Y.Z` tag is what sets the version everywhere (`pip install`, `pip show`, and
100+
`datajoint.__version__`). Do not hand-edit `src/datajoint/version.py`; it only
101+
re-exports the build-generated `_version.py`.
99102

100103
---
101104

‎pyproject.toml‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[build-system]
2-
requires = ["hatchling"]
2+
requires = ["hatchling", "hatch-vcs"]
33
build-backend = "hatchling.build"
44

55
[project]
@@ -219,7 +219,14 @@ module = [
219219
ignore_errors = true
220220

221221
[tool.hatch.version]
222-
path = "src/datajoint/version.py"
222+
# Version is derived from the git tag at build time (hatch-vcs), so the tag is
223+
# the single source of truth: `pip install git+...@vX.Y.Z` reports X.Y.Z. The
224+
# resolved version is written to the generated (git-ignored) src/datajoint/_version.py
225+
# by the build hook below; src/datajoint/version.py reads it back at runtime.
226+
source = "vcs"
227+
228+
[tool.hatch.build.hooks.vcs]
229+
version-file = "src/datajoint/_version.py"
223230

224231
[tool.hatch.build.targets.wheel]
225232
packages = ["src/datajoint"]

‎src/datajoint/version.py‎

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
1-
# version bump auto managed by Github Actions:
2-
# draft_release.yaml (draft), post_draft_release_published.yaml (bump + PR)
3-
# manually set this version will be eventually overwritten by the above actions
4-
__version__ = "2.3.1"
1+
# Version is derived from the git tag at build time by hatch-vcs (see
2+
# pyproject.toml). The build writes the resolved version to the generated,
3+
# git-ignored ``_version.py``; there is no version string to bump by hand.
4+
# This module re-exports it, falling back to installed package metadata so
5+
# ``datajoint.__version__`` also works when running from a source tree.
6+
try:
7+
from ._version import __version__
8+
except ImportError: # not built yet (e.g. a bare git checkout on sys.path)
9+
from importlib.metadata import PackageNotFoundError, version
10+
11+
try:
12+
__version__ = version("datajoint")
13+
except PackageNotFoundError:
14+
__version__ = "0.0.0+unknown"
15+
16+
__all__ = ["__version__"]

0 commit comments

Comments
 (0)