fix(workflows): replace draft release config with post-creation draft conversion#545
Merged
WilliamBerryiii merged 1 commit intomainfrom Feb 13, 2026
Merged
Conversation
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #545 +/- ##
==========================================
- 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:
|
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates the release automation workflows to eliminate a release-please version anchoring race caused by draft releases not creating tags, and improves extension publish workflows by auto-deriving the version from the latest GitHub release when not provided.
Changes:
- Removed
draft: trueandforce-tag-creationfromrelease-please-config.jsonso release-please creates a tag immediately for version anchoring. - Simplified the “tag bridge” logic in
main.ymlby converting the created release to a draft post-creation usinggh release edit --draft=true. - Made extension publish workflows auto-detect the version from the latest GitHub release tag when the
versioninput is empty (with ODD-minor derivation for the prerelease workflow).
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| release-please-config.json | Removes draft/force-tag-creation settings so release-please creates visible tags for anchoring. |
| .github/workflows/main.yml | Replaces manual tag creation bridge with post-creation conversion of the release to draft for asset upload. |
| .github/workflows/extension-publish.yml | Adds version auto-detection from latest GitHub release tag when workflow input is empty. |
| .github/workflows/extension-publish-prerelease.yml | Makes version optional and auto-derives an ODD-minor prerelease version from the latest GitHub release. |
agreaves-ms
approved these changes
Feb 13, 2026
… conversion - remove draft:true from release-please-config.json to fix tag visibility race condition - replace tag bridge step with gh release edit --draft=true post-creation conversion - add auto-detect version from latest release tag in extension publish workflows - derive ODD minor version for pre-release channel automatically Fixes #543 🔧 - Generated by Copilot
3d932be to
8590251
Compare
WilliamBerryiii
pushed a commit
that referenced
this pull request
Feb 13, 2026
🤖 I have created a release *beep* *boop* --- ## [2.3.5](hve-core-v2.3.4...hve-core-v2.3.5) (2026-02-13) ### 🐛 Bug Fixes * **workflows:** replace draft release config with post-creation draft conversion ([#545](#545)) ([2311d04](2311d04)) --- 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>
28 tasks
WilliamBerryiii
added a commit
that referenced
this pull request
Feb 13, 2026
## Description Published releases on this repository are immutable — the `gh release edit --draft=true` step introduced in PR #545 fails with `HTTP 422: state cannot be changed when release is immutable`. This PR replaces the edit approach with a delete-and-recreate cycle: capture the release metadata, delete the immutable published release, and recreate it as a mutable draft so downstream jobs can upload assets. The git tag persists across the delete/create cycle, and the `publish-release` job converts the draft back to published after upload completes. **Why prior approaches failed:** - PR #538 (`"draft": true` in config) — release-please cannot find draft releases via the Releases API, causing it to scan full history and propose a bogus v3.0.0 - PR #545 (`gh release edit --draft=true`) — published releases are immutable on this repo (HTTP 422) ## Related Issue(s) Fixes #543 ## Type of Change Select all that apply: **Code & Documentation:** - [x] Bug fix (non-breaking change fixing an issue) - [ ] New feature (non-breaking change adding functionality) - [ ] Breaking change (fix or feature causing existing functionality to change) - [ ] Documentation update **Infrastructure & Configuration:** - [x] GitHub Actions workflow - [ ] Linting configuration (markdown, PowerShell, etc.) - [ ] Security configuration - [ ] DevContainer configuration - [ ] Dependency update **AI Artifacts:** - [ ] Reviewed contribution with `prompt-builder` agent and addressed all feedback - [ ] Copilot instructions (`.github/instructions/*.instructions.md`) - [ ] Copilot prompt (`.github/prompts/*.prompt.md`) - [ ] Copilot agent (`.github/agents/*.agent.md`) - [ ] Copilot skill (`.github/skills/*/SKILL.md`) **Other:** - [ ] Script/automation (`.ps1`, `.sh`, `.py`) - [ ] Other (please describe): ## Testing - Validated YAML syntax of `main.yml` with `yamllint` - Confirmed `gh release delete` preserves the git tag (documented GitHub CLI behavior) - Confirmed `gh release create --draft --verify-tag` recreates a mutable draft tied to the existing tag - Verified `--notes-file` approach safely handles release body content with special characters ## Checklist ### Required Checks - [x] Documentation is updated (if applicable) - [x] Files follow existing naming conventions - [x] Changes are backwards compatible (if applicable) - [ ] Tests added for new functionality (if applicable) ### Required Automated Checks The following validation commands must pass before merging: - [ ] Markdown linting: `npm run lint:md` - [ ] Spell checking: `npm run spell-check` - [ ] Frontmatter validation: `npm run lint:frontmatter` - [ ] Link validation: `npm run lint:md-links` - [ ] PowerShell analysis: `npm run lint:ps` ## Security Considerations - [x] This PR does not contain any sensitive or NDA information - [x] Any new dependencies have been reviewed for security issues - [x] Security-related scripts follow the principle of least privilege ## Additional Notes The delete-and-recreate approach is safe because: 1. **Tag preservation** — `gh release delete` only removes the release object; the git tag remains intact 2. **Timing** — release-please has already completed version anchoring before this step runs, so the brief absence of the release object has no effect 3. **Metadata fidelity** — release title and body are captured before deletion and reapplied to the new draft via `--notes-file` (avoids shell interpolation issues) 4. **Downstream compatibility** — all subsequent jobs reference `steps.release.outputs.tag_name`, which is unaffected by the recreate
7 tasks
WilliamBerryiii
added a commit
that referenced
this pull request
Feb 14, 2026
…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.
This was referenced Feb 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Fixes a race condition where release-please
"draft": trueconfiguration caused draft releases that are excluded from the GitHub Releases API "latest" query, making them invisible to release-please's own version anchoring within the same workflow invocation. Because the draft release is invisible, release-please scans the full commit history, finds an old breaking change (PR #277), and proposes erroneous v3.0.0 major version bumps. Also added auto-detection of version from the latest GitHub release tag in both extension publish workflows, removing the requirement for manual version input on every dispatch.Proof the race condition fires in production: after v2.3.4 merged, release-please created the v2.3.4 release as draft at 22:05:23 UTC. At 22:08:14 UTC — while v2.3.4 was still draft — release-please opened PR #547 proposing v3.0.0. The v2.3.4 release was not published until 22:10:08 UTC, confirming that release-please computed the next version while v2.3.4 was invisible to its own Releases API query.
Additionally, v2.3.2 remains stuck in draft state, demonstrating that the
publish-releasejob'sgh release edit --draft=falseis unreliable when it depends on upstream jobs that may not always run.Changes
"draft": truefrom root and package level inrelease-please-config.jsonso release-please creates published releases that are immediately visible to the Releases API for version anchoring"force-tag-creation": truefromrelease-please-config.jsonsince it requires release-please v17.2.0+ and was silently ignored by the bundled v17.1.3main.ymlwith agh release edit --draft=truepost-creation conversion, allowing assets to be uploaded to a mutable draft release while preserving release visibility for version anchoringextension-publish-prerelease.ymlwhen version input is empty, with ODD minor derivation for the pre-release channelextension-publish.ymlwhen version input is empty, withhve-core-vprefix strippingextension-publish.ymlinput description to match new auto-detect behavior (was incorrectly referencingpackage.json)Related Issue(s)
Fixes #543
Type of Change
Select all that apply:
Code & Documentation:
Infrastructure & Configuration:
AI Artifacts:
prompt-builderagent and addressed all feedback.github/instructions/*.instructions.md).github/prompts/*.prompt.md).github/agents/*.agent.md).github/skills/*/SKILL.md)Other:
.ps1,.sh,.py)Testing
npm run lint:yaml— all 25 workflow files passedrelease-please-config.jsonis valid JSON with no schema errorspublish-releasejob already containsgh release edit --draft=falseto finalize releases after asset uploadgh release view --json tagNamecorrectly returns the latest release tag format (hve-core-v2.3.4)Checklist
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
The release lifecycle after this change follows a four-phase sequence: release-please creates a published release (tag created, visible to Releases API) → post-creation step converts to draft (mutable for asset upload) → package/attest/upload jobs attach assets →
publish-releasejob converts back to published. This preserves the original HTTP 422 fix from PR #538 while eliminating the version anchoring race condition that caused PRs #530, #532, #534, #539, #540, #542, and #547.Why the tag bridge didn't work
The tag bridge step from PR #538 manually created git tags after draft release creation. However, release-please anchors its version calculation on the GitHub Releases API (not the Tags API). Even though tag
hve-core-v2.3.4existed at commit9a72f2b, release-please's query for the latest release excluded the draft, causing it to scan the full commit history and hit the old breaking change from PR #277.