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
Contributor

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

4 participants