Skip to content

feat(release): semantic-release versioning + self-update v-prefix fix (spec 25) - #79

Merged
gustavobertoi merged 3 commits into
mainfrom
release-automation
Jun 30, 2026
Merged

feat(release): semantic-release versioning + self-update v-prefix fix (spec 25)#79
gustavobertoi merged 3 commits into
mainfrom
release-automation

Conversation

@gustavobertoi

@gustavobertoi gustavobertoi commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Ships the release-automation thin slice (spec 25 / FEATURES #17 — the v0.2.0 gate) and lands the four new feature specs 22–25 designed in this session.

How releases work (single workflow, no token)

One release.yml does everything with the built-in GITHUB_TOKENno PAT/App token to manage:

  • push to mainsvu next --v0 → 0.x guard → (gated on the RELEASE_ENABLED repo variable) git tag + goreleaser release in the same job.
  • push a v* tag by hand → skips svu, runs goreleaser directly (manual release, ungated).
  • workflow_dispatch → manual trigger of the automated path.

Why one job: contents: write lets a workflow push a tag, but a tag pushed with GITHUB_TOKEN does not re-trigger another workflow (GitHub's recursion guard). Folding compute + release into one job is what removes the need for a separate token. The old two-workflow tag.yml + RELEASE_TOKEN approach was dropped.

Kill-switch: the RELEASE_ENABLED repo variable (default off = compute + log, never release). No secret to rotate.

Pieces

  • .svu.yaml v0:true — BETA stays 0.x (BREAKING → minor, never 1.0.0).
  • .github/workflows/release.yml — rewritten single-job pipeline (above), svu pinned v3.4.1, CI v0.* guard.
  • .github/workflows/pr-title.yml — pure-shell conventional-commit PR-title lint.
  • .goreleaser.yaml — grouped changelog (Features / Bug fixes / Performance) + the ldflags v-prefix fix.
  • Specs 22–25 (docs) — init wizard · template/Dockerfile authoring · .env ingestion · release automation; all TUIs are Bubble Tea v2 + Charm (bubbles/lipgloss/huh v2). Wired into FEATURES (feat(secrets): S1 — secret:// core (parser, Provider iface, registry, batched Resolve) #14–17) + a ROADMAP M8 beta-DX lane (stays 0.x).

The load-bearing fix

goreleaser's {{.Version}} is the tag with the v stripped (0.2.0). x/mod/semver requires the leading v, so internal/selfupdate.IsDevBuild() (returns !semver.IsValid(v)) treats a real release as a dev build → the v0.1.0 update notifier + self update are silently dead. Fix: stamp Version=v{{ .Version }} (archive name_template stays v-stripped, matching assetName's TrimPrefix).

Verified against the real code:

Version="0.2.0"              IsValid=false  IsDevBuild=true   # old stamp -> no self-update
Version="v0.2.0"             IsValid=true   IsDevBuild=false  # the fix
Version="v0.1.1-dev-abc1234" IsValid=true   IsDevBuild=false  # snapshot, valid

goreleaser check passes; a snapshot build now stamps v0.1.1-dev-<sha>.

⚠️ Owner action to cut v0.2.0 (not done here)

Set the kill-switch on, then trigger:

gh variable set RELEASE_ENABLED --body true
# then merge a feat: PR / run the Release workflow, or: git tag v0.2.0 && git push origin v0.2.0

svu next --v0 computes v0.2.0 from the M2–M7 history. Only ever push v* semver tags.

Specs 22–24 are design-only; their implementation is the M8 lane (build order: this PR first, then init → template → env-ingest).

🤖 Generated with Claude Code

gustavobertoi and others added 3 commits June 30, 2026 14:49
Design the four newly requested features as spec-quality docs:
- 22 interactive `init` wizard (workspace + shared services)
- 23 interactive template & Dockerfile authoring (TUI)
- 24 `.env` ingestion -> secrets/vars (no more committed .env)
- 25 release automation + 0.x conventional-commit versioning

All TUIs are Bubble Tea v2 + the Charm plugin stack (bubbles/lipgloss/huh v2,
CGO-free) behind one shared internal/prompt theme, each with a --json/flag
fallback. Wire them into FEATURES (#14-17) and a new ROADMAP M8 beta-DX lane
that stays on the 0.x line (next release v0.2.0, never an automated 1.0.0).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… v-prefix bug

Implements spec 25's thin slice (the v0.2.0 gate):
- .svu.yaml (v0:true): keeps BETA on the 0.x line (a BREAKING change bumps the
  MINOR, never 1.0.0).
- .github/workflows/tag.yml: push-to-main -> `svu next --v0` (pinned v3.4.1) ->
  push a v* tag, gated on an owner-provisioned RELEASE_TOKEN (absent = kill
  switch) with a CI v0.* guard, which fires the unchanged release.yml/goreleaser.
- .github/workflows/pr-title.yml: pure-shell conventional-commit PR-title lint
  (squash-merge makes the PR title the commit svu reads).
- .goreleaser.yaml: grouped changelog (Features/Bug fixes/Performance) under
  `use: github`, and the load-bearing ldflags fix Version=v{{.Version}}.

The ldflags fix is a real regression, not cosmetic: goreleaser's {{.Version}} is
v-stripped ("0.2.0"), which golang.org/x/mod/semver rejects, so internal/
selfupdate.IsDevBuild() returns true for a real release -> the update notifier
and `self update` silently treat it as a dev build and never fire. Verified
against the real code: IsDevBuild("0.2.0")=true vs IsDevBuild("v0.2.0")=false;
goreleaser snapshot now stamps v0.1.1-dev-<sha>.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per owner decision, drop the two-workflow + RELEASE_TOKEN (PAT/App token) split
in favor of ONE release.yml that computes -> tags -> releases in a single job
using the built-in GITHUB_TOKEN. Rationale: contents:write lets a workflow push
a tag, but a tag pushed with GITHUB_TOKEN does NOT re-trigger another workflow
(GitHub's recursion guard) -- so the only way to avoid a separate token is to run
tag-compute and goreleaser in the same job, never depending on a re-trigger.

- Remove .github/workflows/tag.yml.
- Rewrite .github/workflows/release.yml: triggers on push:main (svu compute +
  0.x guard + tag, gated on the RELEASE_ENABLED repo *variable*) AND on push of a
  v* tag (a human hand-cut release) AND workflow_dispatch; goreleaser runs once,
  guarded by github.ref so the two paths never double-release.
- Kill-switch is now the RELEASE_ENABLED repo variable (no secret to rotate);
  default unset = compute + log, never release.
- Update spec 25 / FEATURES #17 / ROADMAP M8.0 to the no-token design.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gustavobertoi
gustavobertoi merged commit 01fa063 into main Jun 30, 2026
4 of 5 checks passed
@gustavobertoi
gustavobertoi deleted the release-automation branch June 30, 2026 19:38
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.

1 participant