Skip to content

Commit 3e11fbc

Browse files
tsuzusivchari
andauthored
🌱 Add validation for PREVIOUS_RELEASE_TAG in release-notes-tool (#12380)
* Add validation for PREVIOUS_RELEASE_TAG and support alpha/beta/rc releases * Update release-provider-issues-tool for alpha/rc * Update hack/tools/release/notes/main.go Co-authored-by: sivchari <[email protected]> --------- Co-authored-by: sivchari <[email protected]>
1 parent beddf81 commit 3e11fbc

File tree

4 files changed

+178
-24
lines changed

4 files changed

+178
-24
lines changed

hack/tools/release/internal/update_providers/provider_issues.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,15 +75,17 @@ type IssueResponse struct {
7575
// releaseDetails is the struct for the release details.
7676
type releaseDetails struct {
7777
ReleaseTag string
78-
BetaTag string
78+
PreReleaseTag string
7979
ReleaseLink string
8080
ReleaseDate string
8181
ReleaseNotesLink string
8282
}
8383

8484
// Example command:
8585
//
86+
// GITHUB_ISSUE_OPENER_TOKEN="fake" RELEASE_TAG="v1.6.0-alpha.0" RELEASE_DATE="2023-11-28" PROVIDER_ISSUES_DRY_RUN="true" make release-provider-issues-tool
8687
// GITHUB_ISSUE_OPENER_TOKEN="fake" RELEASE_TAG="v1.6.0-beta.0" RELEASE_DATE="2023-11-28" PROVIDER_ISSUES_DRY_RUN="true" make release-provider-issues-tool
88+
// GITHUB_ISSUE_OPENER_TOKEN="fake" RELEASE_TAG="v1.6.0-rc.0" RELEASE_DATE="2023-11-28" PROVIDER_ISSUES_DRY_RUN="true" make release-provider-issues-tool
8789
func main() {
8890
githubToken, keySet := os.LookupEnv("GITHUB_ISSUE_OPENER_TOKEN")
8991
if !keySet || githubToken == "" {
@@ -264,11 +266,11 @@ func getReleaseDetails() (releaseDetails, error) {
264266
return releaseDetails{}, errors.New("release tag is a required. Refer to README.md in folder for more information")
265267
}
266268

267-
// allow patterns like v1.7.0-beta.0
268-
pattern := `^v\d+\.\d+\.\d+-beta\.\d+$`
269+
// allow patterns like v1.7.0-alpha.0, v1.7.0-beta.0, v1.7.0-rc.0
270+
pattern := `^v\d+\.\d+\.\d+-(alpha|beta|rc)\.\d+$`
269271
match, err := regexp.MatchString(pattern, releaseSemVer)
270272
if err != nil || !match {
271-
return releaseDetails{}, errors.New("release tag must be in format `^v\\d+\\.\\d+\\.\\d+-beta\\.\\d+$` e.g. v1.7.0-beta.0")
273+
return releaseDetails{}, errors.New("release tag must be in format `^v\\d+\\.\\d+\\.\\d+-(alpha|beta|rc)\\.\\d+$` e.g. v1.7.0-beta.0")
272274
}
273275

274276
major, minor, patch := "", "", ""
@@ -296,14 +298,13 @@ func getReleaseDetails() (releaseDetails, error) {
296298

297299
majorMinorWithoutPrefixV := fmt.Sprintf("%s.%s", major, minor) // e.g. 1.7 . Note that there is no "v" in the majorMinor
298300
releaseTag := fmt.Sprintf("v%s.%s.%s", major, minor, patch) // e.g. v1.7.0
299-
betaTag := fmt.Sprintf("%s%s", releaseTag, "-beta.0") // e.g. v1.7.0-beta.0
300301
releaseLink := fmt.Sprintf("https://github.com/kubernetes-sigs/cluster-api/tree/main/docs/release/releases/release-%s.md#timeline", majorMinorWithoutPrefixV)
301-
releaseNotesLink := fmt.Sprintf("https://github.com/kubernetes-sigs/cluster-api/releases/tag/%s", betaTag)
302+
releaseNotesLink := fmt.Sprintf("https://github.com/kubernetes-sigs/cluster-api/releases/tag/%s", releaseSemVer)
302303

303304
return releaseDetails{
304305
ReleaseDate: formattedReleaseDate,
305306
ReleaseTag: releaseTag,
306-
BetaTag: betaTag,
307+
PreReleaseTag: releaseSemVer,
307308
ReleaseLink: releaseLink,
308309
ReleaseNotesLink: releaseNotesLink,
309310
}, nil
@@ -339,13 +340,13 @@ func formatDate(inputDate string) (string, error) {
339340
func getIssueBody() *template.Template {
340341
// do not indent the body
341342
// indenting the body will result in the body being posted as a code snippet
342-
issueBody, err := template.New("issue").Parse(`CAPI {{.BetaTag}} has been released and is ready for testing.
343+
issueBody, err := template.New("issue").Parse(`CAPI {{.PreReleaseTag}} has been released and is ready for testing.
343344
Looking forward to your feedback before {{.ReleaseTag}} release!
344345
345346
## For quick reference
346347
347348
<!-- body -->
348-
- [CAPI {{.BetaTag}} release notes]({{.ReleaseNotesLink}})
349+
- [CAPI {{.PreReleaseTag}} release notes]({{.ReleaseNotesLink}})
349350
- [Shortcut to CAPI git issues](https://github.com/kubernetes-sigs/cluster-api/issues)
350351
351352
## Following are the planned dates for the upcoming releases
@@ -365,7 +366,7 @@ More details of the upcoming schedule can be seen at [CAPI {{.ReleaseTag}} relea
365366

366367
// getIssueTitle returns the issue title template.
367368
func getIssueTitle() *template.Template {
368-
issueTitle, err := template.New("title").Parse(`CAPI {{.BetaTag}} has been released and is ready for testing`)
369+
issueTitle, err := template.New("title").Parse(`CAPI {{.PreReleaseTag}} has been released and is ready for testing`)
369370
if err != nil {
370371
panic(err)
371372
}

hack/tools/release/internal/update_providers/provider_issues_test.go

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,38 +35,71 @@ func Test_GetReleaseDetails(t *testing.T) {
3535
err string
3636
}{
3737
{
38-
name: "Correct RELEASE_TAG and RELEASE_DATE are set",
38+
name: "Correct RELEASE_TAG and RELEASE_DATE are set for alpha",
39+
releaseTag: "v1.7.0-alpha.0",
40+
releaseDate: "2024-04-16",
41+
want: releaseDetails{
42+
ReleaseDate: "Tuesday, 16th April 2024",
43+
ReleaseTag: "v1.7.0",
44+
PreReleaseTag: "v1.7.0-alpha.0",
45+
ReleaseLink: "https://github.com/kubernetes-sigs/cluster-api/tree/main/docs/release/releases/release-1.7.md#timeline",
46+
ReleaseNotesLink: "https://github.com/kubernetes-sigs/cluster-api/releases/tag/v1.7.0-alpha.0",
47+
},
48+
expectErr: false,
49+
},
50+
{
51+
name: "Correct RELEASE_TAG and RELEASE_DATE are set for beta",
3952
releaseTag: "v1.7.0-beta.0",
4053
releaseDate: "2024-04-16",
4154
want: releaseDetails{
4255
ReleaseDate: "Tuesday, 16th April 2024",
4356
ReleaseTag: "v1.7.0",
44-
BetaTag: "v1.7.0-beta.0",
57+
PreReleaseTag: "v1.7.0-beta.0",
4558
ReleaseLink: "https://github.com/kubernetes-sigs/cluster-api/tree/main/docs/release/releases/release-1.7.md#timeline",
4659
ReleaseNotesLink: "https://github.com/kubernetes-sigs/cluster-api/releases/tag/v1.7.0-beta.0",
4760
},
4861
expectErr: false,
4962
},
5063
{
51-
name: "RELEASE_TAG is not in the format ^v\\d+\\.\\d+\\.\\d+-beta\\.\\d+$",
64+
name: "Correct RELEASE_TAG and RELEASE_DATE are set for rc",
65+
releaseTag: "v1.7.0-rc.0",
66+
releaseDate: "2024-04-16",
67+
want: releaseDetails{
68+
ReleaseDate: "Tuesday, 16th April 2024",
69+
ReleaseTag: "v1.7.0",
70+
PreReleaseTag: "v1.7.0-rc.0",
71+
ReleaseLink: "https://github.com/kubernetes-sigs/cluster-api/tree/main/docs/release/releases/release-1.7.md#timeline",
72+
ReleaseNotesLink: "https://github.com/kubernetes-sigs/cluster-api/releases/tag/v1.7.0-rc.0",
73+
},
74+
expectErr: false,
75+
},
76+
{
77+
name: "RELEASE_TAG is not in the correct format",
5278
releaseTag: "v1.7.0.1",
5379
releaseDate: "2024-04-16",
5480
expectErr: true,
55-
err: "release tag must be in format `^v\\d+\\.\\d+\\.\\d+-beta\\.\\d+$` e.g. v1.7.0-beta.0",
81+
err: "release tag must be in format `^v\\d+\\.\\d+\\.\\d+-(alpha|beta|rc)\\.\\d+$` e.g. v1.7.0-beta.0",
5682
},
5783
{
5884
name: "RELEASE_TAG does not have prefix 'v' in its semver",
5985
releaseTag: "1.7.0-beta.0",
6086
releaseDate: "2024-04-16",
6187
expectErr: true,
62-
err: "release tag must be in format `^v\\d+\\.\\d+\\.\\d+-beta\\.\\d+$` e.g. v1.7.0-beta.0",
88+
err: "release tag must be in format `^v\\d+\\.\\d+\\.\\d+-(alpha|beta|rc)\\.\\d+$` e.g. v1.7.0-beta.0",
6389
},
6490
{
6591
name: "RELEASE_TAG contains invalid Major.Minor.Patch SemVer",
6692
releaseTag: "v1.x.0-beta.0",
6793
releaseDate: "2024-04-16",
6894
expectErr: true,
69-
err: "release tag must be in format `^v\\d+\\.\\d+\\.\\d+-beta\\.\\d+$` e.g. v1.7.0-beta.0",
95+
err: "release tag must be in format `^v\\d+\\.\\d+\\.\\d+-(alpha|beta|rc)\\.\\d+$` e.g. v1.7.0-beta.0",
96+
},
97+
{
98+
name: "RELEASE_TAG contains unsupported pre-release type",
99+
releaseTag: "v1.7.0-gamma.0",
100+
releaseDate: "2024-04-16",
101+
expectErr: true,
102+
err: "release tag must be in format `^v\\d+\\.\\d+\\.\\d+-(alpha|beta|rc)\\.\\d+$` e.g. v1.7.0-beta.0",
70103
},
71104
{
72105
name: "invalid yyyy-dd-mm RELEASE_DATE entered",
@@ -104,7 +137,7 @@ func Test_GetReleaseDetails(t *testing.T) {
104137
} else {
105138
g.Expect(got.ReleaseDate).To(Equal(tt.want.ReleaseDate))
106139
g.Expect(got.ReleaseTag).To(Equal(tt.want.ReleaseTag))
107-
g.Expect(got.BetaTag).To(Equal(tt.want.BetaTag))
140+
g.Expect(got.PreReleaseTag).To(Equal(tt.want.PreReleaseTag))
108141
g.Expect(got.ReleaseLink).To(Equal(tt.want.ReleaseLink))
109142
}
110143
})

hack/tools/release/notes/main.go

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"fmt"
2626
"log"
2727
"os/exec"
28+
"strings"
2829

2930
"github.com/blang/semver/v4"
3031
"github.com/pkg/errors"
@@ -42,6 +43,11 @@ const (
4243
alphaRelease = "ALPHA RELEASE"
4344
betaRelease = "BETA RELEASE"
4445
releaseCandidate = "RELEASE CANDIDATE"
46+
47+
// Pre-release type constants.
48+
preReleaseAlpha = "alpha"
49+
preReleaseBeta = "beta"
50+
preReleaseRC = "rc"
4551
)
4652

4753
func main() {
@@ -140,15 +146,15 @@ func releaseTypeFromNewTag(newTagConfig string) string {
140146
return ""
141147
}
142148

143-
// Only allow RC and beta releases. More types must be defined here.
149+
// Only allow alpha, beta and rc releases. More types must be defined here.
144150
// If a new type is not defined, no warning banner will be printed.
145151
switch newTag.Pre[0].VersionStr {
146-
case "rc":
147-
return releaseCandidate
148-
case "beta":
149-
return betaRelease
150-
case "alpha":
152+
case preReleaseAlpha:
151153
return alphaRelease
154+
case preReleaseBeta:
155+
return betaRelease
156+
case preReleaseRC:
157+
return releaseCandidate
152158
}
153159
return ""
154160
}
@@ -187,6 +193,45 @@ func validateConfig(config *notesCmdConfig) error {
187193
}
188194
}
189195

196+
if config.previousReleaseVersion != "" {
197+
if err := validatePreviousReleaseVersion(config.previousReleaseVersion); err != nil {
198+
return err
199+
}
200+
}
201+
202+
return nil
203+
}
204+
205+
func validatePreviousReleaseVersion(previousReleaseVersion string) error {
206+
// Extract version string from ref format (e.g. "tags/v1.0.0-rc.1" -> "v1.0.0-rc.1")
207+
if !strings.Contains(previousReleaseVersion, "/") {
208+
return errors.New("--previous-release-version must be in ref format (e.g. tags/v1.0.0-rc.1)")
209+
}
210+
211+
parts := strings.SplitN(previousReleaseVersion, "/", 2)
212+
if len(parts) != 2 {
213+
return errors.New("--previous-release-version must be in ref format (e.g. tags/v1.0.0-rc.1)")
214+
}
215+
216+
versionStr := parts[1]
217+
218+
// Parse the version to check if it contains alpha, beta, or rc
219+
version, err := semver.ParseTolerant(versionStr)
220+
if err != nil {
221+
return errors.Wrap(err, "invalid --previous-release-version, is not a valid semver")
222+
}
223+
224+
// Check if the version has pre-release identifiers
225+
if len(version.Pre) == 0 {
226+
return errors.Errorf("--previous-release-version must contain '%s', '%s', or '%s' pre-release identifier", preReleaseAlpha, preReleaseBeta, preReleaseRC)
227+
}
228+
229+
// Check if the first pre-release identifier is 'alpha', 'beta', or 'rc'
230+
preReleaseType := version.Pre[0].VersionStr
231+
if preReleaseType != preReleaseAlpha && preReleaseType != preReleaseBeta && preReleaseType != preReleaseRC {
232+
return errors.Errorf("--previous-release-version must contain '%s', '%s', or '%s' pre-release identifier", preReleaseAlpha, preReleaseBeta, preReleaseRC)
233+
}
234+
190235
return nil
191236
}
192237

hack/tools/release/notes/main_test.go

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ limitations under the License.
2020
package main
2121

2222
import (
23+
"strings"
2324
"testing"
2425

2526
"github.com/blang/semver/v4"
@@ -229,12 +230,86 @@ func Test_validateConfig(t *testing.T) {
229230
},
230231
wantErr: false,
231232
},
233+
{
234+
name: "Invalid previousReleaseVersion without ref format",
235+
args: &notesCmdConfig{
236+
fromRef: "ref1/tags",
237+
toRef: "ref2/tags",
238+
newTag: "v1.0.0",
239+
previousReleaseVersion: "v1.0.0-rc.0",
240+
},
241+
wantErr: true,
242+
errorMessage: "--previous-release-version must be in ref format",
243+
},
244+
{
245+
name: "Valid previousReleaseVersion with rc in ref format",
246+
args: &notesCmdConfig{
247+
fromRef: "ref1/tags",
248+
toRef: "ref2/tags",
249+
newTag: "v1.0.0",
250+
previousReleaseVersion: "tags/v1.0.0-rc.0",
251+
},
252+
wantErr: false,
253+
},
254+
{
255+
name: "Valid previousReleaseVersion with alpha in ref format",
256+
args: &notesCmdConfig{
257+
fromRef: "ref1/tags",
258+
toRef: "ref2/tags",
259+
newTag: "v1.0.0",
260+
previousReleaseVersion: "tags/v1.0.0-alpha.1",
261+
},
262+
wantErr: false,
263+
},
264+
{
265+
name: "Valid previousReleaseVersion with beta in ref format",
266+
args: &notesCmdConfig{
267+
fromRef: "ref1/tags",
268+
toRef: "ref2/tags",
269+
newTag: "v1.0.0",
270+
previousReleaseVersion: "tags/v1.0.0-beta.1",
271+
},
272+
wantErr: false,
273+
},
274+
{
275+
name: "Invalid previousReleaseVersion without pre-release in ref format",
276+
args: &notesCmdConfig{
277+
fromRef: "ref1/tags",
278+
toRef: "ref2/tags",
279+
newTag: "v1.0.0",
280+
previousReleaseVersion: "tags/v1.0.0",
281+
},
282+
wantErr: true,
283+
errorMessage: "--previous-release-version must contain 'alpha', 'beta', or 'rc' pre-release identifier",
284+
},
285+
{
286+
name: "Invalid previousReleaseVersion with unsupported pre-release type",
287+
args: &notesCmdConfig{
288+
fromRef: "ref1/tags",
289+
toRef: "ref2/tags",
290+
newTag: "v1.0.0",
291+
previousReleaseVersion: "tags/v1.0.0-dev.1",
292+
},
293+
wantErr: true,
294+
errorMessage: "--previous-release-version must contain 'alpha', 'beta', or 'rc' pre-release identifier",
295+
},
296+
{
297+
name: "Invalid previousReleaseVersion with invalid semver",
298+
args: &notesCmdConfig{
299+
fromRef: "ref1/tags",
300+
toRef: "ref2/tags",
301+
newTag: "v1.0.0",
302+
previousReleaseVersion: "tags/invalid-version",
303+
},
304+
wantErr: true,
305+
errorMessage: "invalid --previous-release-version, is not a valid semver",
306+
},
232307
}
233308
for _, tt := range tests {
234309
t.Run(tt.name, func(t *testing.T) {
235310
err := validateConfig(tt.args)
236311
if tt.wantErr {
237-
if err == nil || err.Error() != tt.errorMessage {
312+
if err == nil || !strings.Contains(err.Error(), tt.errorMessage) {
238313
t.Errorf("expected error '%s', got '%v'", tt.errorMessage, err)
239314
}
240315
} else if err != nil {

0 commit comments

Comments
 (0)