Skip to content

fix(workflows): add manual tag creation for draft releases until release-please-action updates#538

Merged
agreaves-ms merged 2 commits intomainfrom
fix/release-please-force-tag-workaround
Feb 13, 2026
Merged

fix(workflows): add manual tag creation for draft releases until release-please-action updates#538
agreaves-ms merged 2 commits intomainfrom
fix/release-please-force-tag-workaround

Conversation

@agreaves-ms
Copy link
Contributor

@agreaves-ms agreaves-ms commented Feb 13, 2026

fix(workflows): add draft release with manual tag bridge + markdown-it ReDoS patch

Description

Draft Release with Manual Tag Bridge

GitHub does not create git tags for draft releases, but release-please needs the tag to anchor version calculations for subsequent release PRs. Without it, release-please scans the full commit history, may find an old breaking change, and proposes a wrong major bump (evidenced by bogus v3.0.0 PRs #530, #532, #534, #539).

The release-please-action v4.4.0 bundles release-please v17.1.3, which does not support force-tag-creation (introduced in v17.2.0). This PR adds a temporary bridge step that creates the tag via the GitHub API immediately after a draft release is created.

Changes:

  • Set draft: true in release-please-config.json so releases are mutable (fixes HTTP 422 on asset upload)
  • Add manual tag creation step to .github/workflows/main.yml using gh api to push a lightweight tag
  • Add robust error handling: detects duplicate tags specifically, fails on other errors
  • Add contents: write permission for the tag creation API call

Remove the manual tag-creation step once release-please-action ships with release-please >= 17.2.0 and supports force-tag-creation natively. See: googleapis/release-please#2423

markdown-it ReDoS Remediation (GHSA-38c4-r59v-3vqw)

markdownlint-cli2@0.20.0 depends on markdown-it@14.1.0, which has a moderate ReDoS vulnerability (CVE-2026-2327) in the linkify regex. No upstream fix is available yet.

Changes:

  • Add markdown-it: 14.1.1 to overrides in package.json (follows existing undici override pattern)
  • Patch-level bump with a single regex fix — no API surface changes

Validation: npm audit reports 0 vulnerabilities, npm run lint:md passes with 0 errors on all 153 files.

Related Issue(s)

Type of Change

Select all that apply:

Code & Documentation:

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

  • 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)

Note for AI Artifact Contributors:

  • Agents: Research, indexing/referencing other project (using standard VS Code GitHub Copilot/MCP tools), planning, and general implementation agents likely already exist. Review .github/agents/ before creating new ones.
  • Skills: Must include both bash and PowerShell scripts. See Skills.
  • Model Versions: Only contributions targeting the latest Anthropic and OpenAI models will be accepted. Older model versions (e.g., GPT-3.5, Claude 3) will be rejected.
  • See Agents Not Accepted and Model Version Requirements.

Other:

  • Script/automation (.ps1, .sh, .py)
  • Other (please describe):

Testing

  • npm audit — 0 vulnerabilities
  • npm run lint:md — 0 errors, 153 files linted
  • Tag creation step tested via CI on prior runs

Checklist

Required Checks

  • Documentation is updated (if applicable)
  • Files follow existing naming conventions
  • Changes are backwards compatible (if applicable)
  • Tests added for new functionality (if applicable)

AI Artifact Contributions

  • Used /prompt-analyze to review contribution
  • Addressed all feedback from prompt-builder review
  • Verified contribution follows common standards and type-specific requirements

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

  • This PR does not contain any sensitive or NDA information
  • Any new dependencies have been reviewed for security issues
  • Security-related scripts follow the principle of least privilege

Additional Notes

Two independent fixes in this PR:

  1. Draft release bridge — temporary workaround until release-please-action upgrades past v17.2.0
  2. markdown-it override — temporary until markdownlint-cli2 bumps its markdown-it dependency to >=14.1.1

🔧 - Generated by Copilot

@agreaves-ms agreaves-ms requested a review from a team as a code owner February 13, 2026 20:09
Copilot AI review requested due to automatic review settings February 13, 2026 20:09
@github-actions
Copy link

github-actions bot commented Feb 13, 2026

Dependency Review

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

OpenSSF Scorecard

PackageVersionScoreDetails
npm/markdown-it 14.1.1 🟢 6.5
Details
CheckScoreReason
Maintained🟢 103 commit(s) out of 30 and 13 issue activity out of 30 found in the last 90 days -- score normalized to 10
Code-Review🟢 3GitHub code reviews found for 10 commits out of the last 30 -- score normalized to 3
CII-Best-Practices⚠️ 0no badge detected
Vulnerabilities🟢 10no vulnerabilities detected
Security-Policy🟢 10security policy file detected
Token-Permissions⚠️ 0non read-only tokens detected in GitHub workflows
Dependency-Update-Tool🟢 10update tool detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Packaging⚠️ -1no published package detected
License🟢 10license file detected
Pinned-Dependencies🟢 7dependency not pinned by hash detected -- score normalized to 7
Binary-Artifacts🟢 10no binaries found in the repo
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
Fuzzing⚠️ 0project is not fuzzed

