From aa2df7040bdd9dc86f679038a8366a4489de4822 Mon Sep 17 00:00:00 2001 From: Nawaz Hussain Khazielakha Date: Tue, 12 Mar 2024 13:48:15 -0700 Subject: [PATCH] add tests and update provider_issues.go --- .../internal/update_providers/README.md | 6 +- .../update_providers/provider_issues.go | 75 ++++++++---- .../update_providers/provider_issues_test.go | 115 ++++++++++++++++++ 3 files changed, 166 insertions(+), 30 deletions(-) create mode 100644 hack/tools/release/internal/update_providers/provider_issues_test.go diff --git a/hack/tools/release/internal/update_providers/README.md b/hack/tools/release/internal/update_providers/README.md index 80b52fdb646f..a67565b307d4 100644 --- a/hack/tools/release/internal/update_providers/README.md +++ b/hack/tools/release/internal/update_providers/README.md @@ -15,14 +15,14 @@ export PROVIDER_ISSUES_DRY_RUN="true" ``` -- Export `RELEASE_TAG` environment variable to the CAPI release version e.g. `1.6.0`. The suffix `-beta.0` is appended by the utility. +- Export `RELEASE_TAG` environment variable to the CAPI release version e.g. `v1.7.0-beta.0`. Example: ```sh - export RELEASE_TAG="1.6.0" + export RELEASE_TAG="v1.7.0-beta.0" ``` -- Export `RELEASE_DATE` to the targeted CAPI release version date. Fetch the target date from latest [release file](https://github.com/kubernetes-sigs/cluster-api/tree/main/docs/release/releases). +- Export `RELEASE_DATE` to the targeted CAPI release version date. Fetch the target date from latest [release file](https://github.com/kubernetes-sigs/cluster-api/tree/main/docs/release/releases). The `RELEASE_DATE` should be in the format `YYYY-MM-DD`. Example: ```sh diff --git a/hack/tools/release/internal/update_providers/provider_issues.go b/hack/tools/release/internal/update_providers/provider_issues.go index c76f6e91fe83..6616cf2dea1a 100644 --- a/hack/tools/release/internal/update_providers/provider_issues.go +++ b/hack/tools/release/internal/update_providers/provider_issues.go @@ -32,6 +32,8 @@ import ( "strings" "text/template" "time" + + "github.com/pkg/errors" ) const ( @@ -72,15 +74,16 @@ type IssueResponse struct { // releaseDetails is the struct for the release details. type releaseDetails struct { - ReleaseTag string - BetaTag string - ReleaseLink string - ReleaseDate string + ReleaseTag string + BetaTag string + ReleaseLink string + ReleaseDate string + ReleaseNotesLink string } // Example command: // -// GITHUB_ISSUE_OPENER_TOKEN="fake" RELEASE_TAG="1.6.0" RELEASE_DATE="2023-11-28" PROVIDER_ISSUES_DRY_RUN="true" make release-provider-issues-tool +// 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 func main() { githubToken, keySet := os.LookupEnv("GITHUB_ISSUE_OPENER_TOKEN") if !keySet || githubToken == "" { @@ -108,7 +111,12 @@ func main() { fmt.Println("-", strings.Join(repoList, "\n- ")) fmt.Printf("\n") - details := getReleaseDetails() + // get release details + details, releaseDetailsErr := getReleaseDetails() + if releaseDetailsErr != nil { + fmt.Println(releaseDetailsErr.Error()) + os.Exit(1) + } // generate title titleBuffer := bytes.NewBuffer([]byte{}) @@ -249,43 +257,56 @@ func continueOrAbort() { } // getReleaseDetails returns the release details from the environment variables. -func getReleaseDetails() releaseDetails { +func getReleaseDetails() (releaseDetails, error) { + // Parse the release tag releaseSemVer, keySet := os.LookupEnv("RELEASE_TAG") if !keySet || releaseSemVer == "" { - fmt.Println("RELEASE_TAG is a required environmental variable.") - fmt.Println("Refer to README.md in folder for more information.") - os.Exit(1) + return releaseDetails{}, errors.New("release tag is a required. Refer to README.md in folder for more information") } - match, err := regexp.Match("\\d\\.\\d\\.\\d", []byte(releaseSemVer)) + // allow patterns like v1.7.0-beta.0 + pattern := `^v\d+\.\d+\.\d+-beta\.\d+$` + match, err := regexp.MatchString(pattern, releaseSemVer) if err != nil || !match { - fmt.Println("RELEASE_TAG must be in format `\\d\\.\\d\\.\\d` e.g. 1.5") - os.Exit(1) + return releaseDetails{}, errors.New("release tag must be in format `^v\\d+\\.\\d+\\.\\d+-beta\\.\\d+$` e.g. v1.7.0-beta.0") } + major, minor, patch := "", "", "" + majorMinorPatchPattern := `v(\d+)\.(\d+)\.(\d+)` + re := regexp.MustCompile(majorMinorPatchPattern) + releaseSemVerMatch := re.FindStringSubmatch(releaseSemVer) + if len(releaseSemVerMatch) > 3 { + major = releaseSemVerMatch[1] + minor = releaseSemVerMatch[2] + patch = releaseSemVerMatch[3] + } else { + return releaseDetails{}, errors.New("release tag contains invalid Major.Minor.Patch SemVer. It must be in format v(\\d+)\\.(\\d+)\\.(\\d+) e.g. v1.7.0") + } + + // Parse the release date releaseDate, keySet := os.LookupEnv("RELEASE_DATE") if !keySet || releaseDate == "" { - fmt.Println("RELEASE_DATE is a required environmental variable.") - fmt.Println("Refer to README.md in folder for more information.") - os.Exit(1) + return releaseDetails{}, errors.New("release date is a required environmental variable. Refer to README.md in folder for more information") } formattedReleaseDate, err := formatDate(releaseDate) if err != nil { - fmt.Println("Unable to parse the date.", err) - fmt.Println("Refer to README.md in folder for more information.") + return releaseDetails{}, errors.New("unable to parse the date") } - releaseTag := fmt.Sprintf("v%s", releaseSemVer) - betaTag := fmt.Sprintf("v%s%s", releaseSemVer, "-beta.0") - releaseLink := fmt.Sprintf("https://github.com/kubernetes-sigs/cluster-api/tree/main/docs/release/releases/release-%s.md#timeline", releaseSemVer) + majorMinorWithoutPrefixV := fmt.Sprintf("%s.%s", major, minor) // e.g. 1.7 . Note that there is no "v" in the majorMinor + releaseTag := fmt.Sprintf("v%s.%s.%s", major, minor, patch) // e.g. v1.7.0 + betaTag := fmt.Sprintf("%s%s", releaseTag, "-beta.0") // e.g. v1.7.0-beta.0 + releaseLink := fmt.Sprintf("https://github.com/kubernetes-sigs/cluster-api/tree/main/docs/release/releases/release-%s.md#timeline", majorMinorWithoutPrefixV) + releaseNotesLink := fmt.Sprintf("https://github.com/kubernetes-sigs/cluster-api/releases/tag/%s", betaTag) return releaseDetails{ - ReleaseDate: formattedReleaseDate, - ReleaseTag: releaseTag, - BetaTag: betaTag, - ReleaseLink: releaseLink, - } + ReleaseDate: formattedReleaseDate, + ReleaseTag: releaseTag, + BetaTag: betaTag, + ReleaseLink: releaseLink, + ReleaseNotesLink: releaseNotesLink, + }, nil } // formatDate takes a date in ISO format i.e "2006-01-02" and returns it in the format "Monday 2nd January 2006". @@ -324,7 +345,7 @@ Looking forward to your feedback before {{.ReleaseTag}} release! ## For quick reference -- [CAPI {{.BetaTag}} release notes](https://github.com/kubernetes-sigs/cluster-api/releases/tag/{{.BetaTag}}) +- [CAPI {{.BetaTag}} release notes]({{.ReleaseNotesLink}}) - [Shortcut to CAPI git issues](https://github.com/kubernetes-sigs/cluster-api/issues) ## Following are the planned dates for the upcoming releases diff --git a/hack/tools/release/internal/update_providers/provider_issues_test.go b/hack/tools/release/internal/update_providers/provider_issues_test.go new file mode 100644 index 000000000000..4e40aa4ed344 --- /dev/null +++ b/hack/tools/release/internal/update_providers/provider_issues_test.go @@ -0,0 +1,115 @@ +//go:build tools +// +build tools + +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "os" + "testing" + + . "github.com/onsi/gomega" +) + +func Test_GetReleaseDetails(t *testing.T) { + tests := []struct { + name string + releaseTag string + releaseDate string + want releaseDetails + expectErr bool + err string + }{ + { + name: "Correct RELEASE_TAG and RELEASE_DATE are set", + releaseTag: "v1.7.0-beta.0", + releaseDate: "2024-04-16", + want: releaseDetails{ + ReleaseDate: "Tuesday, 16th April 2024", + ReleaseTag: "v1.7.0", + BetaTag: "v1.7.0-beta.0", + ReleaseLink: "https://github.com/kubernetes-sigs/cluster-api/tree/main/docs/release/releases/release-1.7.md#timeline", + ReleaseNotesLink: "https://github.com/kubernetes-sigs/cluster-api/releases/tag/v1.7.0-beta.0", + }, + expectErr: false, + }, + { + name: "RELEASE_TAG is not in the format ^v\\d+\\.\\d+\\.\\d+-beta\\.\\d+$", + releaseTag: "v1.7.0.1", + releaseDate: "2024-04-16", + expectErr: true, + err: "release tag must be in format `^v\\d+\\.\\d+\\.\\d+-beta\\.\\d+$` e.g. v1.7.0-beta.0", + }, + { + name: "RELEASE_TAG does not have prefix 'v' in its semver", + releaseTag: "1.7.0-beta.0", + releaseDate: "2024-04-16", + expectErr: true, + err: "release tag must be in format `^v\\d+\\.\\d+\\.\\d+-beta\\.\\d+$` e.g. v1.7.0-beta.0", + }, + { + name: "RELEASE_TAG contains invalid Major.Minor.Patch SemVer", + releaseTag: "v1.x.0-beta.0", + releaseDate: "2024-04-16", + expectErr: true, + err: "release tag must be in format `^v\\d+\\.\\d+\\.\\d+-beta\\.\\d+$` e.g. v1.7.0-beta.0", + }, + { + name: "invalid yyyy-dd-mm RELEASE_DATE entered", + releaseTag: "v1.7.0-beta.0", + releaseDate: "2024-16-4", + expectErr: true, + err: "unable to parse the date", + }, + { + name: "invalid yyyy/dd/mm RELEASE_DATE entered", + releaseTag: "v1.7.0-beta.0", + releaseDate: "2024/16/4", + expectErr: true, + err: "unable to parse the date", + }, + { + name: "invalid yyyy/mm/dd RELEASE_DATE entered", + releaseTag: "v1.7.0-beta.0", + releaseDate: "2024/4/16", + expectErr: true, + err: "unable to parse the date", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) + + _ = os.Setenv("RELEASE_TAG", tt.releaseTag) + _ = os.Setenv("RELEASE_DATE", tt.releaseDate) + + got, err := getReleaseDetails() + if tt.expectErr { + g.Expect(err.Error()).To(Equal(tt.err)) + } else { + g.Expect(got.ReleaseDate).To(Equal(tt.want.ReleaseDate)) + g.Expect(got.ReleaseTag).To(Equal(tt.want.ReleaseTag)) + g.Expect(got.BetaTag).To(Equal(tt.want.BetaTag)) + g.Expect(got.ReleaseLink).To(Equal(tt.want.ReleaseLink)) + } + _ = os.Unsetenv("RELEASE_TAG") + _ = os.Unsetenv("RELEASE_DATE") + }) + } +}