vfairness · release pipeline · checked 1 Oct 2026

Release pipeline

How a version of vfairness gets from a code change to pip install vfairness: every step, who or what performs it, and what stops a bad release. Whether the library is ready to release is a separate question, answered on Quality & Hardening.

Status: 0.1.0 released on 2026-10-02

Version 0.1.0 is the first public beta, published to PyPI from the v0.1.0 tag through the workflow below, with a GitHub Release. What had to be true before it could go out:

  • DoneThe beta gate reads BETA READY: all 8 conditions met (see Quality & Hardening).
  • DoneSomeone can approve. The pypi environment on the public repository admits v-tags and requires an active reviewer before anything reaches PyPI.
  • DoneEvery CI check is green on the exact release commit, in the development repository and again on the public repository, including the full suite on Python 3.11 to 3.13 and the oldest declared dependency versions.
  • DonePyPI trusts exactly this repository and workflow (trusted publishing over OIDC, no stored token). The wheel and the sdist each carry a provenance attestation on PyPI.
  • DoneTag v0.1.0 on the public repository, published 2026-10-02.
  • DoneThe 0.0.1 placeholder is yanked on PyPI (2026-10-02), with the reason “Name reservation, contains no library code. Use 0.1.0 or later.” A resolver no longer selects it.

The pipeline at a glance

automaticdone by a persona gate that can stop the release

1. Develop

private repository · on every change
Automatic, blocking

Tests on every change

The full test suite on Python 3.11, 3.12 and 3.13, with optional backends required, plus lint, formatting and type checks.

Stops: a change that breaks a test, falls below the coverage floor (55% overall, 80% of new lines), or fails a type check.

Automatic, blocking

Reference and honesty checks

The 34 metrics with an outside reference are compared with fairlearn, scikit-learn, statsmodels and scipy; every measuring capability is fed broken data.

Stops: a number that drifts from the reference, or one invented where nothing was measurable.

Automatic, blocking

Security and the export boundary

Bandit, a dependency vulnerability audit, a secret scan, and a check that the export to the public repository carries nothing internal.

Stops: a known-vulnerable dependency, a leaked secret, or internal material on its way out.

2. Decide

maintainer · before cutting a release
A person runs it

Readiness gate

python scripts/release_gate.py must read BETA READY: all 8 beta conditions met.

Stops: a release while any condition is open. It is run by hand; no workflow runs it for you.

A person, one script

Version and wording

The version is set in one place, and a release script in the development repository changes every "not yet released" sentence to "released" in one commit.

Stops: a half-done change. A test fails if the changelog, README, citation file and site pages disagree about whether the version is released.

3. Export

private to public repository
A person runs it

Snapshot to the public repository

The export script copies the library to validantai/vfairness, screening every file against a list of what may leave, with patterns for secrets and a size limit.

Stops: internal files, secrets and oversized artifacts. It must run before publishing, because PyPI keeps the README as it was at upload.

4. Release

public repository · started by pushing a tag
Automatic gate

1. Suite green on this commit

Before anything is built, the run asks GitHub whether the full test suite passed on the exact commit being tagged, on every Python version.

Stops: a release from a commit whose tests failed, or never ran. A missing, cancelled or skipped run counts as a failure, not a pass.

Automatic

2. Build and verify

Checks that the tag matches the package version, builds the source and wheel packages, validates them, and generates a software bill of materials (SBOM).

Stops: a tag that does not match the version, a malformed package, or an SBOM that describes the build machine instead of the package.

Automatic

3. Clean-room smoke test

Installs the built wheel in a fresh environment and uses the public API from outside the source tree, so it tests what a user would install.

Stops: a package that installs but cannot be imported or used.

A person approves, then automatic

4. Publish to PyPI

The run pauses at the pypi environment until a required reviewer approves. Then it uploads through PyPI trusted publishing: no stored password or token, a short-lived credential, and signed provenance attestations.

Stops: an upload nobody approved, and a publish from anywhere else: it needs a pushed tag, a v-tag and the public repository, all three.

Automatic

5. Publish the GitHub Release

Creates the release for the tag and attaches the packages and the SBOM.

Runs only after the PyPI publish succeeded.

One-time setup

before the first release

A trusted publisher on PyPI

PyPI must trust exactly the public repository validantai/vfairness and its release.yml. Until it does, the publish fails closed: everything before the upload still runs.

The pypi environment, with a required reviewer

This is what makes a person approve every upload. If that environment had never been created, GitHub would create it implicitly on first use with no reviewers, so the approval would gate nothing and the run would go straight to the upload. It exists today with a required reviewer (checked 1 Oct 2026), but that reviewer is an account being retired and has to be replaced by an active one.

What is enforced, and what relies on a person

Enforced by the pipeline itself

  • Tests, coverage floors, lint, types and security on every change.
  • The full test suite must have passed on the exact commit being tagged; a missing or skipped run blocks the release.
  • The tag must match the package version.
  • Only the public repository can publish; a tag in the private repository builds and stops.
  • A person must approve every upload.
  • Trusted Publishing (OIDC). No stored PyPI password or token exists to steal.

Relies on the maintainer

  • Running the readiness gate before cutting a release; no workflow runs it.
  • Exporting before tagging, so PyPI shows the right README.
  • There is no TestPyPI rehearsal (removed 28 Aug 2026), so the gates above are the only protection before an upload that cannot be undone. A PyPI version cannot be overwritten.

The full operator runbook, including what to do if a release goes wrong, is docs/RELEASE_PIPELINE.md in the repository.