Skip to content

Publish signed releases with a verified OpenAPI contract label - #10

Merged
rabeckett merged 2 commits into
mainfrom
feat/release-workflow
Sep 19, 2026
Merged

rabeckett merged 2 commits into
mainfrom
feat/release-workflow

Conversation

@rabeckett

Copy link
Copy Markdown
Collaborator

Adds a release pipeline and the provenance label consumers need, plus the refreshed cost routing tables.

Why

Consumers pin this image by digest and read io.github.gh-aw-router.openapi-sha256 to confirm the running container serves the contract they validated against. The Dockerfile never set that label. Only the local test harness injected it with --label, so any image built by CI would have been rejected at startup.

The label cannot drift

The label now comes from an OPENAPI_SHA256 build argument. The build copies openapi.yaml into the image and checks it with sha256sum --check --strict, so a mismatch fails the build rather than shipping a label that lies about the contract. Verified both directions locally: the correct digest produces the label, and a wrong one fails the build step.

tests/test_container.py computes the digest from the repository and asserts the built image carries it, so the existing container CI job exercises this on every push.

Release workflow

workflow_dispatch only, with a dry_run input that builds and verifies without publishing.

It reads the version from the package rather than bumping it, so a release is a deliberate follow-up to a reviewed version change, and it refuses to run if the tag already exists. The Dockerfile already asserts the build argument matches __version__.

Order matters in one place: it builds amd64 and runs the container contract suite against that exact image through GH_AW_ROUTER_TEST_IMAGE before pushing anything. After publishing it pulls by digest and re-inspects the labels against the same patterns consumers enforce, and confirms both platforms are present. A release that consumers would reject fails here instead of at run time.

The image is signed with cosign and carries an attested SPDX SBOM. All actions are pinned by SHA.

Routing tables

routing/cost-*.json are refreshed with current pricing and model data. Separate commit.

Verification

  • ruff format --check, ruff check, ty check clean
  • 408 tests pass. test_isolated_source fails only on the author machine, where public PyPI is TLS-intercepted; it downloads from files.pythonhosted.org directly and will pass in CI
  • Container build verified on arm64 with all four required labels correct

Consumers pin this image by digest and read
io.github.gh-aw-router.openapi-sha256 to confirm the running container
serves the contract they validated against. The Dockerfile never set that
label, so any image built outside the local test harness was unusable.

The label now comes from an OPENAPI_SHA256 build argument, and the build
copies openapi.yaml in and checks it with sha256sum, so the label cannot
drift from the shipped contract.

The release workflow is workflow_dispatch only. It reads the version from
the package instead of bumping it, so a release follows a reviewed version
change, and it refuses to run if the tag exists. It builds amd64 first and
runs the container contract suite against that exact image, then pushes
linux/amd64 and linux/arm64, signs with cosign, attests an SPDX SBOM, and
re-inspects the published labels against the patterns consumers enforce.
A release that consumers would reject therefore fails here instead.
@rabeckett
rabeckett merged commit 98fda7d into main Sep 19, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant