Release artifacts¶
A maintainer runs the pre-release workflow,
which writes the changelog, commits the release and pushes the version tag. That tag starts the release workflow, which builds and publishes every file below.
{version} stands for the release version, such as 21.10.0. Verify a virtualenv release shows how to check
each file, and Release integrity explains what the checks prove.
Files¶
File |
Where |
Contents |
|---|---|---|
|
the package, with its CycloneDX SBOM under |
|
|
PyPI |
the source distribution |
|
the zipapp: virtualenv and its runtime dependencies for every supported Python, with its own SBOM at the archive root |
|
|
GitHub release |
the Sigstore bundle holding the zipapp’s SLSA provenance |
|
GitHub release |
the zipapp’s CycloneDX SBOM, the same file the zipapp carries |
|
GitHub release |
the wheel’s CycloneDX SBOM, the same file the wheel carries |
|
GitHub release |
the wheel’s SBOM rendered as SPDX 2.3 |
|
|
the latest release’s zipapp |
|
|
the zipapp for Python |
The bootstrap copies come from the public directory of pypa/get-virtualenv, which the release workflow updates, and can trail the latest release.
Releases published before an asset existed do not have it; gh release view {version} --repo pypa/virtualenv lists
what a release carries. Releases from 21.11.0 on are immutable; GitHub rejects changes to their tag and assets after
publication.
Attestations¶
Attestation |
Subject |
Stored at |
|---|---|---|
PyPI publish attestation (PEP 740), predicate
|
wheel, sdist |
|
SLSA provenance, predicate |
|
GitHub attestations API and |
CycloneDX SBOM, predicate |
wheel, sdist |
GitHub attestations API, predicate equal to |
SPDX SBOM, predicate |
wheel, sdist |
GitHub attestations API, predicate equal to |
CycloneDX SBOM, predicate |
|
GitHub attestations API, predicate equal to |
GitHub release attestation, predicate |
the tag’s commit and every GitHub release asset, from 21.11.0 on |
GitHub attestations API, read by |
The release workflow signs every attestation except the last through Sigstore with the
identity https://github.com/pypa/virtualenv/.github/workflows/release.yaml@refs/tags/{version}. GitHub signs the
release attestation when it publishes an immutable release, with the identity https://dotcom.releases.github.com.
SBOMs¶
Wheel SBOM¶
Format: CycloneDX 1.6 JSON.
Location in the wheel:
virtualenv-{version}.dist-info/sboms/virtualenv.cdx.json, following PEP 770. Installers copy it into the installed.dist-infodirectory. The sdist does not carry one.Components: every wheel bundled under
virtualenv/seed/wheels/embed, with its SHA-256, license, the packages it vendors, and avirtualenv:seeded-for-pythonproperty per Python version that receives it; plus the runtime dependencies declared in the wheel metadata, without versions, since the installer resolves those.Build record: the Python version and build backend packages that produced the wheel, the source commit, and the
SOURCE_DATE_EPOCHused for timestamps. Releases up to 21.10.0 also recorded the operating system, architecture and interpreter build of the build machine.First release carrying it: 21.8.1.
SPDX rendering¶
virtualenv.spdx.json is the wheel SBOM rendered as SPDX 2.3 JSON for tools that read only SPDX. It keeps the
packages, SHA-256 hashes, declared licenses, purls and the containment, dependency and build tool relationships. It
leaves out the per-file hashes, because SPDX 2.3 requires a SHA-1 for every file and wheel RECORD files hold
SHA-256, and the build record, which SPDX 2.3 has no field for. Its document namespace ends in the CycloneDX serial
number, so both documents name the same build.
Zipapp SBOM¶
Format: CycloneDX 1.6 JSON, at
virtualenv.pyz.cdx.jsonin the zipapp’s root and as a release asset.Root component:
pkg:generic/virtualenv.pyz@{version}.Components: virtualenv, with a SHA-256 per file and the embedded
pipandsetuptoolswheels by purl and SHA-256; and each bundled distribution, such asfilelockorplatformdirs, with its version, license, avirtualenv:loaded-for-pythonproperty per Python version that imports it, and a SHA-256 per file. Every file in the archive other than the SBOM appears in it.Build record: the Python version and packages of the environment that built the zipapp, and the
SOURCE_DATE_EPOCHused for timestamps. Packages installed from platform-specific wheels appear without their files, which differ per operating system and architecture.
Embedded wheel advisories¶
Only Python 3.9 environments receive the embedded pip 26.0.1 and setuptools 82.0.1, and both versions have
published advisories that a scanner reading the SBOMs reports. pip 26.1 and setuptools 83 and later require
Python 3.10, so --pip and --setuptools cannot pick a fixed version for Python 3.9; create environments for
Python 3.10 or newer to avoid them.
Build reproducibility¶
The release sets SOURCE_DATE_EPOCH to the commit time of the tag (git log -1 --pretty=%ct). Rebuilding the tag
with the same value reproduces the sdist byte for byte.
The wheel, whose SBOM hatch_build.py writes, reproduces byte for byte on any operating system and architecture when these inputs match the release:
the source tree, as a git checkout of the tag, since the SBOM records the commit;
SOURCE_DATE_EPOCH;the Python patch version, which the SBOM records;
the versions of the build backend and its dependencies, which the SBOM lists.
Any build frontend works, since the SBOM leaves out the installer metadata a frontend writes into the build environment.
Wheels up to 21.10.0 recorded the build machine in their SBOM, so a rebuild of those differs in the SBOM and in
RECORD, which holds the SBOM’s hash.
The zipapp, built by tasks/make_zipapp.py, needs
the same inputs, and its entries carry SOURCE_DATE_EPOCH as their timestamp and fixed permissions. The build takes
the distributions it bundles from pylock.zipapp.toml, a PEP 751
lock that pins each wheel by SHA-256, and fails on a hash mismatch. A rebuild of a release from 21.11.0 on also needs
the versions of the tools that built it: the zipapp SBOM lists the packages of the build environment, and the wheel SBOM
inside the zipapp lists the backend that built that wheel.