Skip to content

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

Open
yakimoto wants to merge 1 commit into
mainfrom
fix/ver-001-github-release
Open

ci(release): create GitHub Release after successful PyPI publish (VER-001)#48
yakimoto wants to merge 1 commit into
mainfrom
fix/ver-001-github-release

Conversation

@yakimoto

@yakimoto yakimoto commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

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/jsoninfo.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 5c8efc0. Configure here.

Review in cubic

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.

…-001)

Adds a `release` job to release.yml, gated on `needs: publish` with
`if: needs.publish.result == success` (publish itself only runs on a real v* tag push
and includes post-publish PyPI verification) so it never fires on a failed publish or a
PR dry-run. Scoped to contents: write on just that job (workflow-level permissions stay
contents: read). Idempotent: re-uploads dist/ with --clobber if the release already
exists for the tag.
@yakimoto yakimoto added the rr:unrationed RF.P1 reviewer routing (#1039) label Sep 6, 2026
@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because your workspace is out of credits. Ask your workspace admin to add credits to resume reviews. Manage billing

@codeant-ai

codeant-ai Bot commented Sep 6, 2026

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Reviewed your PR 5c8efc0 Sep 06, 2026 · 01:28 01:29

@codeant-ai

codeant-ai Bot commented Sep 6, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @yakimoto, this account has used its review budget of 2,500,000 diff characters for the last 7 days.

You can request another review in 14 hours and 48 minutes by commenting @sourcery-ai review.

@cursor

cursor Bot commented Sep 6, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_30939d01-6471-4c5a-9457-d86aa16c46ab)

@sourcery-ai

sourcery-ai Bot commented Sep 6, 2026

Copy link
Copy Markdown

Reviewer's Guide

The workflow now automatically creates or repairs a GitHub Release with the built distribution artifacts after a tag-triggered PyPI publish is confirmed live, improving version/release consistency while preserving scoped permissions and adding post-upload validation.

Sequence diagram for verified PyPI publishing and GitHub Release creation

sequenceDiagram
    participant Publish as publish job
    participant PyPI as PyPI
    participant Release as release job
    participant GitHub as GitHub Releases

    Publish->>PyPI: twine upload
    Publish->>PyPI: Post-publish verification
    PyPI-->>Publish: Artifact live and installable
    Publish-->>Release: needs.publish success
    Release->>GitHub: download-artifact dist
    alt Release exists
        Release->>GitHub: gh release upload --clobber
    else Release does not exist
        Release->>GitHub: gh release create --generate-notes
    end
    Release->>GitHub: gh release view --json assets
    GitHub-->>Release: Release contains every dist asset
Loading

File-Level Changes

Change Details Files
Adds a gated, least-privilege release-publication job that creates or updates a GitHub Release only after the PyPI artifact has been successfully published and verified.
  • Runs only after successful tag-based publishing, including the existing post-publish PyPI verification.
  • Grants contents: write only to the new job while preserving workflow and publish permissions.
  • Downloads the existing dist artifact using a full commit-SHA-pinned action.
  • Creates releases with generated notes or updates existing releases idempotently using --clobber.
  • Validates that the release exists and contains every downloaded distribution asset.
  • Passes tag and repository values through environment variables rather than interpolating expressions in the shell script.
.github/workflows/release.yml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@codeant-ai codeant-ai Bot added the size:M This PR changes 30-99 lines, ignoring generated files label Sep 6, 2026
@coderabbitai

coderabbitai Bot commented Sep 6, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Team

Run ID: f60fd689-285d-4aba-a703-6a1493d44d0b

📥 Commits

Reviewing files that changed from the base of the PR and between 6b1afc1 and 5c8efc0.

📒 Files selected for processing (1)
  • .github/workflows/release.yml

Included review availability: 0 reviews are currently available. Your included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

📜 Recent review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (1)
.github/workflows/release.yml (1)

147-195: LGTM!


📝 Summary

