Skip to content

fix(workflows): use draft-first release flow to avoid immutability errors#554

Merged
WilliamBerryiii merged 1 commit intomainfrom
fix/publish-release-tag-immutable
Feb 14, 2026
Merged

fix(workflows): use draft-first release flow to avoid immutability errors#554
WilliamBerryiii merged 1 commit intomainfrom
fix/publish-release-tag-immutable

Conversation

@WilliamBerryiii
Copy link
Member

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 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:

  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 (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:

  • Bug fix (non-breaking change fixing an issue)

Infrastructure & Configuration:

  • GitHub Actions workflow

Testing

Checklist

Required Checks

  • Documentation is updated (if applicable)
  • Files follow existing naming conventions
  • Changes are backwards compatible (if applicable)

Security Considerations

  • This PR does not contain any sensitive or NDA information
  • 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 feat: add --force-tag option to explicitly create git tags for releases. googleapis/release-please#2627.

…rors

- add draft: true to release-please-config.json so releases start as drafts
- create git tag explicitly via API after draft creation (lazy tag workaround)
- replace delete/recreate publish step with gh release edit --draft=false
- remove bridge step that deleted immutable releases to recreate as drafts

🔧 - Generated by Copilot
@WilliamBerryiii WilliamBerryiii requested a review from a team as a code owner February 14, 2026 00:22
Copilot AI review requested due to automatic review settings February 14, 2026 00:22
@github-actions
Copy link

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@codecov-commenter
Copy link

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.34%. Comparing base (5fa5594) to head (119f40b).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #554      +/-   ##
==========================================
- Coverage   85.36%   85.34%   -0.03%     
==========================================
  Files          23       23              
  Lines        4475     4475              
==========================================
- Hits         3820     3819       -1     
- Misses        655      656       +1     
Flag Coverage Δ
pester 85.34% <ø> (-0.03%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.
see 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements a draft-first release flow to resolve persistent HTTP 422 immutability errors that plagued the previous four attempts (PRs #538, #545, #550, #552). The solution leverages GitHub's mutable draft releases: release-please creates releases as drafts, a workaround manually creates git tags (until upstream support for force-tag-creation lands), assets are uploaded to the mutable draft, and finally the draft is published via gh release edit --draft=false.

Changes:

  • Added "draft": true to release-please-config.json to create mutable draft releases
  • Replaced the delete-and-recreate release logic with a manual git tag creation workaround for draft releases with lazy tag behavior
  • Simplified the publish step from delete-and-recreate to a simple gh release edit --draft=false command

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
release-please-config.json Added "draft": true configuration to create releases as mutable drafts
.github/workflows/main.yml Replaced immutable release deletion logic with manual git tag creation for drafts and simplified publish step to edit draft releases

@WilliamBerryiii WilliamBerryiii merged commit c8eee58 into main Feb 14, 2026
24 checks passed
WilliamBerryiii pushed a commit that referenced this pull request Feb 14, 2026
🤖 I have created a release *beep* *boop*
---


##
[2.3.8](hve-core-v2.3.7...hve-core-v2.3.8)
(2026-02-14)


### 🐛 Bug Fixes

* **workflows:** use draft-first release flow to avoid immutability
errors ([#554](#554))
([c8eee58](c8eee58))

---
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>
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.

3 participants