Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: Release

on:
release:
types: [published]

permissions:
contents: read

jobs:
build:
name: Build release artifacts
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Verify release tag matches package version
shell: bash
run: |
set -euo pipefail
tag="${GITHUB_REF_NAME#v}"
version="$(python - <<'PY'
import tomllib
from pathlib import Path
data = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))
print(data["project"]["version"])
PY
)"
echo "tag=$tag package=$version"
test "$tag" = "$version"

- name: Build and inspect package
run: |
python -m pip install --upgrade build twine
python -m build
python -m twine check dist/*

- name: Smoke-test built wheel
shell: bash
run: |
set -euo pipefail
python -m venv /tmp/counterproof-release
/tmp/counterproof-release/bin/pip install dist/*.whl
/tmp/counterproof-release/bin/counterproof --help >/tmp/counterproof-help.txt
grep -q 'CounterProof\|Counterproof' /tmp/counterproof-help.txt

- uses: actions/upload-artifact@v4
with:
name: python-package-distributions
path: dist/
if-no-files-found: error
retention-days: 7

publish:
name: Publish to PyPI
needs: build
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/counterproof
permissions:
id-token: write

steps:
- uses: actions/download-artifact@v4
with:
name: python-package-distributions
path: dist/

- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
51 changes: 51 additions & 0 deletions docs/RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Releasing CounterProof

CounterProof publishes Python distributions through PyPI Trusted Publishing. The release workflow uses GitHub OIDC and does not store a long-lived PyPI API token.

## One-time setup

Before the first release:

1. In GitHub, create an environment named `pypi`.
2. In PyPI account settings, add a **pending GitHub publisher** with:
- PyPI project name: `counterproof`
- Owner: `hippoley`
- Repository: `CounterProof`
- Workflow filename: `release.yml`
- Environment: `pypi`
3. Keep the GitHub environment restricted to maintainers you trust. Requiring approval for the environment is recommended when available.

A pending publisher does not reserve the package name until the first successful publish, so do not treat setup alone as ownership of `counterproof`.

## Release procedure

1. Update `project.version` in `pyproject.toml`.
2. Merge all intended release changes to `main`.
3. Confirm main CI is green.
4. Create a GitHub Release whose tag is exactly `v<project.version>`, for example `v0.2.0`.
5. Publishing the GitHub Release triggers `.github/workflows/release.yml`.

The workflow refuses to publish if the release tag and `pyproject.toml` version disagree.

## What the workflow verifies

Before uploading anything, it:

- builds both source and wheel distributions;
- runs `twine check`;
- installs the built wheel into a clean virtual environment;
- verifies the installed `counterproof` CLI is present.

Only the publish job receives `id-token: write`, and that job is bound to the `pypi` GitHub environment.

## After publishing

Verify:

```bash
python -m pip install --upgrade counterproof
counterproof --help
counterproof doctor
```

Then update README installation examples from the GitHub URL to `pip install counterproof` only after the PyPI release is actually available.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "counterproof"
version = "0.2.0"
description = "Prove agent PR fixes by replaying changed tests against pre-change code."
description = "Prove AI-assisted PR claims with replayable BASE-to-HEAD evidence and scoped receipts."
readme = "README.md"
license = "Apache-2.0"
authors = [{ name = "hippoley" }]
Expand Down
Loading