Release Support¶
Bijux Canon resolves one tag-derived release line into independently built
Python distributions and specialized PyPI, GHCR, and GitHub Release paths.
bijux-canon-dev owns version resolution and publication eligibility; build
logic owns wheel and source-distribution construction; workflows own registry
credentials and publication timing.
flowchart LR
source[Tagged source] --> version[Resolve version]
version --> guard[Publication guard]
guard --> build[Build wheel and sdist]
build --> metadata[Twine and package checks]
metadata --> stage[Stage release artifacts]
stage --> pypi[PyPI]
stage --> ghcr[GHCR]
stage --> github[GitHub Release]
Version Resolution¶
release.version_resolver evaluates sources in order:
- a static
[project].version, when declared; python -m hatch versionin the package directory;- the latest Git tag matching the package’s Hatch tag pattern or package tag convention;
0.0.0when none resolves.
The repository packages normally use Hatch VCS and the shared v<version> tag
pattern. A dirty or untagged checkout can resolve to a development or local
version suitable for testing but not normal publication.
Publication Guard¶
release.publication_guard rejects:
- prerelease identifiers unless
--allow-prereleaseis explicit; - local or dirty version identifiers unless
--allow-local-versionis explicit; - a distribution directory with no recognized wheel or source distribution;
- artifact filenames whose parsed version differs from the resolved version.
The Make integration separately refuses unresolved 0.0.0 and requires at
least one wheel and one source distribution. Exceptions permit an intentional
prerelease or local publication; they do not change the version embedded in
built files. The guard parses both wheel and .tar.gz filenames and reports
every mismatch.
flowchart TD
resolved[Resolved version] --> known{Not 0.0.0?}
known -- no --> refuse[Refuse publication]
known -- yes --> policy{Prerelease/local allowed?}
policy -- no --> refuse
policy -- yes --> files{Wheel and sdist exist?}
files -- no --> refuse
files -- yes --> match{Artifact versions match?}
match -- no --> refuse
match -- yes --> twine[Twine metadata validation]
Build Evidence¶
The shared build path creates both wheel and source distribution beneath the
configured package artifact directory and runs twine check unless explicitly
disabled. Repository contract tests additionally verify that public packages:
- include package source, README, and changelog in the source distribution;
- include typing metadata in the wheel;
- force-include the repository license;
- link package legal files to the root authority;
- keep package-local build products ignored;
- preserve compatibility-package build boundaries.
Package profiles may add release-specific checks before or after the common
build. Inspect the selected package’s release-dry behavior rather than
assuming all distributions have identical evidence.
Local Publication Is Safe by Default¶
The shared publish target checks the version, builds artifacts, and validates
them. Upload is disabled unless PUBLISH_UPLOAD_ENABLED=1. publish-test
similarly requires its explicit enable flag. Registry upload refuses missing
credentials.
SKIP_TWINE_CHECK=1, prerelease allowance, local-version allowance, and
SKIP_EXISTING=1 are visible policy choices. --skip-existing avoids failing
on a version already present; it cannot replace immutable registry bytes and
must not be used to imply that the local build is identical to the published
artifact.
TestPyPI installation verification exists only when a package configures
PUBLISH_VERIFY_INSTALL_CMD. An unconfigured target reports that it did not
run; it is not an installation pass.
Workflow Separation¶
| Workflow | Authority |
|---|---|
release-artifacts.yml |
build and stage package distributions and optional SBOM assets |
release-pypi.yml |
publish staged Python distributions |
release-ghcr.yml |
publish OCI release bundles to GHCR |
release-github.yml |
create or update GitHub Release assets |
Staging and publication remain separate so artifact contents can be reviewed before credentials are used. A successful artifact build does not prove PyPI publication, and a successful registry upload does not prove that GitHub Release or SBOM staging completed.
Release Acceptance Record¶
Retain:
- source tag, commit SHA, clean/dirty state, and resolved version;
- selected public package and compatibility-package inventory;
- wheel and source-distribution filenames and registry identities;
- publication-guard and Twine results;
- package-specific release-dry evidence;
- production and development SBOMs plus validation results;
- workflow run IDs and destination-specific publication results;
- changelog and compatibility decisions for caller-visible changes.
Published bytes are immutable. Recovery from an incorrect release uses a new source decision and version, with yanking or deprecation where supported; it does not rebuild and overwrite the existing version.
Failure Routing¶
| Failure | Owning boundary |
|---|---|
version is 0.0.0 or dirty |
version resolver, tag, or checkout state |
| artifact version mismatch | build environment or stale artifact directory |
| Twine metadata refusal | package metadata or build contents |
| missing wheel or sdist | build target and staged artifact path |
| missing PyPI credential | publication workflow or local secret injection |
| package absent from matrix | root public release inventory and workflow inputs |
| compatibility artifact points to wrong owner | compatibility package metadata and contract tests |
| SBOM absent | SBOM generation, validation, or release staging |
See SBOM and Supply Chain for inventory evidence, the root Release and Versioning guide for package-family policy, and Release Workflows for workflow orchestration.