fix(workflows): delete and recreate draft release to publish#552
fix(workflows): delete and recreate draft release to publish#552WilliamBerryiii merged 1 commit intomainfrom
Conversation
- publish-release job hit same immutability wall (HTTP 422) - tag previously used by immutable release blocks gh release edit - replace gh release edit --draft=false with delete/recreate cycle - consolidate both steps to single API call with jq 🔧 - Generated by Copilot
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
There was a problem hiding this comment.
Pull request overview
Updates the release pipeline in .github/workflows/main.yml to avoid gh release edit failures caused by GitHub release immutability tied to a tag, by switching the final “publish” step to a delete-and-recreate approach.
Changes:
- Replace
gh release edit --draft=falsein thepublish-releasejob with a delete-and-recreate flow. - Consolidate release metadata capture to a single
gh release view --json name,bodycall parsed viajq.
| # Capture draft release metadata | ||
| RELEASE_JSON=$(gh release view "$TAG" --json name,body -R "$REPO") | ||
| NAME=$(echo "$RELEASE_JSON" | jq -r '.name') | ||
| echo "$RELEASE_JSON" | jq -r '.body' > /tmp/release-body.md | ||
| # Delete mutable draft; recreate as published | ||
| gh release delete "$TAG" --yes -R "$REPO" | ||
| gh release create "$TAG" --verify-tag \ | ||
| --title "$NAME" --notes-file /tmp/release-body.md -R "$REPO" |
There was a problem hiding this comment.
Deleting the draft release here will also delete all release assets that were uploaded in the earlier attest-and-upload and upload-plugin-packages jobs. The subsequent gh release create recreates the release metadata but does not restore those assets, so the published release will end up missing binaries. Consider either (a) re-uploading the previously built artifacts after recreating the release (e.g., download workflow artifacts in this job and gh release upload them), or (b) using an API approach that can publish without deleting the release object (if GitHub allows it for your immutability mode).
| # Capture draft release metadata | |
| RELEASE_JSON=$(gh release view "$TAG" --json name,body -R "$REPO") | |
| NAME=$(echo "$RELEASE_JSON" | jq -r '.name') | |
| echo "$RELEASE_JSON" | jq -r '.body' > /tmp/release-body.md | |
| # Delete mutable draft; recreate as published | |
| gh release delete "$TAG" --yes -R "$REPO" | |
| gh release create "$TAG" --verify-tag \ | |
| --title "$NAME" --notes-file /tmp/release-body.md -R "$REPO" | |
| # Publish existing draft release without deleting assets | |
| gh release edit "$TAG" --draft=false -R "$REPO" |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #552 +/- ##
==========================================
- Coverage 85.36% 85.34% -0.03%
==========================================
Files 23 23
Lines 4475 4475
==========================================
- Hits 3820 3819 -1
- Misses 655 656 +1
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
🤖 I have created a release *beep* *boop* --- ## [2.3.7](hve-core-v2.3.6...hve-core-v2.3.7) (2026-02-13) ### 🐛 Bug Fixes * **workflows:** delete and recreate draft release to publish ([#552](#552)) ([e3d6fca](e3d6fca)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: hve-core-release-please[bot] <254602402+hve-core-release-please[bot]@users.noreply.github.com>
…rors (#554) # Pull Request ## Description Fixes the persistent `HTTP 422: tag_name was used by an immutable release` error in the `publish-release` job. This is the **fifth iteration** (PRs #538 → #545 → #550 → #552) — all previous approaches failed because they attempted to delete and recreate releases or tags after publication, which GitHub's immutability model permanently forbids. ### Root Cause GitHub's [immutable releases documentation](https://docs.github.com/en/repositories/releasing-projects-on-github/about-releases#about-immutable-releases) states: > *"Git tags cannot be moved or deleted"* for immutable releases. *"Even if you delete a repository and create a new one with the same name, you cannot reuse tags that were associated with immutable releases."* The tag name is **permanently tainted** at GitHub's infrastructure level once a release is published with immutability enabled. Every delete/recreate approach was fundamentally doomed: | PR | Approach | Failure Mode | |----|----------|-------------| | #538 | `"draft": true` in config | Race condition — release-please can't find draft releases (lazy tag) | | #545 | `gh release edit --draft=true` | Can't edit published immutable release | | #550 | Delete published, recreate as draft | Worked for upload, but publish step failed | | #552 | Delete draft, recreate as published | HTTP 422 — tag name permanently reserved | ### Solution: Draft-First Flow Follow [GitHub's recommended workflow](https://docs.github.com/en/repositories/releasing-projects-on-github/about-releases#creating-immutable-releases): 1. **release-please creates release as draft** (`"draft": true` in config) 2. **Explicit git tag creation** via API (workaround for release-please's lazy tag behavior) 3. **Upload assets to the mutable draft** (existing `attest-and-upload` and `upload-plugin-packages` jobs) 4. **Publish**: `gh release edit --draft=false` — release becomes immutable with all assets already attached ### Draft Race Condition Workaround release-please with `"draft": true` uses lazy tag creation — the git tag isn't materialized until publish. Without the tag, release-please's GraphQL query returns null for `tag` and `tagCommit`, causing it to skip the draft and propose a bogus version bump. This was fixed upstream in [googleapis/release-please#2627](googleapis/release-please#2627) (`force-tag-creation` option), but the fix is **not yet released** in release-please-action (latest: v4.4.0 → release-please 17.1.3). We work around this by explicitly creating the git tag via API after draft creation. When a new release-please-action ships with PR #2627, replace the manual tag creation step with `"force-tag-creation": true` in `release-please-config.json`. ## Related Issue(s) Supersedes PRs #538, #545, #550, #552 ## Type of Change Select all that apply: **Code & Documentation:** - [x] Bug fix (non-breaking change fixing an issue) **Infrastructure & Configuration:** - [x] GitHub Actions workflow ## Testing - Verified YAML and JSON syntax (no lint errors) - Logic validated against [GitHub REST API docs](https://docs.github.com/en/rest/releases/releases), [immutable releases concept](https://docs.github.com/en/repositories/releasing-projects-on-github/about-releases#about-immutable-releases), and [release-please issue #1650](googleapis/release-please#1650) - The draft-first approach avoids all immutability enforcement because assets are uploaded to mutable drafts, and publish is a single `--draft=false` edit (not a delete/create) ## Checklist ### Required Checks - [x] Documentation is updated (if applicable) - [x] Files follow existing naming conventions - [x] Changes are backwards compatible (if applicable) ## Security Considerations - [x] This PR does not contain any sensitive or NDA information - [x] Any new dependencies have been reviewed for security issues ## Additional Notes - **v2.3.7 remediation**: The v2.3.7 tag is permanently tainted. A manual release may be needed if v2.3.7 assets need to be published. The next release-please cycle (v2.3.8+) will use the draft-first flow and should succeed cleanly. - **Future upgrade**: Once `release-please-action` includes `force-tag-creation` support, the manual tag creation step can be replaced with a single config line. This is documented in a code comment referencing googleapis/release-please#2627.
Description
The
publish-releasejob at the end of the release pipeline usesgh release edit --draft=falseto publish the draft release after asset uploads complete. This fails withHTTP 422: tag_name was used by an immutable release— GitHub enforces immutability at the tag level, so any release using a tag that was previously associated with an immutable release cannot be edited viagh release edit.This applies the same delete-and-recreate pattern from PR #550 to the
publish-releasejob: capture the draft's metadata, delete it, and recreate as a published release.Also consolidates both recreate steps (draft and publish) to use a single
gh release view --json name,bodyAPI call withjqinstead of two separate queries.Related Issue(s)
Fixes #543
Type of Change
Select all that apply:
Code & Documentation:
Infrastructure & Configuration:
Other:
.ps1,.sh,.py)Testing
publish-releasejob'sgh release edit --draft=false— this PR fixes that final stepChecklist
Required Checks
Required Automated Checks
The following validation commands must pass before merging:
npm run lint:mdnpm run spell-checknpm run lint:frontmatternpm run lint:md-linksnpm run lint:psSecurity Considerations
Additional Notes
After this change, the full release lifecycle is: release-please creates published (immutable) release → delete and recreate as draft (mutable) → package/attest/upload assets → delete draft and recreate as published. No
gh release editcalls remain in the pipeline.