diff --git a/scripts/releaser/release.go b/scripts/releaser/release.go index 14c5236bae..8c63ff7c46 100644 --- a/scripts/releaser/release.go +++ b/scripts/releaser/release.go @@ -631,9 +631,15 @@ func runRelease(ctx context.Context, inv *serpent.Invocation, executor ReleaseEx fmt.Fprintln(¬es, "> [!NOTE]") fmt.Fprintln(¬es, "> This is a **release candidate** (RC) for testing purposes. It is not recommended for production use. Please report any issues you encounter. Learn more about our [Release Schedule](https://coder.com/docs/install/releases).") case "mainline": - fmt.Fprintln(¬es) - fmt.Fprintln(¬es, "> [!NOTE]") - fmt.Fprintln(¬es, "> This is a mainline Coder release. We advise enterprise customers without a staging environment to install our [latest stable release](https://github.com/coder/coder/releases/latest) while we refine this version. Learn more about our [Release Schedule](https://coder.com/docs/install/releases).") + // Only show the mainline blurb when the version is + // actually the current mainline series. Patches on + // older branches (e.g. ESR) are neither mainline nor + // stable, so we omit the note entirely. + if latestMainline != nil && newVersion.Minor == latestMainline.Minor { + fmt.Fprintln(¬es) + fmt.Fprintln(¬es, "> [!NOTE]") + fmt.Fprintln(¬es, "> This is a mainline Coder release. We advise enterprise customers without a staging environment to install our [latest stable release](https://github.com/coder/coder/releases/latest) while we refine this version. Learn more about our [Release Schedule](https://coder.com/docs/install/releases).") + } } hasContent := false diff --git a/scripts/releaser/version.go b/scripts/releaser/version.go index 7f5b21df90..f1e8107190 100644 --- a/scripts/releaser/version.go +++ b/scripts/releaser/version.go @@ -3,6 +3,7 @@ package main import ( "fmt" "regexp" + "sort" "strconv" "strings" ) @@ -85,6 +86,17 @@ func (v version) Equal(b version) bool { return v.Major == b.Major && v.Minor == b.Minor && v.Patch == b.Patch && v.Pre == b.Pre } +// sortVersionsDesc sorts a slice of versions in descending order +// using semver-correct comparison. This is necessary because git's +// --sort=-v:refname treats pre-release suffixes (e.g. -rc.0) as +// greater than the release version, which is the opposite of semver +// where v2.32.0 > v2.32.0-rc.0. +func sortVersionsDesc(tags []version) { + sort.Slice(tags, func(i, j int) bool { + return tags[i].GreaterThan(tags[j]) + }) +} + // allSemverTags returns all semver tags sorted descending. func allSemverTags() ([]version, error) { out, err := gitOutput("tag", "--sort=-v:refname") @@ -100,6 +112,7 @@ func allSemverTags() ([]version, error) { tags = append(tags, v) } } + sortVersionsDesc(tags) return tags, nil } @@ -119,5 +132,6 @@ func mergedSemverTags() ([]version, error) { tags = append(tags, v) } } + sortVersionsDesc(tags) return tags, nil } diff --git a/scripts/releaser/version_test.go b/scripts/releaser/version_test.go index 0fc6c0e5ff..914094a2e5 100644 --- a/scripts/releaser/version_test.go +++ b/scripts/releaser/version_test.go @@ -165,3 +165,76 @@ func TestVersionEqual(t *testing.T) { }) } } + +func TestSortVersionsDesc(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + input []version + want []version + }{ + { + // This is the exact scenario that triggered the bug: + // git's --sort=-v:refname places v2.32.0-rc.0 before + // v2.32.0, but semver says v2.32.0 > v2.32.0-rc.0. + name: "release_sorts_before_rc", + input: []version{ + {2, 32, 0, "rc.0"}, + {2, 32, 0, ""}, + {2, 31, 2, ""}, + }, + want: []version{ + {2, 32, 0, ""}, + {2, 32, 0, "rc.0"}, + {2, 31, 2, ""}, + }, + }, + { + name: "multiple_rcs_and_releases", + input: []version{ + {2, 33, 0, "rc.1"}, + {2, 33, 0, "rc.0"}, + {2, 32, 0, "rc.0"}, + {2, 32, 0, ""}, + {2, 32, 1, ""}, + {2, 31, 0, ""}, + }, + want: []version{ + {2, 33, 0, "rc.1"}, + {2, 33, 0, "rc.0"}, + {2, 32, 1, ""}, + {2, 32, 0, ""}, + {2, 32, 0, "rc.0"}, + {2, 31, 0, ""}, + }, + }, + { + name: "already_sorted", + input: []version{{3, 0, 0, ""}, {2, 0, 0, ""}, {1, 0, 0, ""}}, + want: []version{{3, 0, 0, ""}, {2, 0, 0, ""}, {1, 0, 0, ""}}, + }, + { + name: "empty", + input: []version{}, + want: []version{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + got := make([]version, len(tt.input)) + copy(got, tt.input) + sortVersionsDesc(got) + if len(got) != len(tt.want) { + t.Fatalf("sortVersionsDesc() returned %d elements, want %d", len(got), len(tt.want)) + } + for i := range got { + if !got[i].Equal(tt.want[i]) { + t.Fatalf("sortVersionsDesc()[%d] = %s, want %s\n full result: %v", i, got[i], tt.want[i], got) + } + } + }) + } +}