Skip to content

fix(release): correct the changelog baseline and stop minting dev tags per merge (PRINFRA-507) - #287

Merged
somanshreddy merged 1 commit into
mainfrom
08-12-fix_release_notes_at_source
Aug 12, 2026
Merged

fix(release): correct the changelog baseline and stop minting dev tags per merge (PRINFRA-507)#287
somanshreddy merged 1 commit into
mainfrom
08-12-fix_release_notes_at_source

Conversation

@somanshreddy

Copy link
Copy Markdown
Collaborator

Scope

Surfaces: CLI | Module: Release process

Summary

v0.7.0 published release notes covering one commit instead of five. Device auth login, brand glossaries and the deprecation feature all shipped invisible to anyone reading the release page.

Root cause

Not GoReleaser being unhelpful. .goreleaser.yaml has no changelog: block, so the baseline defaults to the previous tag of any kind — and dev-release.yml minted a prerelease on every push to main:

prev tag before v0.7.0:  v0.6.1-dev.202608120553   (cut minutes earlier)
commits in that range:   1
commits since v0.6.0:    5

Five dev tags sat between the two stable releases. The changelog was correct for the range it was given; the range was wrong.

Fix

Dev releases become on-demand. The push trigger produced 78 prereleases against 22 stable, nearly all with zero downloads — the last four have zero between them. RELEASE.md has always documented dev builds as manually dispatched (gh workflow run dev-release.yml) and never mentioned the push trigger, so this makes the workflow match the doc rather than changing the contract. Dev builds stay one dispatch away, preserving the occasional real use and the heygen update dev channel.

GORELEASER_PREVIOUS_TAG pins the baseline to the previous stable tag, reusing the filter the pre-release checklist already relies on. This is belt and braces: it keeps the range right if a dev tag ever lands in between again, and it is the safety net for a release cut without an agent driving the checklist.

Post-release gains the missing step — publishing the changelog from step 7. Nothing does it automatically and nothing fails if it is skipped, so the doc now says that plainly instead of leaving notes to be written and thrown away.

Testing

Workflow changes, so verified by exercising the logic rather than by unit tests.

  • Ran the baseline resolution against the repo's real tags: cutting v0.7.0 resolves to v0.6.0 and yields the 5 commits that actually shipped, versus the 1 that was published. Cutting v0.8.0 resolves to v0.7.0.
  • Both workflow files parse as YAML (checked with a real parser, not by eye — a syntax error in release-stable.yml would not surface until the next release attempt).
  • make lint and the full suite green.

Deliberately not included

GoReleaser changelog: grouping. With the baseline fixed the fallback is already a correct commit list for the release, and the curated notes replace it anyway — grouping would only ever polish a path that gets overwritten.

Note for reviewers

One behavior change worth a conscious nod: anyone habitually pulling "the newest dev build" now has to ask for one. Download counts say nobody is, but that is inference from zeros, not proof.

…s per merge (PRINFRA-507)

v0.7.0 published notes covering one commit instead of five. The cause was not
GoReleaser: .goreleaser.yaml has no changelog block, so the baseline defaults to
the previous tag of any kind, and dev-release.yml minted a prerelease on every
push to main. The tag immediately before v0.7.0 was a dev build cut minutes
earlier, so the range was that one merge.

Three changes, smallest first:

Dev releases become on-demand. The push trigger produced 78 prereleases against
22 stable, nearly all with zero downloads, and RELEASE.md has always documented
dev builds as manually dispatched — the workflow simply did more than the doc
claimed. Dropping it removes the tag spam and the thing that corrupted the
baseline. Dev builds are still one `gh workflow run` away, so the occasional
real use and the `heygen update` dev channel both survive.

GORELEASER_PREVIOUS_TAG pins the baseline to the previous *stable* tag, using
the same filter the pre-release checklist already uses. Belt and braces: it
keeps the range correct even if a dev tag lands in between again, and it is the
safety net for a release cut without an agent driving the checklist. Verified
against the real tags: cutting v0.7.0 resolves to v0.6.0 and yields the five
commits that actually shipped.

Post-release gains the step that was missing entirely: publishing the changelog
generated in step 7. Nothing does it automatically and nothing fails if it is
skipped, so the doc now says so plainly rather than leaving the notes to be
written and discarded.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@somanshreddy

Copy link
Copy Markdown
Collaborator Author

Both review findings fixed:

  • Dev tag format in the docs was wrong — and the file contradicted itself: line 9 said {YYYYMMDD}.{shorthash}, the Format table said {YYYYMMDDHHmm}. The workflow mints %Y%m%d%H%M, so the table and the real tags were right and the bullet was stale. Fixed the bullet.
  • Empty GORELEASER_PREVIOUS_TAG on a first-ever release. Good catch that GoReleaser doesn't document whether an empty value means "unset" or "explicitly empty". Rather than find out during someone's first release, the step now exports the var through $GITHUB_ENV only when a previous stable tag exists; otherwise GoReleaser's own resolution is left untouched. Dropped the now-unused id: prev with it.

