Problem
Right now the release tag (e.g. 1.2.3) and j1939/version.py's __version__ string are two independently-maintained sources of truth. Nothing enforces they match, so it's easy to push a release tag and forget to bump version.py first (happened while working on #70/#78) — the published sdist/wheel then reports the wrong __version__.
Proposal
Use setuptools-scm to derive the version directly from the git tag at build time, eliminating the possibility of drift.
Changes needed
-
pyproject.toml
- Add
setuptools-scm to [build-system].requires.
- Replace
dynamic = ["version"] + [tool.setuptools.dynamic] version = { attr = ... } with [tool.setuptools_scm] config (writes a generated _version.py, typically via write_to = "j1939/_version.py").
- Decide on a
local_scheme/version_scheme (e.g. suppress the +g<hash> local part for clean tagged releases, since the PyPI-published artifact is always built from an exact tag).
-
j1939/version.py
- Remove the hardcoded
__version__ = "..." string; either delete the file or have it re-export from the generated _version.py.
-
j1939/__init__.py
- Update
from .version import __version__ if the import path changes (e.g. point at _version.py or keep version.py as a thin re-export shim so the public API — j1939.__version__ — is unaffected).
-
.gitignore
- Ignore the generated
j1939/_version.py (it must not be committed, since it's derived at build/install time).
-
CI (.github/workflows/CI.yml, publish.yml)
- Ensure
actions/checkout uses fetch-depth: 0 (or fetch-tags: true) in any job that builds the package — setuptools-scm needs full tag history to compute the version, and the default shallow checkout won't have it.
- No other workflow changes expected;
python -m build / pip install -e . already pick up the new backend automatically.
-
Editable/dev installs
- Verify
pip install -e . still resolves a sane version for local/dev checkouts (untagged commits get something like 0.1.3.dev4+g<hash> — confirm that's acceptable, e.g. doesn't break the Development Status classifier expectations or any code that parses __version__).
-
Docs
- Update
CONTRIBUTING.md's "Maintainer Deployment Sequence" — step 1 ("Update the version string inside j1939/version.py") goes away entirely; pushing the tag becomes the only step besides the tag itself.
Why this approach (vs. an automated semver-bump bot)
Discussed alongside #70/#78: this repo's release cadence and dependency surface are small enough that a full conventional-commits/release-please style bot would be more machinery than the actual problem warrants. setuptools-scm fixes the specific failure mode (forgetting to sync a file) with no new workflow, no commit-message conventions, and no bot — the tag simply becomes the single source of truth.
Test plan
Problem
Right now the release tag (e.g.
1.2.3) andj1939/version.py's__version__string are two independently-maintained sources of truth. Nothing enforces they match, so it's easy to push a release tag and forget to bumpversion.pyfirst (happened while working on #70/#78) — the published sdist/wheel then reports the wrong__version__.Proposal
Use
setuptools-scmto derive the version directly from the git tag at build time, eliminating the possibility of drift.Changes needed
pyproject.tomlsetuptools-scmto[build-system].requires.dynamic = ["version"]+[tool.setuptools.dynamic] version = { attr = ... }with[tool.setuptools_scm]config (writes a generated_version.py, typically viawrite_to = "j1939/_version.py").local_scheme/version_scheme(e.g. suppress the+g<hash>local part for clean tagged releases, since the PyPI-published artifact is always built from an exact tag).j1939/version.py__version__ = "..."string; either delete the file or have it re-export from the generated_version.py.j1939/__init__.pyfrom .version import __version__if the import path changes (e.g. point at_version.pyor keepversion.pyas a thin re-export shim so the public API —j1939.__version__— is unaffected)..gitignorej1939/_version.py(it must not be committed, since it's derived at build/install time).CI (
.github/workflows/CI.yml,publish.yml)actions/checkoutusesfetch-depth: 0(orfetch-tags: true) in any job that builds the package — setuptools-scm needs full tag history to compute the version, and the default shallow checkout won't have it.python -m build/pip install -e .already pick up the new backend automatically.Editable/dev installs
pip install -e .still resolves a sane version for local/dev checkouts (untagged commits get something like0.1.3.dev4+g<hash>— confirm that's acceptable, e.g. doesn't break theDevelopment Statusclassifier expectations or any code that parses__version__).Docs
CONTRIBUTING.md's "Maintainer Deployment Sequence" — step 1 ("Update the version string inside j1939/version.py") goes away entirely; pushing the tag becomes the only step besides the tag itself.Why this approach (vs. an automated semver-bump bot)
Discussed alongside #70/#78: this repo's release cadence and dependency surface are small enough that a full conventional-commits/release-please style bot would be more machinery than the actual problem warrants.
setuptools-scmfixes the specific failure mode (forgetting to sync a file) with no new workflow, no commit-message conventions, and no bot — the tag simply becomes the single source of truth.Test plan
pip install -e .in a fresh checkout reports a sane dev version0.1.3) and building (python -m build) produces a wheel/sdist with exactly__version__ == "0.1.3"python -m twine check dist/*still passesbuild_checkjob still passes with the checkout depth change