Skip to content

Commit

Permalink
add tests and update provider_issues.go
Browse files Browse the repository at this point in the history
  • Loading branch information
nawazkh committed Mar 12, 2024
1 parent 095cee5 commit b8cace8
Show file tree
Hide file tree
Showing 3 changed files with 194 additions and 23 deletions.
6 changes: 3 additions & 3 deletions hack/tools/release/internal/update_providers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
86 changes: 66 additions & 20 deletions hack/tools/release/internal/update_providers/provider_issues.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,28 @@ var (
}
)

// Exiter is a simple interface to exit the program.
type Exiter interface {
Exit(code int)
}

// RealExiter is the real implementation of the Exiter interface.
type RealExiter struct{}

// Exit exits the program with the given code.
func (re *RealExiter) Exit(code int) {
os.Exit(code)
}

// ProviderIssues is a struct that contains the Exiter interface.
type ProviderIssues struct {
Exiter Exiter
}

func newProviderIssues() *ProviderIssues {
return &ProviderIssues{Exiter: &RealExiter{}}
}

// Issue is the struct for the issue.
type Issue struct {
Title string `json:"title"`
Expand All @@ -72,15 +94,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 == "" {
Expand Down Expand Up @@ -108,7 +131,9 @@ func main() {
fmt.Println("-", strings.Join(repoList, "\n- "))
fmt.Printf("\n")

details := getReleaseDetails()
// get release details
m := newProviderIssues()
details := m.getReleaseDetails()

// generate title
titleBuffer := bytes.NewBuffer([]byte{})
Expand Down Expand Up @@ -249,42 +274,63 @@ func continueOrAbort() {
}

// getReleaseDetails returns the release details from the environment variables.
func getReleaseDetails() releaseDetails {
func (m *ProviderIssues) getReleaseDetails() releaseDetails {
// 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)
m.Exiter.Exit(1)
}

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)
fmt.Println("RELEASE_TAG must be in format `^v\\d+\\.\\d+\\.\\d+-beta\\.\\d+$` e.g. v1.7.0-beta.0")
m.Exiter.Exit(1)
}

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 {
fmt.Println("RELEASE_TAG contains invalid Major.Minor.Patch SemVer. It must be in format v(\\d+)\\.(\\d+)\\.(\\d+) e.g. v1.7.0")
m.Exiter.Exit(1)
}

// 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)
m.Exiter.Exit(1)
}

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.")
m.Exiter.Exit(1)
}

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,
}
}

Expand Down Expand Up @@ -324,7 +370,7 @@ Looking forward to your feedback before {{.ReleaseTag}} release!
## For quick reference
<!-- body -->
- [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
Expand Down
125 changes: 125 additions & 0 deletions hack/tools/release/internal/update_providers/provider_issues_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
//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"
)

// MockExiter is a mock implementation of the Exiter interface.
type MockExiter struct {
ExitCalled bool
ExitCode int
}

// Exit is a mock implementation of the Exiter interface.
func (me *MockExiter) Exit(code int) {
me.ExitCalled = true
me.ExitCode = code
}

func Test_GetReleaseDetails(t *testing.T) {
tests := []struct {
name string
releaseTag string
releaseDate string
want releaseDetails
expectExit bool
}{
{
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",
},
expectExit: false,
},
{
name: "RELEASE_TAG is not in the format ^v\\d+\\.\\d+\\.\\d+-beta\\.\\d+$",
releaseTag: "v1.7.0.1",
releaseDate: "2024-04-16",
expectExit: true,
},
{
name: "RELEASE_TAG does not have prefix 'v' in its semver",
releaseTag: "1.7.0-beta.0",
releaseDate: "2024-04-16",
expectExit: true,
},
{
name: "RELEASE_TAG contains invalid Major.Minor.Patch SemVer",
releaseTag: "v1.x.0-beta.0",
releaseDate: "2024-04-16",
expectExit: true,
},
{
name: "invalid yyyy-dd-mm RELEASE_DATE entered",
releaseTag: "v1.7.0-beta.0",
releaseDate: "2024-16-4",
expectExit: true,
},
{
name: "invalid yyyy/dd/mm RELEASE_DATE entered",
releaseTag: "v1.7.0-beta.0",
releaseDate: "2024/16/4",
expectExit: true,
},
{
name: "invalid yyyy/mm/dd RELEASE_DATE entered",
releaseTag: "v1.7.0-beta.0",
releaseDate: "2024/4/16",
expectExit: true,
},
}

for _, tt := range tests {
mockExiter := &MockExiter{}
m := &ProviderIssues{Exiter: mockExiter}

t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t)

_ = os.Setenv("RELEASE_TAG", tt.releaseTag)
_ = os.Setenv("RELEASE_DATE", tt.releaseDate)

got := m.getReleaseDetails()
if tt.expectExit {
g.Expect(mockExiter.ExitCalled).To(BeTrue())
g.Expect(mockExiter.ExitCode).To(Equal(1))
} else {
g.Expect(mockExiter.ExitCalled).To(BeFalse())
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")
})
}
}

0 comments on commit b8cace8

Please sign in to comment.