Unreachable in this repo (22 stable tags), but it's the kind of thing that only ever fires on a fresh fork, where nobody is watching for it.

@somanshreddy
somanshreddy force-pushed the 08-12-fix_release_notes_at_source branch from 3a27ece to 12559ab Compare August 12, 2026 07:12

@somanshreddy somanshreddy left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Endorse (COMMENTED — my own account; GH-APPROVE to @rames Jusso). Root cause + fix are correct. Dev-per-merge tags shadowed GoReleaser's changelog baseline (it baselines on the previous tag of any kind → a dev tag cut minutes before the stable release → notes cover one commit). The fix:

  • dev builds on-demand — dropped the push: [main] trigger; on-demand dispatch only.
  • baseline pinned to the previous stable tag — the new step resolves GORELEASER_PREVIOUS_TAG via git tag --list 'v*' | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | grep -vFx "$version" | head -n1. The regex correctly excludes -dev./prerelease tags, and -vFx "$version" excludes the current release, so the changelog range is the full release whoever cuts it. First-stable edge handled (leave the override unset, not empty — right call, since GoReleaser doesn't document empty-value semantics).
  • curated-notes post-release step now documented.

Hardest look: dropping per-merge dev prereleases is low-risk — 78 dev tags / ~zero downloads, and RELEASE.md already described dev as manually-dispatched (so the doc is genuinely unchanged on that point). "Inference from zeros, not proof" is a fair caveat; if you want proof the per-asset download counts are queryable via gh api repos/.../releases, but the zeros + the changelog-corruption cost make the tradeoff clearly right. Clean fix — and it closes the loop on the #284/#286 release-gate line (the surface diff catches what shipped; this makes the changelog say it).

@james-russo-rames-d-jusso james-russo-rames-d-jusso left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Layering on Somansh's self-endorsement (COMMENTED — leaving the stamp for OG). Diagnosis + fix land cleanly; three observations for later and one call-out that isn't in the diff.

What I verified

  • Author + CI. somanshreddy (human, co-authored claude). All required checks green.

  • Zero-download claim (empirical, not repeated). Enumerated the 4 most recent dev prereleases via gh api /repos/heygen-com/heygen-cli/releases --paginate:

    tag published asset downloads
    v0.6.1-dev.202608120628 2026-08-12 06:30Z 0
    v0.6.1-dev.202608120553 2026-08-12 05:55Z 0
    v0.6.1-dev.202608120550 2026-08-12 05:52Z 0
    v0.6.1-dev.202608112003 2026-08-11 20:05Z 0

    Total across the last four: 0. Claim verified. Worth naming that #5 back (v0.6.1-dev.202608050333) is 22 downloads and there are ~5–6 older dev tags in the 15–22 range — so "occasional real use" (your PR-body phrasing) is the accurate reading. The framing in the description already handles this honestly; no ask.

  • Baseline selector correctness.

    • ^v[0-9]+\.[0-9]+\.[0-9]+$ correctly excludes every -dev./-rc/-beta/-alpha prerelease — not just dev tags. Good.
    • --sort=-v:refname gives semver-aware ordering (v1.10.0 > v1.2.0).
    • grep -vFx "$version" fully-anchored + fixed-string: safe because Validate version already pins $version to ^v[0-9]+\.[0-9]+\.[0-9]+$ before this step runs, so no metacharacters can leak.
    • The "Create git tag" step pushes the new tag before Resolve runs, so the newly-created tag is in the local tag DB — self-exclusion via -vFx is what makes this work. Correct sequencing.
    • First-stable edge: leave GORELEASER_PREVIOUS_TAG unset (not empty) — right call given GoReleaser doesn't document empty-value semantics.
  • Sibling consistency. The exact same regex ^v[0-9]+\.[0-9]+\.[0-9]+$ is now used in three places: dev-release.yml compute step, release-stable.yml resolve step, and RELEASE.md's LAST_STABLE pre-release checklist. Consistent — this fix reuses the filter that was already there, not inventing a new pattern. Positive.

Observations (not blockers)

  1. v0.7.0 itself is not backfilled by this PR. The mechanism fix is forward-only: RELEASE.md gains a step to publish curated notes for future releases, but the public v0.7.0 release page still shows notes covering the one commit. If v0.7.0 matters as a discoverable release page (users landing on it from GitHub or heygen update --version docs), a one-shot gh release edit v0.7.0 --notes-file <curated> after this PR merges closes the loop. Otherwise the visible page stays misleading.

  2. No dry-run for baseline selection. The only way to test the resolve step today is to cut a real release. If a nightly-build workflow ever gets added that reintroduces per-push dev tags — or if the regex silently drifts — the same class of bug re-emerges silently. A workflow_dispatch input like dry_run: true that runs Resolve and echoes the chosen baseline (without invoking GoReleaser) would give this fix a keeper. Not this PR; noting for the punch list.

  3. -rc / -beta treatment is by design but worth naming. The regex excludes all prereleases. If the team ever adopts RC cuts (v0.8.0-rc.1v0.8.0), v0.8.0's baseline resolves to v0.7.0, not v0.8.0-rc.1 — the changelog will span the RC's contents plus anything added between RC and final. That's arguably the right default for "what shipped in this release," but it's a semantic choice that would surprise anyone who assumed RC-to-final diffing. Worth a comment on the resolve step if RC cuts are ever on the roadmap.

Nits

  • RELEASE.md tag-format update ({YYYYMMDD}.{shorthash}{YYYYMMDDHHmm}) is a straight correction — the compute step in dev-release.yml produces date -u +%Y%m%d%H%M with no shorthash, so the prior doc had drifted silently. The "doc always documented X" framing in the PR body is a bit generous on this specific line; the doc has been correct on the trigger semantics (manually dispatched) but stale on the format. Minor.

What I didn't verify

  • I didn't actually run the new resolve step against a real tag list in a dry-run harness — verified by reading the regex and cross-checking against the tag list from gh api /releases. If concerns above (2) is picked up as a follow-up, the same script becomes the verification.

Review by Rames D Jusso

@somanshreddy

Copy link
Copy Markdown
Collaborator Author

Thanks — three of four land, one is already done.

Obs 1 (v0.7.0 not backfilled) — already closed, before this PR. The curated notes went onto v0.7.0 with gh release edit --notes-file immediately after the cut; the release page has shown the full Deprecated/New/Improved/Internal writeup since then and the raw sha list is gone (grep for 8f86544b3ebf in the body returns 0). What this PR fixes is the next release needing that manual step at all. Worth flagging because the observation would otherwise send someone to redo work that's done.

Obs 2 (no dry-run for baseline selection) — agreed, and the sharpest of the three. Cutting a real release is currently the only way to exercise the resolve step, which is a bad property for the one piece of logic whose failure mode is silent. A dry_run input that runs Resolve and echoes the baseline without invoking GoReleaser would give it a keeper. Capturing it as a follow-up rather than widening this PR.

Obs 3 (-rc/-beta excluded) — by design, and you're right that it deserves saying out loud. With RC cuts, v0.8.0 would baseline on v0.7.0, so the changelog spans the RC contents plus anything added after — which is what you want for "what shipped in this release", but only if you know that's the choice. No RC cuts on the roadmap today; if that changes, the comment goes on the resolve step.

Nit — fair, and my PR body was loose. "The doc always documented this" is true of the trigger (manually dispatched, never mentioned the push trigger) and not of the format, which had drifted to {YYYYMMDD}.{shorthash} while the workflow emitted %Y%m%d%H%M. Two different things and I ran them together. The format line was a straight stale-doc correction, not a doc-matches-reality alignment.

No code changes from this round.

@jrusso1020 jrusso1020 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approving. Verified at 12559ab9: all six required contexts green (test on ubuntu / macos / windows, lint, secrets, goreleaser-check). No CODEOWNERS in this repo, so REVIEW_REQUIRED here was the missing approval rather than a missing owner.

The baseline resolver is correct where it counts. I ran its pipeline against a synthetic tag list and it picks v0.7.0 out of v0.8.0 / v0.7.0 / v0.7.0-dev.202608120628 / v0.6.1. Two things are quietly load-bearing and both hold: grep -vFx "$version" matters because Create git tag pushes the current tag two steps earlier, so it is already in git tag --list by the time this runs; and fetch-depth: 0 on the checkout is what makes that list complete, since a default shallow fetch would leave only the tag just created.

One nit, not a blocker: the first-stable branch cannot execute. Under set -euo pipefail, a failing grep inside a command-substitution assignment aborts the step, so when nothing matches, the step dies at the assignment and never reaches if [ -z "$prev" ]. Confirmed in all three shapes:

  • no stable tags at all: exit 1, branch never reached
  • only stable tag is the current version: exit 1, branch never reached
  • normal case: prev=v0.7.0, exit 0

Unreachable in this repo (26 stable releases exist), and the failure mode would be a loud step failure at release time rather than a silently wrong changelog, so it does not change the verdict. It matters if these workflows get copied to a new repo, which is the usual fate of release workflows: the first release there would die on a bare grep exit instead of printing the message the comment promises. One-line fix if you want the branch to do what it says: ... | head -n 1 || true)".

Smaller: the dev-release.yml comment's counts read 78 ... against 22 stable releases, and today the repo has 109 prereleases against 26 stable releases. Same direction, and the argument is stronger with the real numbers, but it is a permanent comment so worth correcting while it is cheap.

Doc change is accurate: the dev tags actually minted are YYYYMMDDHHmm (e.g. v0.6.1-dev.202608120628), so RELEASE.md had been documenting a {YYYYMMDD}.{shorthash} format that is not what ships. Publishing the curated notes as an explicit post-release step is the right call given nothing fails when it is skipped.

— Rames Jusso

@somanshreddy
somanshreddy merged commit b6e6b02 into main Aug 12, 2026
9 checks passed
@somanshreddy
somanshreddy deleted the 08-12-fix_release_notes_at_source branch August 12, 2026 18:51
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