Scanned Files

  • package-lock.json

@codecov-commenter
Copy link

codecov-commenter commented Feb 13, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.34%. Comparing base (fa116ea) to head (15b8764).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #538      +/-   ##
==========================================
- 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 adds a temporary workaround to manually create Git tags for draft releases generated by release-please. The change is needed because the current version of release-please-action (v4.4.0) does not yet bundle release-please >= 17.2.0, which would support the force-tag-creation configuration option natively. The workaround uses the GitHub API via gh api to create a lightweight tag immediately after release-please creates a draft release.

Changes:

  • Added a new workflow step that creates tags for draft releases using the GitHub API
  • Implemented error suppression to handle re-runs where the tag already exists

Copy link

@nguyena2 nguyena2 left a comment

Choose a reason for hiding this comment

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

Looks like the release went through so this can be closed

@agreaves-ms
Copy link
Contributor Author

Looks like the release went through so this can be closed

No I forced it to go through so I could get the minor version number incremented @nguyena2

- set draft: true in release-please-config.json for mutable uploads
- add manual tag creation step with version-anchoring for release-please
- improve error handling with specific duplicate-tag detection
- add contents: write permission for tag creation API call

🔧 - Generated by Copilot
@WilliamBerryiii WilliamBerryiii force-pushed the fix/release-please-force-tag-workaround branch from 4410e0e to 9f8ff2d Compare February 13, 2026 20:56
- add markdown-it 14.1.1 override to resolve GHSA-38c4-r59v-3vqw
- patch fixes ReDoS in linkify.mjs regex (CVE-2026-2327)
- follows existing undici override pattern for transitive vulns

🔒 - Generated by Copilot
Copilot AI review requested due to automatic review settings February 13, 2026 21:06
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

Copilot reviewed 3 out of 4 changed files in this pull request and generated 3 comments.

@agreaves-ms agreaves-ms merged commit 4a6ef2c into main Feb 13, 2026
24 checks passed
@agreaves-ms agreaves-ms deleted the fix/release-please-force-tag-workaround branch February 13, 2026 21:13
WilliamBerryiii pushed a commit that referenced this pull request Feb 13, 2026
🤖 I have created a release *beep* *boop*
---


##
[2.3.3](hve-core-v2.3.2...hve-core-v2.3.3)
(2026-02-13)


### 🐛 Bug Fixes

* **workflows:** add manual tag creation for draft releases until
release-please-action updates
([#538](#538))
([4a6ef2c](4a6ef2c))

---
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>
WilliamBerryiii added a commit that referenced this pull request Feb 13, 2026
… conversion (#545)

## Description

Fixes a race condition where release-please `"draft": true`
configuration 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-release` job's `gh release edit --draft=false` is
unreliable when it depends on upstream jobs that may not always run.

### Changes

- fix(workflows): removed `"draft": true` from root and package level in
`release-please-config.json` so release-please creates published
releases that are immediately visible to the Releases API for version
anchoring
- fix(workflows): removed `"force-tag-creation": true` from
`release-please-config.json` since it requires release-please v17.2.0+
and was silently ignored by the bundled v17.1.3
- fix(workflows): replaced 20-line tag bridge step in `main.yml` with a
`gh release edit --draft=true` post-creation conversion, allowing assets
to be uploaded to a mutable draft release while preserving release
visibility for version anchoring
- feat(workflows): added auto-detection of version from latest GitHub
release tag in `extension-publish-prerelease.yml` when version input is
empty, with ODD minor derivation for the pre-release channel
- feat(workflows): added auto-detection of version from latest GitHub
release tag in `extension-publish.yml` when version input is empty, with
`hve-core-v` prefix stripping
- fix(workflows): updated `extension-publish.yml` input description to
match new auto-detect behavior (was incorrectly referencing
`package.json`)

## 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 linting passes via `npm run lint:yaml` — all 25
workflow files passed
- Verified `release-please-config.json` is valid JSON with no schema
errors
- Confirmed the `publish-release` job already contains `gh release edit
--draft=false` to finalize releases after asset upload
- Confirmed `gh release view --json tagName` correctly returns the
latest release tag format (`hve-core-v2.3.4`)

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

- [x] 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
- [ ] Any new dependencies have been reviewed for security issues
- [x] Security-related scripts follow the principle of least privilege

## 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-release` job 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.4` existed at commit `9a72f2b`,
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.
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
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.
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.

4 participants