Skip to content

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:

  1. GitHub Environments - release, testpypi, and pypi environments configured in repository settings
  2. PyPI Trusted Publishing - Configured for both PyPI and TestPyPI (OIDC, no tokens)
  3. Deploy Key - SSH deploy key with write access (for bypassing branch protection)
  4. GHCR access - packages: write permission on the release job (granted per-workflow)
  5. All tests passing - CI must be green on main branch

Release Workflow

The release process is triggered manually via GitHub Actions.

Triggering a Release

  1. Go to ActionsRelease workflow
  2. Click Run workflow
  3. Choose version strategy:
    • Explicit version: Enter exact version (e.g., 0.3.0)
    • Semantic bump: Select patch, minor, or major

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

  1. Version Update

    uv version 0.2.0  # or
    uv version --bump minor
    
    Updates version in pyproject.toml

  2. Lockfile Refresh

    uv lock
    
    Regenerates uv.lock so it stays in sync with the new version

  3. Changelog Generation

    git cliff --tag v0.2.0 --unreleased --prepend CHANGELOG.md
    
    Generates changelog from conventional commits

  4. Signed Commit and Tag

    git commit -S -m "chore: release v0.2.0"
    git tag -s v0.2.0 -m "Release v0.2.0"
    git push origin HEAD
    git push origin v0.2.0
    
    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.

  5. Build

    uv sync --locked --group test
    uv run pytest  # Full test suite
    uv build  # Create wheel and sdist
    

  6. Publish to TestPyPI

    • Uploads to TestPyPI first via OIDC Trusted Publishing, with PEP 740 attestations, as a smoke test before the production push.
  7. Publish to PyPI

    • Uses OIDC Trusted Publishing (no tokens required)
    • Uploads wheel and source distribution with PEP 740 attestations
  8. Publish container image to GHCR

    • Builds multi-arch (linux/amd64, linux/arm64) image
    • Pushes to ghcr.io/ozeranskii/httptap with {version}, {major}.{minor}, {major}, and latest tags
    • Signs the image with cosign (keyless Sigstore)
    • Attaches SLSA build provenance via actions/attest-build-provenance
  9. 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 with deploy key
  • Configures Python and uv
  • Updates version in pyproject.toml
  • Generates changelog
  • Commits and pushes changes
  • Creates and pushes git tag

2. Build Package

  • Checks out the tagged version
  • Runs full test suite
  • Builds wheel and sdist
  • Generates SBOM in CycloneDX and SPDX JSON formats via Syft
  • Copies the versioned OpenVEX document from .vex/httptap.openvex.json into the sbom/ directory as httptap-X.Y.Z.openvex.json
  • Generates a gzipped man(1) page with argparse-manpage
  • Uploads dist/, sbom/, and man/ artifacts separately

3. Publish to TestPyPI

  • Downloads dist/ artifacts
  • Publishes via TestPyPI OIDC Trusted Publishing with PEP 740 attestations

4. Publish to PyPI

  • Runs only after TestPyPI succeeds
  • Publishes using Trusted Publishing with PEP 740 attestations

5. Publish container image to GHCR

  • Builds multi-arch image with Buildx + QEMU
  • Signs with cosign (keyless Sigstore OIDC)
  • Attaches SLSA build provenance

6. Create GitHub Release

  • Downloads dist/, sbom/, and man/ 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

<type>(<scope>): <subject>

<body>

<footer>

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

Manual Release Steps

If you need to release manually (not recommended):

1. Update Version

uv version 0.2.0

2. Regenerate Lockfile

uv lock

3. Generate Changelog

git cliff --tag v0.2.0 --unreleased --prepend CHANGELOG.md

4. Commit Changes

git add pyproject.toml uv.lock CHANGELOG.md
git commit -m "chore: release v0.2.0"

5. Create Tag

git tag -a v0.2.0 -m "Release v0.2.0"

6. Push

git push origin main
git push origin v0.2.0

7. Build and Publish

uv build
uv publish  # Requires PyPI credentials

7. Create GitHub Release

Use gh CLI or web interface to create release with changelog notes.

Troubleshooting

Branch Protection Errors

If push fails due to branch protection:

  1. Verify deploy key has write access
  2. Check deploy key is in bypass list for branch protection rules
  3. Ensure ssh-key is configured in workflow checkout

Changelog Empty

If changelog generation returns empty:

  1. Ensure commits follow conventional format
  2. Check git-cliff configuration in .release/git-cliff.toml
  3. Verify tag doesn't already exist

PyPI Publishing Fails

If PyPI publishing fails:

  1. Verify pypi environment exists
  2. Check Trusted Publishing is configured on PyPI
  3. Ensure workflow has id-token: write permission

Test Failures

If tests fail during release:

  1. Workflow will stop before publishing
  2. Fix issues and re-run workflow
  3. No partial releases will occur

Post-Release

After successful release:

  1. Verify package on PyPI: https://pypi.org/project/httptap/
  2. Check GitHub release: https://github.com/ozeranskii/httptap/releases
  3. Test installation: uv pip install httptap=={version}
  4. Announce release (Twitter, Discord, etc.)

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

See Also