ci(release): create GitHub Release after successful PyPI publish (VER-001)

YA
1mo ago
+59-0

User description

Why

GA gate criterion VER-001 ("Version and release truth agree from source through
deployment") is a must-pass blocker, currently fail. The measured finding
(governance/ga-gate/evidence/VER-001-release-ledger-2026-09-05.md in
wave-av/claude-workstation) is that no release.yml in any of the four public
release-publishing repos ever creates a GitHub Release — this repo's "latest" Release
is v1.0.0 (2026-04-05) while main/pyproject.toml is already at 2.1.0.

What changed

Adds a release job to .github/workflows/release.yml, after the existing publish
job:

  • Gate: needs: publish with if: needs.publish.result == 'success'. publish
    itself only runs if: startsWith(github.ref, 'refs/tags/v') (never on a
    pull_request dry-run) and already includes its own "Post-publish verification"
    step (waits for pip index versions, installs the fresh wheel from PyPI, asserts
    wave_sdk.__version__), so this job only ever fires once the artifact is confirmed
    live.
  • Permissions: contents: write scoped to only this job. Workflow-level
    permissions: is unchanged (contents: read).
  • No untrusted interpolation: the tag name comes in via env: TAG_NAME: ${{ github.ref_name }}, read as a shell variable; no ${{ }} appears inside any run:
    body in the new job.
  • Idempotent: a Release that already exists for the tag gets its dist/ artifacts
    re-uploaded with --clobber instead of failing.
  • Assets: downloads the dist artifact the build job already uploads
    (actions/upload-artifact), via actions/download-artifact@018cc2cf… pinned by full
    commit SHA (verified against gh api repos/actions/download-artifact/git/refs/tags/v6.0.0
    — 018cc2cf5baa6db3ef3c5f8a56943fffe632ef53), and attaches every file under dist/
    to the Release.

Verification performed

  • python3 -c "import yaml; yaml.safe_load(open('.github/workflows/release.yml'))" —
    parses clean, 3 jobs (build, publish, release).
  • actionlint .github/workflows/release.yml — exit 0, no findings.
  • Confirmed no ${{ }} inside a run: body in the new job (only inside env:).
  • Confirmed job-level permissions:: workflow stays contents: read; publish keeps
    id-token: write + contents: read; only release adds contents: write.
  • Verified the download-artifact SHA pin against the GitHub API directly, rather than
    trusting a remembered value — an earlier draft of this pin was wrong.

What this PR does NOT do

This PR does not create any GitHub Release, and there is no back-fill candidate
for this repo today
— and the gap here is worse than a missing Release. This repo's
own package (wave-sdk on PyPI) is stuck at 2.0.0
(curl https://pypi.org/pypi/wave-sdk/json → info.version = "2.0.0"), even though
main's pyproject.toml is at 2.1.0 and a v2.1.0 tag already exists. Per the GA
evidence doc, this repo's own release.yml run on v2.1.0 failed with
invalid-publisher — PyPI's registered Trusted Publisher for wave-sdk does not
resolve to this repo. Separately, the different package name wave-av-sdk (currently
3.0.0 on PyPI) carries provenance pointing at wave-av/sdks, not this repo. Neither
v2.1.0 (unpublished here) nor any other tag has an artifact on the registry this repo
actually publishes to, so there is nothing to back-fill: (b) does not apply until the
Trusted Publisher / package-ownership question (item (d) in the evidence doc's remediation
list) is resolved by Jake.

Part of the VER-001 GA burn-down lane (wave-av/claude-workstation
governance/ga-gate/spec/WAVE-GA-burndown-v1.0.0.json).


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


[!NOTE]
Cursor Bugbot is generating a summary for commit 5c8efc071082aa89139374c88ecfc2049a281d8b. Configure here.

Summary by Sourcery

Create a verified, artifact-backed GitHub Release after each successful PyPI publication.

New Features:

  • Create or update a GitHub Release for each successfully published and verified version tag, attaching the built distribution artifacts.

Enhancements:

  • Make GitHub Release creation idempotent and verify that all distribution artifacts are attached.
  • Restrict release-job repository write permissions to the job that creates the release.

CI:

  • Extend the release workflow with a post-publish GitHub Release job using a pinned artifact-download action.

Tests:

  • Validate the updated workflow syntax and action linting.

CodeAnt-AI Description

Create verified GitHub Releases after successful package publishing

What Changed

  • A GitHub Release is created automatically after the package is successfully published to PyPI and verified as installable
  • Release pages include the built distribution files and generated release notes
  • Existing releases are updated by re-uploading their distribution files instead of failing
  • The workflow verifies that every built file is attached before reporting success

Impact

✅ GitHub Releases stay aligned with verified PyPI packages
✅ Downloadable distribution files on every release
✅ Safe retries for interrupted or repeated releases

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.