Releasing base-cli
September 1, 2026 ยท View on GitHub
The base-cli distribution is built and published from the standalone
basefoundry/base-cli repository. The package name on PyPI is base-cli; the
Python import name is base_cli.
Version and tag contract
VERSION is the release version source of truth. The build backend reads it for
the wheel and sdist metadata, and base_cli.__version__ reports the same value
from a source checkout or from installed distribution metadata.
Production releases use a matching annotated-style tag such as v0.1.0.
The Package workflow rejects a tag that does not exactly match v${VERSION}.
Validation workflow
Pull requests and pushes to main start from a clean artifact destination,
build one sdist and one wheel, enforce the source allowlist, run twine check,
and install the reviewed wheel in an isolated environment. The installed-wheel
smoke test exercises public API, lifecycle, and output behavior without the
source tree on sys.path. Tests run across Python 3.10 through 3.14 on Linux,
macOS, and Windows, with Debian, Fedora, and WSL validation retained. Blocking
quality gates cover Ruff formatting/lint, strict public-sample typing, an 80%
branch-coverage threshold, documentation/example checks, and dependency/static
security scans. The performance contract also checks fresh
import and isolated invocation budgets, while the adversarial suite exercises
redaction, protocol framing, persistence, concurrency, retention, and signal
cleanup.
The publish job downloads that same reviewed artifact; it does not rebuild
during publication. The build also emits a deterministic SHA256SUMS file and
an SPDX 2.3 SBOM.spdx.json release artifact. On tag and protected dispatch
runs, GitHub's OIDC-backed actions/attest job records both build provenance
and an SBOM attestation for the exact artifact digests; no PyPI token or other
long-lived publish secret is used.
For a version tag, the same Package workflow creates a GitHub Release after
the protected PyPI publication and attestations succeed. The release attaches
the exact reviewed wheel, sdist, SHA256SUMS, and SBOM.spdx.json downloaded
from the build job. GitHub-generated comparison notes are supplemented by the
dated section in CHANGELOG.md; the tagged release is rejected when VERSION
or that section does not match the tag. Rerunning a tag updates an existing
release's assets with --clobber instead of creating a second release.
Independent verification
The release job uses the reviewed, hash-locked toolchain in
requirements/release.txt; it does not install mutable latest build or
publishing packages. The lock includes transitive release-path dependencies,
and the PEP 517 backend is pinned to the same setuptools and wheel versions in
pyproject.toml. The job builds two clean source archives with the same
SOURCE_DATE_EPOCH and rejects digest drift before publishing the reviewed
artifacts.
To intentionally refresh the toolchain, edit the four direct requirements in
requirements/release.in and regenerate the lock with:
uv pip compile requirements/release.in --python-version 3.13 \
--generate-hashes --output-file requirements/release.txt
Review the complete diff, run the package workflow on a pull request, and only then merge the update. Runtime dependency windows are deliberately not tied to this release-only toolchain.
Download the release metadata artifact from the successful Package workflow
run (the artifact is named base-cli-release-metadata-<run-id>), alongside
the wheel or sdist you downloaded from PyPI:
gh run download <run-id> \
--repo basefoundry/base-cli \
--name base-cli-release-metadata-<run-id> \
--dir release-metadata
sha256sum -c release-metadata/SHA256SUMS
The SPDX document's namespace and comment include the source revision used by the workflow. For a tagged release, verify the matching GitHub attestations with the GitHub CLI:
gh attestation verify base_cli-<version>-py3-none-any.whl \
--repo basefoundry/base-cli
The same command can verify the sdist. A clean-room verifier should compare
the downloaded artifact's digest with SHA256SUMS, confirm the SBOM namespace
contains the expected tag commit, and inspect the attestation's workflow and
repository identity before installation.
Changelog and release notes
CHANGELOG.md follows Keep a Changelog.
Keep [Unreleased] first, use one section per change category, and write
user-facing bullets rather than internal implementation notes. Every released
version must include its release date and a reference link at the bottom of the
file. Move entries from [Unreleased] into the dated section when the release
PR is prepared; do not rewrite an already published section.
The repository validates these rules in CI with
python scripts/validate_changelog.py, including duplicate bullets, duplicate
categories, missing release links, malformed dates, and accidental internal
planning text.
Documentation site
The Documentation workflow builds this site with mkdocs build --strict and
publishes the reviewed site to GitHub Pages after changes land on main. The
canonical URL is https://basefoundry.github.io/base-cli/ and is exposed in
the PyPI project metadata as the Documentation link.
Repository administrators should enable GitHub Pages for the repository using
the GitHub Actions source and approve the github-pages environment the first
time the workflow deploys. Pull requests run the strict build and repository
link checks without publishing.
TestPyPI rehearsal
-
Dispatch Package from the branch or tag to be rehearsed and choose
testpypi. -
Approve the protected
testpypienvironment when prompted. -
Verify the published artifact from a clean environment:
python -m venv /tmp/base-cli-smoke /tmp/base-cli-smoke/bin/python -m pip install \ --index-url https://test.pypi.org/simple/ \ --extra-index-url https://pypi.org/simple/ \ base-cli /tmp/base-cli-smoke/bin/python -c \ 'import base_cli; print(base_cli.__version__)'
The testpypi GitHub environment must be configured with PyPI trusted
publishing for this repository and workflow before the dispatch can upload.
Production release
-
Update
VERSIONand the changelog in a reviewed pull request. -
Merge to
mainand create the matchingv${VERSION}tag. -
Approve the protected
pypienvironment. The workflow verifies the tag, dated changelog section, builds and tests the artifact, then publishes the exact artifact to PyPI via trusted publishing and creates the matching GitHub Release. -
Verify installation from PyPI and download the matching GitHub Release assets:
python -m venv /tmp/base-cli-smoke /tmp/base-cli-smoke/bin/python -m pip install --upgrade base-cli /tmp/base-cli-smoke/bin/python -c \ 'import base_cli; import importlib.metadata as m; assert base_cli.__version__ == m.version("base-cli"); print(base_cli.__version__)'
The pypi GitHub environment must require approval and be configured with the
PyPI trusted publisher for .github/workflows/package.yml. No long-lived PyPI
token is stored in the repository.
Recovery
PyPI versions cannot be overwritten. If validation fails, fix the branch and rerun the workflow before creating a tag. If TestPyPI succeeds but a production publish fails, inspect the workflow logs and rerun the same approved tag only after confirming that neither artifact nor metadata needs correction. A version that was published successfully must be incremented for the next release.