Version Management for spotforecast2-safe
Overview
As a safety-critical system, spotforecast2-safe implements strict version control to ensure auditability and reproducibility across releases. This document outlines the version management strategy for MLOps engineers.
Design Principle: Single Source of Truth
Version is defined once, in pyproject.toml
[project]
name = "spotforecast2-safe"
version = "0.0.5" # ← Single source of truthAll other files (e.g., MODEL_CARD.md, documentation) reference this version programmatically or are automatically synchronized.
Architecture
1. Version Definition Layer (pyproject.toml)
- Contains the canonical version
- Used by
pip,uv, and build systems - Updated during release workflows
2. Version Access Layer (__init__.py)
Exposes version via
__version__attributeUses
importlib.metadata.version()(runtime dynamic)Fallback for development environments
Accessible programmatically:
import spotforecast2_safe print(spotforecast2_safe.__version__) # → "0.0.5"
3. Version Synchronization Layer (scripts/update_version.py)
- Sets the release version in
pyproject.tomland propagates it toMODEL_CARD.md - Guarantees consistency across files
- Invoked by
make release, by the pre-commit hook, or by hand
4. Version Verification Layer (Pre-commit hook, --verify)
- Validates version consistency
- Prevents accidental mismatches
- Supports audit compliance
The rendered documentation is deliberately outside this chain. docs/index.qmd writes {{< var version >}}, which resolves from _variables.yml; that file is regenerated from pyproject.toml by docs/pre-render.py on every quarto render. The documentation version cannot drift, so nothing needs to synchronize it.
Workflow
For release engineers
1. During development
The version stays at the current release version in pyproject.toml. No manual updates needed.
2. Cutting a release
The version number is chosen by the maintainer — nothing derives it from commit messages. The release target in the Makefile performs the bump as its second step, after the verify gate:
make release VERSION=25.3.0This runs scripts/update_version.py --set 25.3.0, which writes the version into pyproject.toml and synchronizes MODEL_CARD.md (the version table, the current-release CPE, the citation, and the lifecycle sentence). It then generates the CHANGELOG.md section and stops, so the maintainer can review both before committing.
To perform only the bump, outside a release:
uv run python scripts/update_version.py --set 25.3.0 # set and synchronize
uv run python scripts/update_version.py --set 25.3.0 --dry-run # preview the changes
uv run python scripts/update_version.py --verify # check consistencySee RELEASING.md for the complete release runbook.
3. Using pre-commit hooks (recommended)
The pre-commit hook re-runs the synchronization whenever pyproject.toml changes, so a hand-edited version can never reach a commit without MODEL_CARD.md following it:
# Setup (one-time)
uv tool install pre-commit
pre-commit installFor Developers
I want to check the current version:
Option A: From Python
import spotforecast2_safe
print(spotforecast2_safe.__version__)Option B: From CLI
python scripts/update_version.py --verifyOption C: From pyproject.toml
grep 'version = ' pyproject.tomlI want to verify everything is in sync:
python scripts/update_version.py --verifyExit code: 0 = version consistent, 1 = inconsistency detected
Script Reference
scripts/update_version.py
Purpose: Set the release version and synchronize it across project artifacts.
Usage:
# Set the release version (what `make release` calls)
uv run python scripts/update_version.py --set 25.3.0
# Normal: re-synchronize from the version already in pyproject.toml
uv run python scripts/update_version.py
# Dry-run: Show changes without modifying files
uv run python scripts/update_version.py --dry-run
# Verify: Only check consistency (no modifications)
uv run python scripts/update_version.py --verifyOutput:
======================================================================
spotforecast2-safe: Version Synchronization Script
======================================================================
📋 Version Consistency Check:
pyproject.toml: 25.2.0
MODEL_CARD.md: 25.2.0
✓ Versions are in sync!
Files Synchronized
pyproject.toml- Primary sourceMODEL_CARD.md- Model/Method card (for EU AI Act compliance)src/spotforecast2_safe/__init__.py- Package metadata (read dynamically viaimportlib.metadata)_variables.yml- Quarto documentation variables (regenerated bydocs/pre-render.pyat render time, not by this script)
Safety & Compliance Features
✅ Audit Trail
# Git history preserves all version changes
git log --oneline -- pyproject.toml✅ Consistency Guarantees
- Version synchronization script prevents mismatches
- Pre-commit hooks enforce synchronization
make releaserefuses to proceed on a dirty tree, an existing tag, or a malformed version
✅ EU AI Act Compliance
- Version is documented in MODEL_CARD.md (Article 13 - Transparency)
- Version history is traceable in git
- Releases are tagged with version information
✅ Reproducibility
- Each version pins exact dependency versions
uv lockcreates lockfile for determinism- Users can reproduce results with
spotforecast2_safe==0.0.5
Release Integration
There is no continuous-integration service. Versioning is one step of the local release pipeline defined in the Makefile:
make release VERSION=25.3.0 # verify gate, bump, synchronize, generate CHANGELOG (stops here)
# review the diff, then:
make release-commit VERSION=25.3.0 # commit, tag v25.3.0, push
make build sbom # wheel + sdist in dist/, SBOM in artifacts/
make publish # twine check + twine upload
make doc-publish # render and rsync the sitemake release runs make verify before touching the version, so a release cannot be staged from a tree that fails the tests, the lint checks, the SPDX audit, or the API-reference sync.
The full runbook, including what the retired GitHub Actions pipeline used to provide and no longer does, is in RELEASING.md.
Troubleshooting
Issue: Version mismatch detected
⚠ Version mismatch detected!
pyproject.toml: 0.0.6
MODEL_CARD.md: 0.0.5
Solution: Run synchronization script
python scripts/update_version.pyIssue: Pre-commit hook prevents commit
This is intentional! The hook detected a version mismatch.
Solution:
# Let the hook automatically fix it
pre-commit run --all-files
# Then commit normally
git add .
git commit -m "chore: synchronize versions"Issue: importlib.metadata not found
This should not happen in Python 3.8+. If it does:
- Check Python version:
python --version - Ensure package is installed:
uv pip install -e . - Fallback version (0.0.5) will be used automatically
Best Practices
✅ Do
- Change the version only through
update_version.py --set, which keepspyproject.tomlthe single source of truth - Use
make releaseso the bump, the changelog and theverifygate stay in one sequence - Install the pre-commit hooks
- Read the generated
CHANGELOG.mdsection before runningmake release-commit
❌ Don’t
- Manually edit the version in MODEL_CARD.md
- Hand-edit
_variables.yml— it is regenerated on every render - Skip
make verifybefore a release - Commit version mismatches to the release branch
Versioning Scheme
spotforecast2-safe uses Semantic Versioning (MAJOR.MINOR.PATCH):
- Major (0.X.X): Breaking API changes (rare for a data transformation library)
- Minor (X.Y.X): New features, improvements (backward compatible)
- Patch (X.Y.Z): Bug fixes, documentation updates
Example: - 0.0.1 → 0.0.2: Patch - bug fix - 0.0.5 → 0.1.0: Minor - new feature - 0.X.X → 1.0.0: Major - breaking change