From 2bea8fb3829982e79457ce6ec105979c40c4ae0b Mon Sep 17 00:00:00 2001 From: Marcin Tojek Date: Wed, 15 Jul 2026 12:51:44 +0200 Subject: [PATCH] fix(scripts/releaser/v1): remove doubled "v" in release calendar latest release link (#27260) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem The interactive releaser (`scripts/releaser`) renders the "Latest Release" cell of the release calendar with a doubled version prefix, e.g. `[vv2.35.0](.../tag/v2.35.0)`. `version.String()` already returns a `v`-prefixed string (e.g. `v2.35.0`), but `updateCalendar` wrapped it in a `"[v%s]"` template, so the link label gained a second `v`. The tag URL was already correct because release tags carry the `v` prefix. ## Fix Drop the extra `v` from the label template (`"[v%s]"` → `"[%s]"`). The URL is unchanged. - Label before: `[vv2.35.0]` - Label after: `[v2.35.0]` ## Test Added `scripts/releaser/v1/docs_test.go`: - `TestUpdateCalendarLatestReleaseVersionPrefix` asserts the `LatestRelease` cell for a matching row on both a patch and a minor release. It fails on the old code (`[vv2.35.x]`) and passes with the fix. - `TestUpdateCalendarNotReleasedRowName` covers the `Not Released` → `Mainline` promotion and the major.minor "Release name" link (patch omitted).
Investigation notes - Entry path: `scripts/release.sh` → `go run ./scripts/releaser --legacy` → `runRelease` → `promptAndUpdateDocs` → `updateReleaseDocs` → `updateCalendarFile` → `updateCalendar` (`scripts/releaser/v1/docs.go`). - Root cause in `updateCalendar`: `fmt.Sprintf("[v%s](%s)", newVer.String(), ...)` combined with `version.String()` returning `v%d.%d.%d`. - Only the link label was affected; the `releaseTagURLFmt` URL was correct because tags are `v`-prefixed. - The standalone `scripts/update-release-calendar.sh` is a separate implementation and is not affected (it strips the `v` before re-adding one). - Companion PR for `release/2.35` (file `scripts/releaser/docs.go`): #27259.
--- This PR was generated by Coder Agents. --- scripts/releaser/v1/docs.go | 2 +- scripts/releaser/v1/docs_test.go | 85 ++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 scripts/releaser/v1/docs_test.go diff --git a/scripts/releaser/v1/docs.go b/scripts/releaser/v1/docs.go index 19e59e7695..359a9fb117 100644 --- a/scripts/releaser/v1/docs.go +++ b/scripts/releaser/v1/docs.go @@ -171,7 +171,7 @@ func updateCalendar( for i, r := range rows { if r.Major == newVer.Major && r.Minor == newVer.Minor { rows[i].LatestRelease = fmt.Sprintf( - "[v%s](%s)", + "[%s](%s)", newVer.String(), fmt.Sprintf(releaseTagURLFmt, newVer.String()), ) diff --git a/scripts/releaser/v1/docs_test.go b/scripts/releaser/v1/docs_test.go new file mode 100644 index 0000000000..052b07169d --- /dev/null +++ b/scripts/releaser/v1/docs_test.go @@ -0,0 +1,85 @@ +package v1 //nolint:testpackage // Tests unexported release helpers. + +import "testing" + +// TestUpdateCalendarLatestReleaseVersionPrefix checks the formatting of the +// "Latest Release" cell produced by updateCalendar. version.String() already +// includes a leading "v", so the link label must not add a second one, while +// the release tag URL keeps the "v" because tags are prefixed with it. +func TestUpdateCalendarLatestReleaseVersionPrefix(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + newVer version + channel string + want string + }{ + { + name: "patch release", + newVer: version{Major: 2, Minor: 35, Patch: 1}, + channel: "mainline", + want: "[v2.35.1](https://github.com/coder/coder/releases/tag/v2.35.1)", + }, + { + name: "minor release", + newVer: version{Major: 2, Minor: 35, Patch: 0}, + channel: "mainline", + want: "[v2.35.0](https://github.com/coder/coder/releases/tag/v2.35.0)", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + rows := []calendarRow{{ + ReleaseName: "2.35", + Major: 2, + Minor: 35, + ReleaseDate: "February 03, 2026", + Status: "Mainline", + LatestRelease: "N/A", + }} + + got := updateCalendar(rows, tt.newVer, tt.channel) + + var cell string + for _, r := range got { + if r.Major == tt.newVer.Major && r.Minor == tt.newVer.Minor { + cell = r.LatestRelease + break + } + } + + if cell != tt.want { + t.Fatalf("LatestRelease = %q, want %q", cell, tt.want) + } + }) + } +} + +// TestUpdateCalendarNotReleasedRowName checks that a "Not Released" row is +// promoted to "Mainline" with a major.minor "Release name" link (patch +// omitted) once its minor version is released. +func TestUpdateCalendarNotReleasedRowName(t *testing.T) { + t.Parallel() + + rows := []calendarRow{{ + ReleaseName: "2.36", + Major: 2, + Minor: 36, + Status: "Not Released", + LatestRelease: "N/A", + }} + + got := updateCalendar(rows, version{Major: 2, Minor: 36, Patch: 0}, "mainline") + + if got[0].Status != "Mainline" { + t.Errorf("Status = %q, want %q", got[0].Status, "Mainline") + } + const want = "[2.36](https://coder.com/changelog/coder-2-36)" + if got[0].ReleaseName != want { + t.Fatalf("ReleaseName = %q, want %q", got[0].ReleaseName, want) + } +}