Summary by CodeRabbit

  • Chores
    • GitHub Releases are now created or updated automatically after successful PyPI publishing for version tags.
    • Built distribution files are attached to the corresponding release.
    • Release creation verifies that all expected distribution assets are present.

Walkthrough

The release workflow now creates or updates a GitHub Release after successful PyPI publishing. It downloads distribution artifacts, uploads them with idempotent behavior, and verifies that every artifact is attached.

Changes

Release publication

Layer / File(s) Summary
Post-publish release job
.github/workflows/release.yml
The workflow downloads the dist artifact after successful PyPI publication, creates or updates the GitHub Release, uploads all distributions with --clobber, and fails when artifacts or release assets are missing.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 5c8ef

Successful tagged PyPI publications will now produce or update a matching GitHub Release with the built distribution assets. Artifact presence and completeness checks limit the risk of publishing incomplete releases.

Sequence Diagram(s)

sequenceDiagram
  participant PyPI
  participant GitHubActions
  participant GitHubRelease
  PyPI-->>GitHubActions: successful publication
  GitHubActions->>GitHubActions: download dist artifact
  GitHubActions->>GitHubRelease: create or update release
  GitHubActions->>GitHubRelease: upload distribution assets
  GitHubRelease-->>GitHubActions: release assets
  GitHubActions->>GitHubActions: verify every distribution asset
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description check ✅ Passed The description clearly explains the new post-publish GitHub Release job, its permissions, artifact handling, idempotency, validation, and known limitations. It is directly related to the changeset.
Title check ✅ Passed The title concisely and accurately identifies the main change: creating a GitHub Release after a successful PyPI publish.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/ver-001-github-release
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch fix/ver-001-github-release

Comment @coderabbitai help to get the list of available commands.

@gitar-bot

gitar-bot Bot commented Sep 6, 2026

Copy link
Copy Markdown

Note

Automatic reviews are paused because your team has used its included automatic processing for this billing period (headroom scales with your seat count). You can still comment "Gitar review" to run one anytime, and automatic reviews resume on their own by October 1. Add seats for more headroom.
Learn more

Code Review ✅ Approved

Adds a release job to the CI workflow that creates a GitHub Release after successful PyPI publication, with proper gating on publish success, scoped contents: write permissions, pinned artifact-download action, and idempotent re-upload handling. No issues found.

Options

Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@macroscopeapp

macroscopeapp Bot commented Sep 6, 2026

Copy link
Copy Markdown

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — The workflow now automatically creates or mutates public GitHub Releases and uploads distribution artifacts after successful PyPI publication, using a new contents:write permission. This expands the production release process beyond routine CI configuration and introduces a durable external side effect.

Not approved because:

  • Credit balance exhausted. Approvability relies on correctness review in order to determine eligibility

Review your spending limits in Billing settings. You can add or adjust custom eligibility rules. Learn more.

Comment on lines +156 to +159
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: dist
path: dist

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The release downloads artifacts from the earlier build, while PyPI receives a separate rebuild, so non-reproducible metadata can make release assets differ from published artifacts. [api mismatch]

Assessment: 🟠 Major · 🔁 Occurrence: Sometimes

Use CodeAnt Skill Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** .github/workflows/release.yml
**Line:** 156:159
**Comment:**
	*Api Mismatch: The release downloads artifacts from the earlier build, while PyPI receives a separate rebuild, so non-reproducible metadata can make release assets differ from published artifacts.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

echo "::error::no files under dist/ to attach to the release"
exit 1
fi
if gh release view "$TAG_NAME" >/dev/null 2>&1; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Any gh release view failure is treated as absence, so authentication, repository, or transient API errors trigger misleading creation attempts and hide the original failure. [logic error]

Assessment: 🟠 Major · 🔁 Occurrence: Rarely

Use CodeAnt Skill Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** .github/workflows/release.yml
**Line:** 176:176
**Comment:**
	*Logic Error: Any `gh release view` failure is treated as absence, so authentication, repository, or transient API errors trigger misleading creation attempts and hide the original failure.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 1 file

