fix: create GitHub release against the v-prefixed tag#430
Conversation
The shared github-release workflow used a single version value for both the release name and the git tag_name. This repo passed the bare version (e.g. 3.9.1), so createRelease targeted a non-existent "3.9.1" tag and would create a stray tag off the default branch instead of using the real "v3.9.1" tag. Replace the shared-workflow call in both publish-svn workflows with an inline github-script job that tags the release with the v-prefixed ref (github.ref_name) while keeping the bare version as the release title, preserves the merged-release- PR notes behavior, and skips if a release for the tag already exists.
|
@claude review once |
There was a problem hiding this comment.
LGTM — well-scoped fix that correctly distinguishes the v-prefixed tag from the bare version, with idempotency guard. Inline comments cover nit-level hardening for the release-PR body extraction.
Extended reasoning...
Overview
Both publish workflows (publish-svn.yml for v3, publish-svn-v2.yml for v2) replace the shared OneSignal/sdk-shared/.github/workflows/github-release.yml call with an inline actions/github-script job. The new job uses github.ref_name for tag_name (the actual v-prefixed tag) while keeping the bare version for the release name, which matches the existing release history pattern. It also adds an idempotency guard via getReleaseByTag (404 → proceed, success → skip) so re-runs don't fail.
Security risks
None. Changes are confined to .github/workflows/ files. The script reads PR metadata and creates a release on the same repo using either the existing GH_PUSH_TOKEN secret or the default github.token, with explicit contents: write and pull-requests: read job-level permissions (no escalation). The version/tag values flow through env: rather than direct ${{ }} interpolation into the script body, which avoids the standard github-script injection pitfall.
Level of scrutiny
Low. This is CI-only, doesn't touch the plugin code or the SVN publish jobs, and the v2 + v3 changes are mechanically identical. The fix is narrow (release tag_name was bare instead of v-prefixed) and the rationale — releases were getting created against synthetic bare tags off the default branch — is well-documented in the PR description and matches the visible symptom (the 3.9.1 release had to be created manually).
Other factors
The bug hunter surfaced three nit-level edge cases in the PR-body extraction (startsWith prefix collision on patch versions, null PR body throwing on .split, and the 50-PR pagination cap with silent empty-notes fallback) — all real but none fire in the normal flow, and the inline comments already provide concrete one-line fixes if the author wants to harden further. The PR author has already smoke-tested the script body with mocked github/context/core objects, and the YAML parses. Reviewer sherwinski explicitly tagged me to review.
Address review feedback on the release-PR-body extraction: - look up the release PR by the tag's commit SHA via listPullRequestsAssociatedWithCommit instead of paging closed PRs, removing both the 50-PR window limit and the title prefix-collision (3.9.1 vs 3.9.10) - require an exact "Release <version>" title match, falling back to the first merged associated PR - guard against a null PR body (was a potential TypeError that would fail the job) - emit a warning when no release PR is found instead of silently publishing empty notes
|
@claude review once |
There was a problem hiding this comment.
LGTM — all three prior findings addressed cleanly in eeae703; the remaining inline note is a nit on the prerelease regex.
Extended reasoning...
Overview
PR replaces a shared-workflow github-release job in both publish-svn.yml (v3) and publish-svn-v2.yml (v2) with an inline actions/github-script step. The fix is to tag the GitHub Release with the actual v-prefixed ref (github.ref_name) while keeping the bare version as the release title — restoring consistency with prior releases (v3.9.0 tag / 3.9.0 title).
Security risks
None of note. The script runs with contents: write and pull-requests: read, scoped to the minimum needed for createRelease and the PR-body lookup. Token falls back to github.token when GH_PUSH_TOKEN is unset. No untrusted input is shelled out; PR title/body are only read.
Level of scrutiny
Moderate. This is release-pipeline plumbing — failures are recoverable (manually create the release), but silent wrong-content (e.g. notes from the wrong PR) would be embarrassing. The hardening work in the follow-up commit (SHA-keyed PR lookup, null body guard, exact title match, core.warning on miss) addresses the realistic failure modes; the script body was exercised against mocked octokit covering exact-match-over-collision, null body, no-PR, fallback, and existing-release-skip paths per the author's note.
Other factors
- All three of my prior findings (
startsWithcollision,nullbodyTypeError, 50-PR pagination cap) are addressed in eeae703 — the SHA-keyedlistPullRequestsAssociatedWithCommitis the right structural fix and obviates the pagination concern entirely. - The remaining inline note (
prerelease: /alpha|beta/i.test(version)missingrc/pre/dev/snapshot) is a true nit: only fires for a manually-pushed non-X.Y.Zv-tag, sincetag-on-release-merge.ymlonly auto-tags strictX.Y.Z, and the wrong-flag release is trivially editable. - Idempotency guard (
getReleaseByTag→ 404 → create) is solid; non-404 errors propagate. - No production code or security-sensitive paths touched.
| name: version, | ||
| body, | ||
| draft: false, | ||
| prerelease: /alpha|beta/i.test(version), |
There was a problem hiding this comment.
🟡 ⚪ Nit (same code lives in publish-svn-v2.yml:174): prerelease: /alpha|beta/i.test(version) only flags alpha and beta substrings, so a manually-pushed tag like v3.9.1-rc.1 (or -pre/-dev/-next/-snapshot) would match the v3.* trigger and be published as a stable GitHub Release. Today tag-on-release-merge.yml only auto-tags strict X.Y.Z, so this only bites a manually-pushed prerelease tag — but broadening to /-(alpha|beta|rc|pre|dev|snapshot)/i is a one-liner while this code is being touched.
Extended reasoning...
What the bug is
In both publish-svn.yml:145 and publish-svn-v2.yml:174, the GitHub Release is created with:
prerelease: /alpha|beta/i.test(version),That regex only recognises alpha and beta as prerelease markers. The other conventional identifiers — rc (release candidate, by far the most common after alpha/beta), pre, dev, next, snapshot — are not matched.
Concrete trigger
Step by step for a hypothetical v3.9.1-rc.1:
- A maintainer pushes
v3.9.1-rc.1. It matches the workflow triggertags: 'v3.*'and runspublish-svn.yml. - The 'Strip v prefix from tag' step sets
bare_version = '3.9.1-rc.1'. - The
github-releasejob runs the inlinegithub-script./alpha|beta/i.test('3.9.1-rc.1')evaluates tofalse. createReleaseis called withprerelease: false— published as a stable GitHub Release.
Why existing guards don't catch it
tag-on-release-merge.yml:34 enforces ^chore: Release ([0-9]+\.[0-9]+\.[0-9]+)( \(#[0-9]+\))?$ on the commit subject before creating the v-tag, so the auto-tag flow only produces strict X.Y.Z. The only path to a prerelease v-tag is a maintainer manually pushing one — which is also the same path the PR description acknowledges was used for the 3.9.1 recovery.
Addressing the refutation
One verifier refuted this as a duplicate of a previously-refuted bug and as implausible because (a) the repo's auto-tag flow can't produce prerelease tags and (b) WP.org SVN doesn't have a prerelease concept. Both points are correct in fact but don't dispose of the issue: the prerelease: field clearly exists in the code with the intent of flagging prereleases — it just undercovers the conventional identifier set. The 'SVN trunk publishes a -rc tag as stable anyway' point is a separate, larger problem; it doesn't argue against fixing this line. And the manual-prerelease scenario, while rare, is exactly the kind of edge case that this PR exists to make self-healing.
Impact
Low — only fires when a maintainer manually pushes a non-X.Y.Z v-tag. The wrong-flag GitHub Release is also recoverable (edit the release, flip the checkbox). Hence nit, not normal.
Fix
Broaden the test to cover the standard set:
prerelease: /-(alpha|beta|rc|pre|dev|snapshot)/i.test(version),or, if the intent is 'any SemVer prerelease', just /-/.test(version) since bare X.Y.Z never contains a hyphen. Apply in both files (publish-svn.yml:145 and `publish-svn-v2.yml:174").
One Line Summary
Fix the GitHub Release step so it uses the real
v-prefixed git tag instead of creating a stray bare-version tag.Motivation
The publish workflows delegated GitHub Release creation to the shared
OneSignal/sdk-shared/.github/workflows/github-release.yml, which uses a singleversionvalue for both the releasenameand the gittag_name:This repo passed the bare version (e.g.
3.9.1, with thevstripped), but our tags are v-prefixed (v3.9.1). SocreateRelease({ tag_name: "3.9.1" })would target a tag that doesn't exist and create a brand-new lightweight3.9.1tag off the default branch — inconsistent with thev3.9.1convention and disconnected from the published commit. The shared workflow assumestag == name == version(bare tags), which doesn't hold for this repo.This is also why the 3.9.1 GitHub Release had to be created manually after the release pipeline ran.
Scope
github-releasejob in bothpublish-svn.yml(v3) andpublish-svn-v2.yml(v2) with an inlineactions/github-scriptjob.github.ref_name(the actualv-prefixed tag that triggered the publish) and titled with the bare version, matching existing releases (v3.9.0tag /3.9.0title).Release <version>PR body.Testing
github-scriptbody passesnode --checkand runs end-to-end against mockedgithub/context/coreobjects.Notes
github-release.ymlfor these two workflows because that workflow cannot express atag_namethat differs from the releasename. An alternative would be to add an optionaltag_nameinput to the shared workflow; that's a broader, cross-SDK change and can be done separately if preferred.GH_PUSH_TOKENorg secret and is being addressed independently.Checklist
github-scriptbody syntax-checked and smoke-tested