PyPNM Release Guide (Single-Branch Model Using tools/release/release.py)¶
Table of contents¶
2. Version Source Of Truth And Mirrored Locations
4. Release Script Overview (tools/release/release.py)
- 5.1 Automatic Maintenance Release (Default)
- 5.2 Automatic Next Version By Mode (
--next) - 5.3 Explicit Version (
--version)
6. Preflight controls and prerequisites
This guide describes how to manage versions and releases for the PyPNM project using a single primary branch and the tools/release/release.py helper script. The release script is the primary entry point for bumping versions, running tests, creating tags, and pushing changes.
1. Branch Model¶
For current PyPNM development, a single-branch model is used:
main
Active development and release branch. All regular work and official releases happen frommain.
Optional branches you may use later:
-
feature/*
Short-lived branches for experiments or isolated changes. Merge back intomainwhen done, then delete. -
stable(optional, future)
You can introduce astablebranch later if you need a dedicated GA branch. For now, it is not required; tags onmainprovide clear release points.
When in doubt, work directly on main, create tags for each release, and use Git history plus tags to reproduce any version.
2. Version Source Of Truth And Mirrored Locations¶
The canonical version string is defined in a single Python module and mirrored into pyproject.toml by the release tooling.
Canonical source:
src/pypnm/version.py
Recommended structure:
from __future__ import annotations
__all__ = ["__version__"]
# MAJOR.MINOR.MAINTENANCE.BUILD
__version__: str = "0.2.1.0"
Rules:
- Treat
src/pypnm/version.pyas the single source of truth. - The
[project].versionfield inpyproject.tomlis a mirror that must always match__version__. - Do not hand-edit the version in
pyproject.toml; let the release script maintain it.
Example FastAPI wiring:
from fastapi import FastAPI
from pypnm.version import __version__
app = FastAPI(
title="PyPNM REST API",
version=__version__,
description=fast_api_description,
openapi_url="/openapi.json",
docs_url="/docs",
redoc_url="/redoc",
)
2.1 Files Touched By A Release¶
When you run the release script, it updates or validates these locations:
-
src/pypnm/version.py
Canonical__version__definition. Updated to the new four-part version string. -
pyproject.toml
[project].versionis kept in lockstep with__version__. Updated to the same value as insrc/pypnm/version.py.
The release script also performs git operations (commit, tag, push) once the version is updated and tests pass.
3. Versioning Scheme¶
PyPNM uses a four-part version:
MAJOR.MINOR.MAINTENANCE.BUILD
Guidelines:
-
MAJOR
Increment when there are significant breaking changes or large structural shifts. -
MINOR
Increment when adding features in a backward-compatible way. -
MAINTENANCE
Increment when fixing bugs or making compatible improvements that are smaller than a full minor release. -
BUILD
Increment for very small hotfixes or internal rebuilds that do not change public behavior but still need a distinct version.
Note: any version with a non-zero BUILD segment is considered a hot-fix release (typically cut from the hot-fix branch). These releases are not treated as clean GA releases and are not guaranteed to pass the full test matrix.
Examples:
0.2.1.0- Minor feature set with maintenance updates.0.2.2.0- Next maintenance release.0.3.0.0- Next minor release.1.0.0.0- First major release.
All four segments must be numeric. Both src/pypnm/version.py and pyproject.toml must carry the same four-part string after a release.
Release candidate tags¶
For major, minor, and maintenance bumps, the release script asks whether the release is GA. If you answer no, it treats the tag as a release candidate and adds a -rcX suffix (for example v0.3.0.0-rc1). The version files remain numeric (0.3.0.0); only the git tag (and README/docs TAG placeholders) carry the RC suffix. Build bumps do not prompt for RC tags.
GitHub releases are created from tags. RC tags create GitHub prereleases, while GA tags create normal releases.
4. Release Script Overview (tools/release/release.py)¶
The tools/release/release.py script is the primary entry point for performing a release. It is responsible for:
- Computing or accepting a target version.
- Updating version strings in both version files.
- Running the test suite.
- Creating a release commit.
- Tagging the release.
- Pushing branch and tags to
origin. - Writing a markdown release report under
release-reports/with a summary table of the last commit and the workflow inventory.
You typically run this script directly; it internally handles all version file changes.
4.1 High Level Release Steps¶
When you run tools/release/release.py (and confirm the prompt when applicable), it performs the following:
- Read the current version from
src/pypnm/version.py. - Read the
[project].versionfrompyproject.tomland verify they match. - Compute the target version, either:
- From an explicit
--versionargument, or - From a
--nextmode (major, minor, maintenance, build), or - From an implicit maintenance bump when neither
--versionnor--nextis provided. - Display the planned version bump (for auto-computed versions) and wait for a
y/yesconfirmation. - Ensure the git working tree is clean.
- Checkout the target branch (default
main) and pull with--ff-only. - Update both version files to the target version.
- Optionally run
pytest(unless--skip-testsis used). - Optionally run local Docker build + health preflight (
tools/local/local_container_build.sh --smoke; skip with--skip-docker-test). - Build docs with
mkdocs --strict. - Commit the version bump.
- Create an annotated git tag (optionally with
-rcXfor major/minor/maintenance). - Push the branch and tag to
origin(skipped when running--test-release).
If any step fails (for example, tests fail or versions do not match), the script prints an error and exits without completing the release.
5. Release Modes And Examples¶
You can run the release script in different ways depending on how you want to control the version.
5.1 Automatic Maintenance Release (Default)¶
If you run tools/release/release.py with no version arguments, it computes the next maintenance version automatically.
Example:
tools/release/release.py
Behavior:
- Reads
current_version(for example0.2.1.0). - Computes
next_versionas the next maintenance version (for example0.2.2.0). - Prints:
Current version: 0.2.1.0
Planned version bump: 0.2.1.0 -> 0.2.2.0
Proceed with release? [y/N]:
- Only proceeds with the release if you respond with
yoryes. Any other input aborts.
This mode is convenient for routine maintenance updates.
5.2 Automatic Next Version By Mode (--next)¶
You can ask the script to compute the next version using a specific mode:
tools/release/release.py --next major
tools/release/release.py --next minor
tools/release/release.py --next maintenance
tools/release/release.py --next build
Behavior:
- Reads the current version.
- Computes the next version using the requested mode.
- Shows the current and proposed version, then waits for a
y/yesconfirmation. - Runs the release flow after you confirm.
Use this when you want to clearly indicate the type of release (major, minor, maintenance, build) without manually typing the version string.
For major, minor, and maintenance bumps, you will be prompted to confirm GA; if you answer no, the script will add an -rcX suffix to the tag. Build bumps do not prompt for RC tags.
5.3 Explicit Version (--version)¶
If you want complete control of the target version, you can pass it explicitly:
tools/release/release.py --version 0.3.0.0
Behavior:
- Validates the explicit version string.
- Skips auto-computation since you already chose the version.
- Does not ask for version confirmation (you have already decided the version).
- Proceeds with the release steps directly.
This mode is helpful when you have a predefined release version (for example, aligning with external documentation or planning notes).
5.4 Dry Run (--dry-run)¶
You can preview what the script would do without performing any changes:
tools/release/release.py --dry-run
tools/release/release.py --next minor --dry-run
tools/release/release.py --version 0.3.0.0 --dry-run
Behavior:
- Computes the target version (if needed).
- Prints a step-by-step list of planned actions, including the version bump and git operations.
- Exits without touching any files, running tests, committing, tagging, or pushing.
Use this to sanity-check a release before running it for real.
5.5 Skipping Tests (--skip-tests)¶
You can skip running pytest during a release (not recommended for normal use):
tools/release/release.py --skip-tests
tools/release/release.py --next minor --skip-tests
tools/release/release.py --version 0.3.0.0 --skip-tests
The release flow remains the same except the test step is skipped.
5.6 Target Branch (--branch) And Tag Prefix (--tag-prefix)¶
By default, the release script operates on main and uses v as the tag prefix.
You can override these:
# Release from stable
tools/release/release.py --branch stable
# Release from stable with minor bump
tools/release/release.py --branch stable --next minor
# Use a custom tag prefix, for example pypnm-0.3.0.0
tools/release/release.py --version 0.3.0.0 --tag-prefix pypnm-
If you use the optional aliases from scripts/install_aliases.sh, the defaults are:
pypnm-releaseruns onmainwith the default maintenance bump.pypnm-release-hot-fixruns onhot-fixand bumps the build segment.
The tag name is always built as <tag-prefix><version>, for example:
v0.2.2.0pypnm-0.3.0.0
If you select a release candidate for a major, minor, or maintenance bump, the tag name becomes <tag-prefix><version>-rcX, for example:
v0.3.0.0-rc1pypnm-1.0.0.0-rc2
5.7 Test-Only Release (--test-release)¶
Use this to exercise the full release flow locally without modifying Git history.
tools/release/release.py --test-release
Behavior:
- Runs the same checks as a normal release (hygiene scans, tests, docker/k8s preflight, mkdocs).
- Does not checkout/pull the release branch.
- Skips commit, tag, and push steps.
- Restores version files and TAG placeholders back to the original version.
5.8 Commit Report Only¶
Use these when you only want a change summary report without running the release flow:
# Report the previous commit
tools/release/release.py --last-commit-report
# Report the current commit
tools/release/release.py --latest-commit-report
Behavior:
- Writes a markdown report under
release-reports/. - Prints the section summary table in the CLI.
- If multiple commits are ahead of the upstream branch, includes an additional summary for the pending range.
6. Standard Release Flow On main¶
Below is an example of a typical release workflow using automatic maintenance bumping on main:
# 1) Make sure main is up to date and clean
git checkout main
git pull origin main
git status
# 2) Optional: inspect the current version
tools/release/release.py --dry-run
# 3) Run the real release (auto maintenance bump)
tools/release/release.py
Results:
src/pypnm/version.pyis updated (for example0.2.1.0->0.2.2.0).pyproject.toml[project].versionis updated to the same value.- A commit
Release 0.2.2.0is added onmain. - Tag
v0.2.2.0is created and pushed toorigin. - You can later reproduce this release with:
git checkout v0.2.2.0
If you prefer to explicitly control version semantics, replace the last step with:
# Minor release
tools/release/release.py --next minor
# Or a fully explicit version
tools/release/release.py --version 0.3.0.0
6. Preflight controls and prerequisites¶
- Skip tests:
--skip-tests(omits pytest). - Skip Docker preflight:
--skip-docker-test(omitstools/local/local_container_build.sh --smoke). - Docker preflight requirements: Docker Engine with buildx and the docker compose plugin on PATH (for Debian/Ubuntu you can install via apt, or use the official Docker CE repository), and permission to talk to the daemon (use
sudoor add your user to thedockergroup and re-login). If buildx is missing, Docker will fall back to the legacy builder and emit a deprecation warning during image builds.
Example (Debian/Ubuntu) to satisfy the preflight prerequisites:
sudo apt-get update
sudo apt-get install -y docker.io docker-buildx-plugin docker-compose-plugin
# allow non-root docker (log out/in after this):
sudo usermod -aG docker "$USER"
7. Quick Reference¶
- Automatic maintenance release (prompted):
tools/release/release.py
- Automatic next version by mode (prompted):
tools/release/release.py --next major
tools/release/release.py --next minor
tools/release/release.py --next maintenance
tools/release/release.py --next build
- Explicit version (no version prompt):
tools/release/release.py --version 0.3.0.0
- Dry run only:
tools/release/release.py --dry-run
- Release from another branch (for example future
stable):
tools/release/release.py --branch stable --next minor
- Test-only local release (no commit/tag/push):
tools/release/release.py --test-release
With this model, the tools/release/release.py script is the primary entry point for PyPNM releases. It computes or accepts the version, updates all necessary version files, runs tests, commits, tags, and pushes the result, giving you a repeatable and controlled release process.