Release Process¶
This document describes the automated release process for httptap.
Overview¶
Releases are fully automated using GitHub Actions. The workflow handles versioning, changelog generation, testing, building, signing, publishing to TestPyPI and PyPI, and pushing a signed container image to GHCR.
Prerequisites¶
Before creating a release, ensure:
- GitHub Environments -
release,testpypi, andpypienvironments configured in repository settings, each allowing deployments frommainonly;pypihas a required reviewer - PyPI Trusted Publishing - Configured for both PyPI and TestPyPI (OIDC, no tokens)
- Deploy Key - SSH deploy key with write access, stored as the
DEPLOY_KEYsecret of thereleaseenvironment only, and allowed to bypass themainbranch protection and the tag ruleset that protectsrefs/tags/v* - GHCR access -
packages: writepermission on the release job (granted per-workflow) - All tests passing - CI must be green on main branch
Release Workflow¶
The release process is triggered manually via GitHub Actions.
Triggering a Release¶
- Go to Actions → Release workflow
- Click Run workflow
- Choose version strategy:
- Explicit version: Enter exact version (e.g.,
0.3.0) - Semantic bump: Select
patch,minor, ormajor
- Explicit version: Enter exact version (e.g.,
Semantic Versioning¶
| Bump Type | Example | Use Case |
|---|---|---|
patch | 0.1.0 → 0.1.1 | Bug fixes, small improvements |
minor | 0.1.0 → 0.2.0 | New features, backwards compatible |
major | 0.1.0 → 1.0.0 | Breaking changes |
What Happens Automatically¶
-
Version Update
Updatesversioninpyproject.toml -
Lockfile Refresh
Regeneratesuv.lockso it stays in sync with the new version -
Changelog Generation
Generates changelog from conventional commits -
Signed Commit and Tag (local only)
Keyless Sigstore signing via gitsign: a short-lived Fulcio certificate is issued through the workflow's OIDC identity, so no long-lived GPG keys are required. Nothing is pushed yet: the commit and tag travel to the next jobs as a git bundle artifact.git commit -S -m "chore: release v0.2.0" git tag -s v0.2.0 -m "Release v0.2.0" git bundle create release.bundle "^$GITHUB_SHA" HEAD refs/tags/v0.2.0 -
Build
Runs on the unpushed release tag from the bundle. The end-to-end suite then runs the CLI from the built wheel, installed with theuv sync --locked --no-dev --group test uv run --no-sync pytest # Full test suite uv build # Create wheel and sdist uv venv "$RUNNER_TEMP/httptap-wheel" uv pip install --python "$RUNNER_TEMP/httptap-wheel" "$(echo dist/httptap-*.whl)[otel]" uv sync --locked --no-dev --no-install-project --group test --group e2e uv run --no-sync pytest tests/e2e --no-cov -n auto --httptap "$RUNNER_TEMP/httptap-wheel/bin/httptap"otelextra, so a packaging mistake fails the release before anything is attested or uploaded. -
Push Commit and Tag
Only after the build and attestation succeed. The push is fast-forward only and atomic, so ifmainmoved during the release neither the branch nor the tag is updated and the workflow stops here, before anything is published. -
Publish to TestPyPI
- Uploads to TestPyPI first via OIDC Trusted Publishing, with PEP 740 attestations, as a smoke test before the production push.
-
Publish to PyPI
- Uses OIDC Trusted Publishing (no tokens required)
- Uploads wheel and source distribution with PEP 740 attestations
-
Publish container image to GHCR
- Runs only after the PyPI publish, so it waits for the
pypireview - Builds multi-arch (linux/amd64, linux/arm64) image
- Pushes to
ghcr.io/ozeranskii/httptapwith{version},{major}.{minor},{major}, andlatesttags - Signs the image with cosign (keyless Sigstore)
- Attaches SLSA build provenance via
actions/attest-build-provenance
- Runs only after the PyPI publish, so it waits for the
-
GitHub Release
- Creates release with generated notes
- Attaches build artifacts, SBOMs, VEX, and the man page
Workflow Configuration¶
The release workflow is defined in .github/workflows/release.yml:
Key Jobs¶
1. Prepare Release¶
- Checks out code (read-only, no deploy key)
- Configures Python and uv
- Updates version in pyproject.toml
- Generates changelog
- Adds the release to the
fixedstatements of.vex/httptap.openvex.jsonand bumps the document version - Creates the signed release commit and tag locally
- Uploads them as a
release-bundleartifact; nothing is pushed
2. Build Package¶
- Checks out the unpushed release tag from the bundle
- Runs full test suite
- Builds wheel and sdist
- Runs the end-to-end suite (
tests/e2e) against the built wheel with theotelextra - Generates SBOM in CycloneDX and SPDX JSON formats via Syft
- Fails if a
fixedstatement in.vex/httptap.openvex.jsondoes not list the release, then copies the document into thesbom/directory ashttptap-X.Y.Z.openvex.json - Generates a gzipped
man(1)page with argparse-manpage - Uploads
dist/,sbom/, andman/artifacts separately
3. Push Release Commit and Tag¶
- Runs only after the build and provenance attestation succeed
- The only job that writes to the git repository; it pushes over SSH with the deploy key of the
releaseenvironment, so its workflow token is read-only (contents: read) - Fast-forwards
mainto the release commit and pushes the tag in one atomic push; fails without publishing anything ifmainmoved during the release
4. Publish to TestPyPI¶
- Downloads
dist/artifacts - Publishes via TestPyPI OIDC Trusted Publishing with PEP 740 attestations
5. Publish to PyPI¶
- Runs only after TestPyPI succeeds
- Publishes using Trusted Publishing with PEP 740 attestations
6. Publish container image to GHCR¶
- Runs only after the PyPI publish, so nothing reaches GHCR before the
pypienvironment's review - Builds multi-arch image with Buildx + QEMU
- Signs with cosign (keyless Sigstore OIDC)
- Attaches SLSA build provenance
7. Create GitHub Release¶
- The only job with
contents: write, which it needs to create the release - Downloads
dist/,sbom/, andman/artifacts - Creates GitHub release with changelog notes
- Attaches wheel, sdist, SBOM (
*.cdx.json,*.spdx.json), VEX (*.openvex.json), and the man page
Changelog Generation¶
Changelogs are automatically generated using git-cliff based on conventional commits.
Commit Format¶
Supported Types¶
| Type | Changelog Section | Example |
|---|---|---|
feat | Features | feat(cli): add --timeout flag |
fix | Bug Fixes | fix(tls): handle expired certificates |
perf | Performance | perf(dns): optimize resolver cache |
docs | Documentation | docs: update API reference |
refactor | Refactor | refactor(core): extract analyzer logic |
test | Testing | test: add integration tests |
chore | Miscellaneous | chore: update dependencies |
Breaking Changes¶
Mark breaking changes in commit footer:
feat(api): redesign analyzer interface
BREAKING CHANGE: HTTPTapAnalyzer constructor signature changed
Version Strategy¶
httptap follows Semantic Versioning:
- Major version (1.0.0) - Breaking changes
- Minor version (0.1.0) - New features, backwards compatible
- Patch version (0.0.1) - Bug fixes
Pre-1.0 Development¶
During pre-1.0 development (0.x.x):
- Minor version may include breaking changes
- Patch version for bug fixes and minor features
- Move to 1.0.0 when API is stable
Troubleshooting¶
Branch Protection Errors¶
If push fails due to branch protection:
- Verify deploy key has write access
- Check deploy key is in bypass list for the branch protection rules and the
refs/tags/v*tag ruleset - Ensure
ssh-keyis configured in workflow checkout - Check that
DEPLOY_KEYis a secret of thereleaseenvironment and that the workflow was run frommain, the only branch the environment accepts
Changelog Empty¶
If changelog generation returns empty:
- Ensure commits follow conventional format
- Check git-cliff configuration in
.release/git-cliff.toml - Verify tag doesn't already exist
PyPI Publishing Fails¶
If PyPI publishing fails:
- Verify
pypienvironment exists - Check Trusted Publishing is configured on PyPI
- Ensure workflow has
id-token: writepermission
Test Failures¶
If tests fail during release:
- Workflow will stop before publishing
- Fix issues and re-run workflow
- No partial releases will occur
Post-Release¶
After successful release:
- Verify package on PyPI: https://pypi.org/project/httptap/
- Check GitHub release: https://github.com/ozeranskii/httptap/releases
- Test installation:
uv pip install httptap=={version} - Announce release (e.g. GitHub Discussions, Telegram)
Release Checklist¶
Before triggering release:
- All CI checks passing on main
- No known critical bugs
- Documentation updated
- Breaking changes documented
- Migration guide written (for major versions)
- Dependencies updated
- Security vulnerabilities addressed