From ef3505c69fe2b8104697b764dfcf19adb4450430 Mon Sep 17 00:00:00 2001 From: Chetan Patwal Date: Fri, 3 Jun 2022 16:44:23 +0530 Subject: [PATCH] Automate release process (#5369) --- .github/workflows/release-candidate.yaml | 2 +- .github/workflows/release.yaml | 2 +- .github/workflows/tag-release.yaml | 42 --------- .goreleaser.yml | 2 +- build/scripts/do-release-candidate.sh | 5 +- build/scripts/do-release.sh | 2 +- build/scripts/tag-common.sh | 11 ++- build/scripts/tag-release-candidate.sh | 12 ++- build/scripts/tag-release.sh | 22 +---- pkg/version/generate/release_generate.go | 90 ++++++++++++------- pkg/version/generate/release_generate_test.go | 49 ++-------- 11 files changed, 92 insertions(+), 147 deletions(-) delete mode 100644 .github/workflows/tag-release.yaml mode change 100644 => 100755 build/scripts/tag-common.sh diff --git a/.github/workflows/release-candidate.yaml b/.github/workflows/release-candidate.yaml index 55c63bf331..83351680ff 100644 --- a/.github/workflows/release-candidate.yaml +++ b/.github/workflows/release-candidate.yaml @@ -27,5 +27,5 @@ jobs: uses: ./.github/actions/setup-identity with: token: "${{ secrets.WEAVEWORKSBOT_TOKEN }}" - - name: Open PRs to release branch and default branch + - name: Push tag and open PR to default branch run: make prepare-release-candidate diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index c2b894d517..7bef95a549 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -27,5 +27,5 @@ jobs: uses: ./.github/actions/setup-identity with: token: "${{ secrets.WEAVEWORKSBOT_TOKEN }}" - - name: Open PRs to release branch and default branch + - name: Push tag and open PR to default branch run: make prepare-release diff --git a/.github/workflows/tag-release.yaml b/.github/workflows/tag-release.yaml deleted file mode 100644 index 20284ea6f4..0000000000 --- a/.github/workflows/tag-release.yaml +++ /dev/null @@ -1,42 +0,0 @@ -name: Tag release -# This workflow watches release branches for PRs merged with a certain label -# and commits with the label in their commit message and then creates a tag -# for the version eksctl reports at that commit - -on: - pull_request: - types: [closed] - push: - branches: - - release-[0-9]+.[0-9]+ - -jobs: - tag: - if: | - contains(github.event.pull_request.labels.*.name, '/trigger-release') && github.event.pull_request.merge_commit_sha != null - || contains(github.event.head_commit.message, '/trigger-release') - name: Tag release from version - environment: release - runs-on: ubuntu-latest - container: weaveworks/eksctl-build:0eb3a29941b9740f055ecb3fd9b663762a7ee9bd - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - token: ${{ secrets.WEAVEWORKSBOT_TOKEN }} - fetch-depth: 0 - - name: Cache go-build and mod - uses: actions/cache@v3 - with: - path: | - ~/.cache/go-build/ - ~/go/pkg/mod/ - key: go-${{ hashFiles('go.sum') }} - restore-keys: | - go- - - name: Tag from version - run: | - version=$(go run pkg/version/generate/release_generate.go full-version) - git tag "${version}" - git tag "v${version}" - git push origin "${version}" "v${version}" diff --git a/.goreleaser.yml b/.goreleaser.yml index 0ea4afe664..c526fea5a6 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -14,7 +14,7 @@ builds: - CGO_ENABLED=0 ldflags: # gitTag set from a generated file (see ./tag_release.sh) - - -s -w -X github.com/weaveworks/eksctl/pkg/version.buildDate={{.Date}} -X github.com/weaveworks/eksctl/pkg/version.gitCommit={{.ShortCommit}} + - -s -w -X github.com/weaveworks/eksctl/pkg/version.buildDate={{.Date}} -X github.com/weaveworks/eksctl/pkg/version.gitCommit={{.ShortCommit}} -X github.com/weaveworks/eksctl/pkg/version.PreReleaseID={{.Env.PRE_RELEASE_ID}} goos: - windows - darwin diff --git a/build/scripts/do-release-candidate.sh b/build/scripts/do-release-candidate.sh index b04b5f474b..5ca780db63 100755 --- a/build/scripts/do-release-candidate.sh +++ b/build/scripts/do-release-candidate.sh @@ -13,6 +13,9 @@ if [ ! -f "${RELEASE_NOTES_FILE}" ]; then exit 1 fi +# Update eksctl version to release-candidate +pre_release_id="${tag#*-}" + export RELEASE_DESCRIPTION="${tag}" -GORELEASER_CURRENT_TAG=v${tag} goreleaser release --rm-dist --timeout 60m --skip-validate --config=./.goreleaser.yml --release-notes="${RELEASE_NOTES_FILE}" +GORELEASER_CURRENT_TAG="v${tag}" PRE_RELEASE_ID="${pre_release_id}" goreleaser release --rm-dist --timeout 60m --skip-validate --config=./.goreleaser.yml --release-notes="${RELEASE_NOTES_FILE}" diff --git a/build/scripts/do-release.sh b/build/scripts/do-release.sh index d0e7c4955c..b7768a418f 100755 --- a/build/scripts/do-release.sh +++ b/build/scripts/do-release.sh @@ -16,4 +16,4 @@ if [ ! -f "${RELEASE_NOTES_FILE}" ]; then fi cat ./.goreleaser.yml ./.goreleaser.brew.yml > .goreleaser.brew.combined.yml -GORELEASER_CURRENT_TAG=v${tag} goreleaser release --rm-dist --timeout 60m --skip-validate --config=./.goreleaser.brew.combined.yml --release-notes="${RELEASE_NOTES_FILE}" +GORELEASER_CURRENT_TAG=v${tag} PRE_RELEASE_ID="" goreleaser release --rm-dist --timeout 60m --skip-validate --config=./.goreleaser.brew.combined.yml --release-notes="${RELEASE_NOTES_FILE}" diff --git a/build/scripts/tag-common.sh b/build/scripts/tag-common.sh old mode 100644 new mode 100755 index 480a491624..1b366d5c4d --- a/build/scripts/tag-common.sh +++ b/build/scripts/tag-common.sh @@ -20,7 +20,7 @@ function current_branch() { } function release_generate() { - go run pkg/version/generate/release_generate.go "${1}" + go run pkg/version/generate/release_generate.go "${1}" ${2:+"${2}"} } function check_origin() { @@ -63,6 +63,15 @@ function commit() { git commit --message "${commit_msg}" } +function tag_and_push_release() { + local version="${1}" + local msg="${2}" + for tag in "${version}" "v${version}"; do + git tag --annotate --message "${msg}" "${tag}" + git push origin "${tag}" + done +} + function tag_version_and_latest() { echo "Tagging new version and latest_release" local commit_msg=$1 diff --git a/build/scripts/tag-release-candidate.sh b/build/scripts/tag-release-candidate.sh index 6e779d9c68..f2e4799eee 100755 --- a/build/scripts/tag-release-candidate.sh +++ b/build/scripts/tag-release-candidate.sh @@ -36,15 +36,13 @@ check_current_branch "${release_branch}" ensure_up_to_date "${release_branch}" || echo "${release_branch} not found in origin, will push new branch upstream." # Update eksctl version to release-candidate -rc_version=$(release_generate release-candidate) -m="Tag ${rc_version} release candidate" +latest_reachable_tag=$(git describe --tags --abbrev=0) +pre_release_id=$(release_generate next-pre-release-id "${latest_reachable_tag}") +full_version="${candidate_for_version}-${pre_release_id}" +m="Tag ${full_version} release candidate" -commit "${m}" "${release_notes_file}" -tag_version_and_latest "${m}" "${rc_version}" - -# Make PR to release branch -make_pr "${release_branch}" +tag_and_push_release "${full_version}" "${m}" # Make PR to update default branch if necessary git checkout "${default_branch}" diff --git a/build/scripts/tag-release.sh b/build/scripts/tag-release.sh index 94edee63fd..c6fa68c089 100755 --- a/build/scripts/tag-release.sh +++ b/build/scripts/tag-release.sh @@ -5,30 +5,14 @@ DIR="${BASH_SOURCE%/*}" # shellcheck source=tag-common.sh source "${DIR}/tag-common.sh" -release_branch=$(release_branch) - check_prereqs check_origin -git checkout "${default_branch}" -check_current_branch "${default_branch}" -ensure_up_to_date "${default_branch}" - -git checkout "${release_branch}" -check_current_branch "${release_branch}" -ensure_up_to_date "${release_branch}" - -# Update eksctl version by removing the pre-release id -release_version=$(release_generate release) +release_version=$(release_generate print-version) release_notes_file=$(ensure_release_notes "${release_version}") -m="Release ${release_version}" - -commit "${m}" "${release_notes_file}" - -tag_version_and_latest "${m}" "${release_version}" - -make_pr "${release_branch}" +msg="Release ${release_version}" +tag_and_push_release "${release_version}" "${msg}" # Make PR to update default branch if necessary git checkout "${default_branch}" diff --git a/pkg/version/generate/release_generate.go b/pkg/version/generate/release_generate.go index 4472df4e6c..bf40c68c00 100644 --- a/pkg/version/generate/release_generate.go +++ b/pkg/version/generate/release_generate.go @@ -4,10 +4,10 @@ package main import ( + "errors" "fmt" "log" "os" - "strconv" "strings" "github.com/blang/semver" @@ -16,9 +16,11 @@ import ( "github.com/weaveworks/eksctl/pkg/version" ) -const versionFilename = "pkg/version/release.go" -const defaultPreReleaseID = "dev" -const defaultReleaseCandidate = "rc.0" +const ( + versionFilename = "pkg/version/release.go" + defaultPreReleaseID = "dev" + defaultReleaseCandidate = "rc.0" +) func main() { if len(os.Args) < 2 { @@ -27,52 +29,74 @@ func main() { command := os.Args[1] - var newVersion, newPreRelease string switch command { - case "release": - newVersion, newPreRelease = prepareRelease() - case "release-candidate": - newVersion, newPreRelease = prepareReleaseCandidate() case "development": - newVersion, newPreRelease = nextDevelopmentIteration() + newVersion, newPreRelease := nextDevelopmentIteration() + if err := writeVersionToFile(newVersion, newPreRelease, versionFilename); err != nil { + log.Fatalf("unable to write file: %v", err) + } + + version.Version = newVersion + version.PreReleaseID = newPreRelease + fmt.Println(version.GetVersion()) + case "next-pre-release-id": + switch len(os.Args) { + case 2: + fmt.Println(defaultReleaseCandidate) + case 3: + next, err := nextPreReleaseID(os.Args[2]) + if err != nil { + log.Fatalf("error generating next pre-release ID: %v", err) + } + fmt.Println(next) + default: + log.Fatalf("usage: release_generate %s ", command) + } + case "full-version": fmt.Println(version.GetVersion()) - return case "print-version": // Print simplified version X.Y.Z fmt.Println(version.Version) - return case "print-major-minor-version": fmt.Println(printMajorMinor()) - return default: - log.Fatalf("unknown option %q. Expected 'release', 'release-candidate', 'development', 'print-version' or 'print-major-minor-version'", command) + log.Fatalf("unknown option %q. Expected one of %v", command, strings.Join([]string{"development", "next-pre-release-id", "full-version", "print-version", "print-major-minor-version"}, ", ")) } - if err := writeVersionToFile(newVersion, newPreRelease, versionFilename); err != nil { - log.Fatalf("unable to write file: %s", err.Error()) +} + +func nextPreReleaseID(latestPreReleaseVersion string) (string, error) { + if latestPreReleaseVersion == "" { + return defaultReleaseCandidate, nil } - version.Version = newVersion - version.PreReleaseID = newPreRelease - fmt.Println(version.GetVersion()) -} + latestPreReleaseVersion = strings.TrimPrefix(latestPreReleaseVersion, "v") + ver, err := semver.Parse(latestPreReleaseVersion) + if err != nil { + return "", fmt.Errorf("invalid pre-release version: %w", err) + } + currentVersion, err := semver.Parse(version.Version) + if err != nil { + return "", fmt.Errorf("unexpected error parsing current version: %s: %w", version.Version, err) + } -func prepareRelease() (string, string) { - return version.Version, "" -} + verWithoutPre := ver + verWithoutPre.Pre = nil + if verWithoutPre.LT(currentVersion) || len(ver.Pre) == 0 { + return defaultReleaseCandidate, nil + } -func prepareReleaseCandidate() (string, string) { - if strings.HasPrefix(version.PreReleaseID, "rc.") { - // Next RC - rcNumber, err := strconv.Atoi(strings.TrimPrefix(version.PreReleaseID, "rc.")) - if err != nil { - log.Fatalf("cannot parse rc version from pre-release id %s", version.PreReleaseID) - } - newRC := rcNumber + 1 - return version.Version, fmt.Sprintf("rc.%d", newRC) + if len(ver.Pre) != 2 { + return "", errors.New("unexpected format for PR version") + } + id := ver.Pre[1] + if !id.IsNumeric() { + return "", fmt.Errorf("expected PR version to be numeric; got %q", id.String()) } - return version.Version, defaultReleaseCandidate + + return fmt.Sprintf("rc.%d", id.VersionNum+1), nil + } func printMajorMinor() string { diff --git a/pkg/version/generate/release_generate_test.go b/pkg/version/generate/release_generate_test.go index 7667c69d21..6176aa51af 100644 --- a/pkg/version/generate/release_generate_test.go +++ b/pkg/version/generate/release_generate_test.go @@ -16,21 +16,6 @@ var _ = Describe("release tests", func() { version.PreReleaseID = "dev" }) - It("produces a release without a pre-release id", func() { - v, p := prepareRelease() - - Expect(v).To(Equal("0.5.0")) - Expect(p).To(BeEmpty()) - }) - - It("produces the correct release for 2 digit minor versions", func() { - version.Version = "0.25.0" - v, p := prepareRelease() - - Expect(v).To(Equal("0.25.0")) - Expect(p).To(BeEmpty()) - }) - It("increases minor version for the next development iteration from a release", func() { version.PreReleaseID = "" @@ -49,30 +34,14 @@ var _ = Describe("release tests", func() { Expect(p).To(Equal("dev")) }) - It("produces the correct default release candidate from dev", func() { - version.PreReleaseID = "dev" - - v, p := prepareReleaseCandidate() - - Expect(v).To(Equal("0.5.0")) - Expect(p).To(Equal("rc.0")) - }) - It("produces the correct default release candidate from release", func() { - version.PreReleaseID = "" - - v, p := prepareReleaseCandidate() - - Expect(v).To(Equal("0.5.0")) - Expect(p).To(Equal("rc.0")) - }) - - It("produces next release candidate", func() { - version.PreReleaseID = "rc.1" - - v, p := prepareReleaseCandidate() - - Expect(v).To(Equal("0.5.0")) - Expect(p).To(Equal("rc.2")) - }) + DescribeTable("gets the next pre-release ID", func(releaseVersion, expectedPreReleaseID string) { + preReleaseID, err := nextPreReleaseID(releaseVersion) + Expect(err).NotTo(HaveOccurred()) + Expect(preReleaseID).To(Equal(expectedPreReleaseID)) + }, + Entry("no previous RC", "", "rc.0"), + Entry("rc.0 exists", "0.100.0-rc.0", "rc.1"), + Entry("rc.5 exists", "v0.100.0-rc.5", "rc.6"), + ) })