Skip to content
Merged
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
39 changes: 33 additions & 6 deletions scripts/tag-release.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
that sha is on main, matches the versionName being tagged, and that the AAB built from it exists
locally before creating the release.

The tag is created and pushed with git, then the release is created against it. `gh release
create --target <sha>` is NOT used: it returns HTTP 403 for Actions tokens and fine-grained
PATs even with contents: write (cli/cli#9514), while plain `git push` of the tag is unaffected.
That bug is invisible when running as a human, which is why it only surfaced once the release
moved into a workflow.

.PARAMETER Version Tag name / versionName, e.g. "1.0.49" (no "v" prefix — matches 1.0.49, the
first tag cut for this repo).
.PARAMETER Sha The chore/release-<version> bump's merge commit on main. Get it right after
Expand Down Expand Up @@ -45,9 +51,21 @@ if ($LASTEXITCODE -ne 0) { Write-Error "‘$Sha’ is not a commit in this repo.
git merge-base --is-ancestor $Sha origin/main
if ($LASTEXITCODE -ne 0) { Write-Error "$Sha is not an ancestor of origin/main — refusing to tag it."; exit 1 }

# 2. The tag must not already exist — no silent re-tagging.
$existing = git tag -l $Version
if ($existing) { Write-Error "Tag $Version already exists (points at $(git rev-parse $Version)). Bump the version or delete the stale tag first."; exit 1 }
# 2. An existing tag is either a re-run to finish, or a mistake. The tag is pushed before the
# release is created, so a failure between those two steps leaves the tag in place with nothing
# attached; refusing outright would make that state permanent. Same sha and no release yet means
# resume. A DIFFERENT sha is still a hard stop — that tag says something else shipped.
$tagExists = [bool](git tag -l $Version)
if ($tagExists) {
$taggedSha = (git rev-list -n 1 $Version)
if ($taggedSha -ne $Sha) {
Write-Error "Tag $Version already exists and points at $taggedSha, not $Sha. Bump the version or delete the stale tag first."
exit 1
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Resume rejects equivalent commit SHAs

Medium Severity

The new resume check compares the existing tag's commit from git rev-list to -Sha as raw strings. git rev-list always prints a full hash, so an abbreviated -Sha that every earlier git check already accepted looks like a different commit and aborts the resume.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5f5cc28. Configure here.

gh release view $Version *> $null
if ($LASTEXITCODE -eq 0) { Write-Error "$Version is already tagged AND released. Nothing to do."; exit 1 }
Write-Host "Tag $Version already present at $Sha with no release — creating the release for it."
}

# 3. versionName at that sha must match the tag being cut — catches a wrong/stale sha.
# git show returns a string[] (one element per line) in PowerShell; -match on an array filters
Expand All @@ -65,9 +83,18 @@ if (-not (Test-Path $aab)) {
Write-Warning "$aab not found locally — this tag should be cut AFTER building and uploading that bundle (release-signing-and-aab.md #3-5). Continuing anyway; verify you meant to."
}

Write-Host "Tagging $Version at $Sha (versionCode $versionCode)..."
$ghArgs = @("release", "create", $Version, "--target", $Sha, "--title", $Title)
# 5. Push the tag ourselves, then create the release against it. See .DESCRIPTION for why this is
# not `gh release create --target`.
if (-not $tagExists) {
Write-Host "Tagging $Version at $Sha (versionCode $versionCode)..."
git tag $Version $Sha
if ($LASTEXITCODE -ne 0) { Write-Error "Could not create the tag $Version locally."; exit 1 }
git push origin "refs/tags/$Version"
if ($LASTEXITCODE -ne 0) { Write-Error "Could not push the tag $Version. The local tag remains; delete it with ``git tag -d $Version`` if you are not retrying."; exit 1 }
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Retry skips pushing a local-only tag

Medium Severity

When a local tag already exists at -Sha, the script skips git push and treats that tag as already published. A leftover local tag after a failed push is not on origin, so gh release create without --target has no remote tag to attach to.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5f5cc28. Configure here.


$ghArgs = @("release", "create", $Version, "--title", $Title)
if ($NotesFile) { $ghArgs += @("-F", $NotesFile) } else { $ghArgs += "--generate-notes" }
gh @ghArgs
if ($LASTEXITCODE -ne 0) { Write-Error "gh release create failed."; exit 1 }
if ($LASTEXITCODE -ne 0) { Write-Error "gh release create failed. The tag $Version is pushed; re-run this script to attach the release."; exit 1 }
Write-Host "Done: https://github.com/$(gh repo view --json nameWithOwner -q .nameWithOwner)/releases/tag/$Version"