Confidence score: 2/5

  • In .github/workflows/release.yml, treating every gh release view failure as “not found” can mask authentication, repository, or transient API failures and trigger a misleading release creation—distinguish not-found responses before entering the create branch.
  • In .github/workflows/release.yml, rebuilding instead of publishing the exact dist/ artifact used by the PyPI job can produce different metadata or bytes, leaving the GitHub Release out of sync—reuse the existing artifact or make the release consume the PyPI job’s artifact.
  • In .github/workflows/release.yml, deleting the tag before gh release create can cause the release to be created from the default branch while attaching artifacts built from the original tag—add --verify-tag so the job fails rather than publishing a mismatched release.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".github/workflows/release.yml">

<violation number="1" location=".github/workflows/release.yml:158">
P1: Publish the exact `dist/` artifact used here, or make the release consume the artifact produced by the PyPI publish job. A separate rebuild can produce different metadata or bytes, leaving the GitHub Release out of sync with the package verified on PyPI.</violation>

<violation number="2" location=".github/workflows/release.yml:176">
P1: Distinguish a not-found response from other `gh release view` failures before entering the create branch. Otherwise authentication, repository, or transient API errors are masked and can produce a misleading create attempt.</violation>

<violation number="3" location=".github/workflows/release.yml:181">
P2: If the tag is deleted before this command runs, `gh release create` creates it from the default branch and attaches artifacts built from the original tag. Add `--verify-tag` so the job fails instead of publishing a release for a different commit.</violation>
</file>
Architecture diagram
sequenceDiagram
    participant Git as Git Tag Push
    participant WF as Workflow Dispatch
    participant Build as build Job
    participant Pub as publish Job
    participant PyPI as PyPI Registry
    participant Rel as release Job
    participant GH as GitHub Release
    participant Art as Artifact Store

    Note over Git,GH: VER-001: GitHub Release after verified PyPI publish

    Git->>WF: push tag (refs/tags/v*)
    
    WF->>Build: checkout + build
    Build->>Art: upload dist/ artifact
    Art-->>Build: artifact stored
    
    alt tag matches refs/tags/v*
        Build->>Pub: need success
        Pub->>PyPI: twine upload / Trusted Publishing
        PyPI-->>Pub: published
        Pub->>PyPI: pip index versions (verify)
        PyPI-->>Pub: package visible
        Pub->>PyPI: install fresh wheel
        PyPI-->>Pub: install OK
        Pub->>Pub: assert wave_sdk.__version__
        
        alt publish success
            Pub-->>Rel: trigger (needs.publish.result == 'success')
            Rel->>Art: download dist/ (pinned SHA)
            Art-->>Rel: dist/ files
            
            Rel->>GH: gh release view TAG_NAME
            alt release exists
                GH-->>Rel: found
                Rel->>GH: upload --clobber (idempotent)
            else no release
                GH-->>Rel: not found
                Rel->>GH: create --generate-notes
            end
            
            Rel->>GH: verify all assets attached
            GH-->>Rel: asset list
            alt all files present
                Rel-->>WF: VER-001 satisfied
            else file missing
                Rel-->>WF: fail with error
            end
        end
    end
Loading

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment on lines +176 to +182
if gh release view "$TAG_NAME" >/dev/null 2>&1; then
echo "release $TAG_NAME already exists — uploading dist/ (idempotent path, --clobber)"
gh release upload "$TAG_NAME" "${ASSETS_TO_UPLOAD[@]}" --clobber
else
echo "release $TAG_NAME does not exist — creating with generated notes"
gh release create "$TAG_NAME" "${ASSETS_TO_UPLOAD[@]}" --title "$TAG_NAME" --generate-notes
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Distinguish a not-found response from other gh release view failures before entering the create branch. Otherwise authentication, repository, or transient API errors are masked and can produce a misleading create attempt.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/release.yml, line 176:

<comment>Distinguish a not-found response from other `gh release view` failures before entering the create branch. Otherwise authentication, repository, or transient API errors are masked and can produce a misleading create attempt.</comment>

