From ac4ad010d62a6ebd4b49e194e9be2f982e471d42 Mon Sep 17 00:00:00 2001 From: Dan Crosta Date: Tue, 18 Mar 2025 12:46:02 -0400 Subject: [PATCH] adjust custom/nightly/adhoc build terminology Previously, a version build was (incorrectly) termed "nightly" if it contained a commit count; these are not necessarily, or maybe never, true nightly builds. We now call them "custom" builds. Previously, a build with an arbitrary label was termed "custom"; these are now called "adhoc". --- version.go | 64 ++++++++++++++++------------ version_test.go | 108 +++++++++++++++++++++++++++++++++--------------- 2 files changed, 112 insertions(+), 60 deletions(-) diff --git a/version.go b/version.go index 963b825..054caa4 100644 --- a/version.go +++ b/version.go @@ -37,7 +37,7 @@ const ( rc = releasePhase(3) cloudonly = releasePhase(4) stable = releasePhase(5) - custom = releasePhase(6) + adhoc = releasePhase(6) ) // Version represents a CockroachDB (binary) version. Versions consist of three parts: @@ -53,11 +53,11 @@ type Version struct { // the fields are compared in the order listed here, and the earliest field with // a difference determines the relative ordering of two unequal versions. // - // The reference order: year, ordinal, patch, phase, phaseOrdinal, phaseSubOrdinal, nightlyOrdinal - year, ordinal, patch int - phase releasePhase - phaseOrdinal, phaseSubOrdinal, nightlyOrdinal int - customLabel string + // The reference order: year, ordinal, patch, phase, phaseOrdinal, phaseSubOrdinal, customOrdinal + year, ordinal, patch int + phase releasePhase + phaseOrdinal, phaseSubOrdinal, customOrdinal int + adhocLabel string // raw is the original, unprocessed string this Version was created with raw string } @@ -82,7 +82,7 @@ func (v Version) Patch() int { // - %p: phase sort order (see the top of version.go) // - %o: phase ordinal (eg, the 1 in "v24.1.0-rc.1") // - %s: phase sub-ordinal (eg the 2 in "v24.1.0-rc.1-cloudonly.2") -// - %n: nightly ordinal (eg the 12 in "v24.1.0-12-gabcdef") +// - %n: adhoc build ordinal (eg the 12 in "v24.1.0-12-gabcdef") // - %%: literal "%" func (v Version) Format(formatStr string) string { placeholderRe := regexp.MustCompile("%[^%XYZpPosn]") @@ -96,7 +96,7 @@ func (v Version) Format(formatStr string) string { beta: "beta", rc: "rc", cloudonly: "cloudonly", - custom: "", + adhoc: "", stable: "", } @@ -107,7 +107,7 @@ func (v Version) Format(formatStr string) string { formatStr = strings.ReplaceAll(formatStr, "%P", phaseName[v.phase]) formatStr = strings.ReplaceAll(formatStr, "%o", strconv.Itoa(v.phaseOrdinal)) formatStr = strings.ReplaceAll(formatStr, "%s", strconv.Itoa(v.phaseSubOrdinal)) - formatStr = strings.ReplaceAll(formatStr, "%n", strconv.Itoa(v.nightlyOrdinal)) + formatStr = strings.ReplaceAll(formatStr, "%n", strconv.Itoa(v.customOrdinal)) formatStr = strings.ReplaceAll(formatStr, "%%", "%") return formatStr } @@ -171,9 +171,19 @@ func (v Version) IsPrerelease() bool { return v.phase < cloudonly && !v.Empty() } -// IsCustomOrNightlyBuild determines if the version is a custom build or nightly build. -func (v Version) IsCustomOrNightlyBuild() bool { - return v.nightlyOrdinal > 0 +// IsCustomOrAdhocBuild determines if the version is a adhoc build or adhoc build. +func (v Version) IsCustomOrAdhocBuild() bool { + return v.IsCustomBuild() || v.IsAdhocBuild() +} + +// IsCustomBuild determines if the version is a adhoc build. +func (v Version) IsCustomBuild() bool { + return v.customOrdinal > 0 +} + +// IsAdhocBuild determines if the version is a adhoc build. +func (v Version) IsAdhocBuild() bool { + return v.adhocLabel != "" } // IsCloudOnlyBuild determines if the version is a CockroachDB Cloud specific build. @@ -197,17 +207,17 @@ func Parse(str string) (Version, error) { patterns := []*regexp.Regexp{ regexp.MustCompile(`^v(?P[1-9][0-9]*)\.(?P[1-9][0-9]*)\.(?P(?:[1-9][0-9]*|0))(?:-fips)?$`), regexp.MustCompile(`^v(?P[1-9][0-9]*)\.(?P[1-9][0-9]*)\.(?P(?:[1-9][0-9]*|0))-(?Palpha|beta|rc|cloudonly)\.(?P[0-9]+)(?:-fips)?$`), - regexp.MustCompile(`^v(?P[1-9][0-9]*)\.(?P[1-9][0-9]*)\.(?P(?:[1-9][0-9]*|0))-(?P(?:[1-9][0-9]*|0))-g[a-f0-9]+(?:-fips)?$`), - regexp.MustCompile(`^v(?P[1-9][0-9]*)\.(?P[1-9][0-9]*)\.(?P(?:[1-9][0-9]*|0))-(?Palpha|beta|rc|cloudonly).(?P[0-9]+)-(?P(?:[1-9][0-9]*|0))-g[a-f0-9]+(?:-fips)?$`), + regexp.MustCompile(`^v(?P[1-9][0-9]*)\.(?P[1-9][0-9]*)\.(?P(?:[1-9][0-9]*|0))-(?P(?:[1-9][0-9]*|0))-g[a-f0-9]+(?:-fips)?$`), + regexp.MustCompile(`^v(?P[1-9][0-9]*)\.(?P[1-9][0-9]*)\.(?P(?:[1-9][0-9]*|0))-(?Palpha|beta|rc|cloudonly).(?P[0-9]+)-(?P(?:[1-9][0-9]*|0))-g[a-f0-9]+(?:-fips)?$`), regexp.MustCompile(`^v(?P[1-9][0-9]*)\.(?P[1-9][0-9]*)\.(?P(?:[1-9][0-9]*|0))-(?Palpha|beta|rc|cloudonly).(?P[0-9]+)-cloudonly(-rc|\.)(?P(?:[1-9][0-9]*|0))$`), regexp.MustCompile(`^v(?P[1-9][0-9]*)\.(?P[1-9][0-9]*)\.(?P(?:[1-9][0-9]*|0))-(?Pcloudonly)-rc(?P[0-9]+)$`), regexp.MustCompile(`^v(?P[1-9][0-9]*)\.(?P[1-9][0-9]*)\.(?P(?:[1-9][0-9]*|0))-(?Pcloudonly)(?P[0-9]+)?$`), // vX.Y.Z- will sort after the corresponding "plain" vX.Y.Z version - regexp.MustCompile(`^v(?P[1-9][0-9]*)\.(?P[1-9][0-9]*)\.(?P(?:[1-9][0-9]*|0))-(?P[-a-zA-Z0-9\.\+]+)$`), + regexp.MustCompile(`^v(?P[1-9][0-9]*)\.(?P[1-9][0-9]*)\.(?P(?:[1-9][0-9]*|0))-(?P[-a-zA-Z0-9\.\+]+)$`), // sha256::latest-vX.Y-build will sort just after vX.Y.0, but before vX.Y.1 - regexp.MustCompile(`^sha256:(?P[^:]+):latest-v(?P[1-9][0-9]*)\.(?P[1-9][0-9]*)-build$`), + regexp.MustCompile(`^sha256:(?P[^:]+):latest-v(?P[1-9][0-9]*)\.(?P[1-9][0-9]*)-build$`), } preReleasePhase := map[string]releasePhase{ @@ -257,15 +267,15 @@ func Parse(str string) (Version, error) { } } - // nightly/custom builds, eg -10-g7890abcd - if ord := submatch(pat, matches, "nightlyOrdinal"); ord != "" { - v.nightlyOrdinal, _ = strconv.Atoi(ord) + // adhoc/adhoc builds, eg -10-g7890abcd + if ord := submatch(pat, matches, "customOrdinal"); ord != "" { + v.customOrdinal, _ = strconv.Atoi(ord) } - // arbitrary/custom build tags; we have these old versions and need to parse them - if customLabel := submatch(pat, matches, "customLabel"); customLabel != "" { - v.phase = custom - v.customLabel = customLabel + // arbitrary/adhoc build tags; we have these old versions and need to parse them + if adhocLabel := submatch(pat, matches, "adhocLabel"); adhocLabel != "" { + v.phase = adhoc + v.adhocLabel = adhocLabel } return v, nil @@ -296,12 +306,12 @@ func MustParse(str string) Version { // rc, cloudonly. Pre-release versions will look like "v24.1.0-cloudonly.1" // or "v23.2.0-rc.1". // -// Additionally, we have custom builds, which have suffixes like "--g", +// Additionally, we have adhoc builds, which have suffixes like "--g", // where is an integer commit count past the branch point, and is // the git SHA. These versions sort AFTER the corresponding "normal" version, // eg "v24.1.0-1-g9cbe7c5281" is AFTER "v24.1.0". // -// A version can have both a pre-release and custom build suffix, like +// A version can have both a pre-release and adhoc build suffix, like // "v24.1.0-rc.2-14-g". In these cases, the pre-release portion has precedence, // so this example would sort after v24.1.0-rc.2, but before v24.1.0-rc.3. func (v Version) Compare(w Version) int { @@ -323,10 +333,10 @@ func (v Version) Compare(w Version) int { if rslt := cmp.Compare(v.phaseSubOrdinal, w.phaseSubOrdinal); rslt != 0 { return rslt } - if rslt := cmp.Compare(v.nightlyOrdinal, w.nightlyOrdinal); rslt != 0 { + if rslt := cmp.Compare(v.customOrdinal, w.customOrdinal); rslt != 0 { return rslt } - if rslt := cmp.Compare(v.customLabel, w.customLabel); rslt != 0 { + if rslt := cmp.Compare(v.adhocLabel, w.adhocLabel); rslt != 0 { return rslt } return 0 diff --git a/version_test.go b/version_test.go index 64b0d59..14ef93b 100644 --- a/version_test.go +++ b/version_test.go @@ -50,7 +50,7 @@ func TestVersion_Getters(t *testing.T) { require.True(t, v.IsPrerelease()) require.False(t, v.IsCloudOnlyBuild()) - require.False(t, v.IsCustomOrNightlyBuild()) + require.False(t, v.IsCustomOrAdhocBuild()) } func TestVersion_IsPrerelease(t *testing.T) { @@ -79,31 +79,73 @@ func TestVersion_IsPrerelease(t *testing.T) { require.False(t, MustParse("v23.2.0-cloudonly2").IsPrerelease()) } -func TestVersion_IsCustomOrNightlyBuild(t *testing.T) { - // Valid pre-release versions - require.False(t, MustParse("v20.2.0-beta.3").IsCustomOrNightlyBuild()) - require.False(t, MustParse("v19.1.0-rc.5").IsCustomOrNightlyBuild()) - require.False(t, MustParse("v20.2.0-alpha.1").IsCustomOrNightlyBuild()) - require.True(t, MustParse("v21.1.0-rc.2-163-g122c66f436").IsCustomOrNightlyBuild()) - require.True(t, MustParse("v21.1.0-beta.5-57-gf05a57face").IsCustomOrNightlyBuild()) - require.True(t, MustParse("v21.1.0-alpha.3-2846-g7ae3ac92f7").IsCustomOrNightlyBuild()) - - // Valid [cloudonly] pre-release versions - require.False(t, MustParse("v23.2.0-rc.2-cloudonly-rc2").IsCustomOrNightlyBuild()) +func TestVersion_CustomAndAdhocBuilds(t *testing.T) { + builds := []struct { + version string + adhoc bool + custom bool + }{ + // Valid pre-release versions + {version: "v20.2.0-beta.3", adhoc: false, custom: false}, + {version: "v19.1.0-rc.5", adhoc: false, custom: false}, + {version: "v20.2.0-alpha.1", adhoc: false, custom: false}, + {version: "v21.1.0-rc.2-163-g122c66f436", adhoc: false, custom: true}, + {version: "v21.1.0-beta.5-57-gf05a57face", adhoc: false, custom: true}, + {version: "v21.1.0-alpha.3-2846-g7ae3ac92f7", adhoc: false, custom: true}, + + // Valid [cloudonly] pre-release versions + {version: "v23.2.0-rc.2-cloudonly-rc2", adhoc: false, custom: false}, + + // Valid production versions + {version: "v19.2.6", adhoc: false, custom: false}, + {version: "v21.1.0", adhoc: false, custom: false}, + {version: "v21.1.0-247-g5668206478", adhoc: false, custom: true}, + + // Valid [cloudonly] production versions + {version: "v23.1.12-cloudonly-rc2", adhoc: false, custom: false}, + + // Valid [cloudonly] v23.2.0 (production) versions may or may not have "rc" after "-cloudonly" suffix. + {version: "v23.2.0-cloudonly-rc2", adhoc: false, custom: false}, + {version: "v23.2.0-cloudonly", adhoc: false, custom: false}, + {version: "v23.2.0-cloudonly.1", adhoc: false, custom: false}, + {version: "v23.2.0-cloudonly2", adhoc: false, custom: false}, + + // All other adhoc labels + {version: "v23.2.0-arbitrary-adhoc-label", adhoc: true, custom: false}, + } - // Valid production versions - require.False(t, MustParse("v19.2.6").IsCustomOrNightlyBuild()) - require.False(t, MustParse("v21.1.0").IsCustomOrNightlyBuild()) - require.True(t, MustParse("v21.1.0-247-g5668206478").IsCustomOrNightlyBuild()) + t.Run("IsCustomOrAdhocBuild", func(t *testing.T) { + for _, tc := range builds { + v := MustParse(tc.version) + if tc.adhoc || tc.custom { + require.True(t, v.IsCustomOrAdhocBuild()) + } else { + require.False(t, v.IsCustomOrAdhocBuild()) + } + } + }) - // Valid [cloudonly] production versions - require.False(t, MustParse("v23.1.12-cloudonly-rc2").IsCustomOrNightlyBuild()) + t.Run("IsAdhocBuild", func(t *testing.T) { + for _, tc := range builds { + v := MustParse(tc.version) + if tc.adhoc { + require.True(t, v.IsAdhocBuild()) + } else { + require.False(t, v.IsAdhocBuild()) + } + } + }) - // Valid [cloudonly] v23.2.0 (production) versions may or may not have "rc" after "-cloudonly" suffix. - require.False(t, MustParse("v23.2.0-cloudonly-rc2").IsCustomOrNightlyBuild()) - require.False(t, MustParse("v23.2.0-cloudonly").IsCustomOrNightlyBuild()) - require.False(t, MustParse("v23.2.0-cloudonly.1").IsCustomOrNightlyBuild()) - require.False(t, MustParse("v23.2.0-cloudonly2").IsCustomOrNightlyBuild()) + t.Run("IsCustomBuild", func(t *testing.T) { + for _, tc := range builds { + v := MustParse(tc.version) + if tc.custom { + require.True(t, v.IsCustomBuild()) + } else { + require.False(t, v.IsCustomBuild()) + } + } + }) } func TestVersion_IsCloudOnlyBuild(t *testing.T) { @@ -305,15 +347,15 @@ func TestParse(t *testing.T) { phaseOrdinal: 2, }, }, - // custom releases + // adhoc releases { raw: "v24.2.3-12-gabcd1234", want: Version{ - year: 24, - ordinal: 2, - patch: 3, - phase: stable, - nightlyOrdinal: 12, + year: 24, + ordinal: 2, + patch: 3, + phase: stable, + customOrdinal: 12, }, }, } { @@ -400,7 +442,7 @@ func TestVersionCompare(t *testing.T) { want: aLessThanB, }, - // even if there's a prerelease or nightly tag + // even if there's a prerelease or custom tag { a: "v20.2.7", b: "v21.1.0-alpha.3", @@ -454,7 +496,7 @@ func TestVersionCompare(t *testing.T) { want: aLessThanB, }, - // nightly & custom builds are greater than "regular" builds, prereleases, and cloudonly + // custom & adhoc builds are greater than "regular" builds, prereleases, and cloudonly { a: "v21.1.0-alpha.3", b: "v21.1.0-1-g9cbe7c5281", @@ -533,12 +575,12 @@ func TestVersionOrdering(t *testing.T) { want: []string{"v20.2.10", "v21.1.0-alpha.1", "v21.1.0-beta.2", "v21.1.0-rc.1", "v21.1.0-cloudonly.1", "v21.1.0"}, }, { - name: "sorts custom builds after corresponding normal version", + name: "sorts adhoc builds after corresponding normal version", input: []string{"v21.1.0-1-g9cbe7c5281", "v21.1.0", "v21.1.0-rc.1"}, want: []string{"v21.1.0-rc.1", "v21.1.0", "v21.1.0-1-g9cbe7c5281"}, }, { - name: "sorts nonstandard custom builds after corresponding normal version", + name: "sorts nonstandard adhoc builds after corresponding normal version", input: []string{"v21.1.0-customLabel", "v21.1.0", "v21.1.0-rc.1"}, want: []string{"v21.1.0-rc.1", "v21.1.0", "v21.1.0-customLabel"}, },