<file context>
@@ -134,3 +134,62 @@ jobs:
+            echo "::error::no files under dist/ to attach to the release"
+            exit 1
+          fi
+          if gh release view "$TAG_NAME" >/dev/null 2>&1; then
+            echo "release $TAG_NAME already exists — uploading dist/ (idempotent path, --clobber)"
+            gh release upload "$TAG_NAME" "${ASSETS_TO_UPLOAD[@]}" --clobber
</file context>
Suggested change
if gh release view "$TAG_NAME" >/dev/null 2>&1; then
echo "release $TAG_NAME already exists — uploading dist/ (idempotent path, --clobber)"
gh release upload "$TAG_NAME" "${ASSETS_TO_UPLOAD[@]}" --clobber
else
echo "release $TAG_NAME does not exist — creating with generated notes"
gh release create "$TAG_NAME" "${ASSETS_TO_UPLOAD[@]}" --title "$TAG_NAME" --generate-notes
fi
release_view_error="$(mktemp)"
if gh release view "$TAG_NAME" >/dev/null 2>"$release_view_error"; then
rm -f "$release_view_error"
echo "release $TAG_NAME already exists — uploading dist/ (idempotent path, --clobber)"
gh release upload "$TAG_NAME" "${ASSETS_TO_UPLOAD[@]}" --clobber
else
release_view_status=$?
if ! grep -Eqi 'not found|404' "$release_view_error"; then
cat "$release_view_error" >&2
rm -f "$release_view_error"
exit "$release_view_status"
fi
rm -f "$release_view_error"
echo "release $TAG_NAME does not exist — creating with generated notes"
gh release create "$TAG_NAME" "${ASSETS_TO_UPLOAD[@]}" --title "$TAG_NAME" --generate-notes
fi

steps:
- uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: dist

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Publish the exact dist/ artifact used here, or make the release consume the artifact produced by the PyPI publish job. A separate rebuild can produce different metadata or bytes, leaving the GitHub Release out of sync with the package verified on PyPI.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/release.yml, line 158:

<comment>Publish the exact `dist/` artifact used here, or make the release consume the artifact produced by the PyPI publish job. A separate rebuild can produce different metadata or bytes, leaving the GitHub Release out of sync with the package verified on PyPI.</comment>

<file context>
@@ -134,3 +134,62 @@ jobs:
+    steps:
+      - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
+        with:
+          name: dist
+          path: dist
+
</file context>

gh release upload "$TAG_NAME" "${ASSETS_TO_UPLOAD[@]}" --clobber
else
echo "release $TAG_NAME does not exist — creating with generated notes"
gh release create "$TAG_NAME" "${ASSETS_TO_UPLOAD[@]}" --title "$TAG_NAME" --generate-notes

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: If the tag is deleted before this command runs, gh release create creates it from the default branch and attaches artifacts built from the original tag. Add --verify-tag so the job fails instead of publishing a release for a different commit.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/release.yml, line 181:

<comment>If the tag is deleted before this command runs, `gh release create` creates it from the default branch and attaches artifacts built from the original tag. Add `--verify-tag` so the job fails instead of publishing a release for a different commit.</comment>

<file context>
@@ -134,3 +134,62 @@ jobs:
+            gh release upload "$TAG_NAME" "${ASSETS_TO_UPLOAD[@]}" --clobber
+          else
+            echo "release $TAG_NAME does not exist — creating with generated notes"
+            gh release create "$TAG_NAME" "${ASSETS_TO_UPLOAD[@]}" --title "$TAG_NAME" --generate-notes
+          fi
+          echo "verifying the release exists and carries every dist/ asset"
</file context>
Suggested change
gh release create "$TAG_NAME" "${ASSETS_TO_UPLOAD[@]}" --title "$TAG_NAME" --generate-notes
gh release create "$TAG_NAME" "${ASSETS_TO_UPLOAD[@]}" --title "$TAG_NAME" --generate-notes --verify-tag

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

rr:unrationed RF.P1 reviewer routing (#1039) size:M This PR changes 30-99 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant