Add scripts for tagging Velero releases (#2592)

* Add release tools

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>

* Document the tag-release release tool

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>

* Make sure the upstream used is correct

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>

* Add copyright statement

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>

* Address review feedback

* Pause to allow for cherry-picking on the release branch before pushing
  it
* Move master branch logic into an else statement
* Correct typo

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>

* Uncomment check for dirty git working tree

Signed-off-by: Nolan Brubaker <brubakern@vmware.com>
pull/2320/head
Nolan Brubaker 2020-07-08 17:56:18 -04:00 committed by GitHub
parent 695715ff6a
commit c8f4b60b5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 266 additions and 12 deletions

View File

@ -0,0 +1,75 @@
/*
Copyright 2020 the Velero contributors.
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 (
"fmt"
"os"
"regexp"
)
// This regex should match both our GA format (example: v1.4.3) and pre-release format (v1.2.4-beta.2)
// The following sub-capture groups are defined:
// major
// minor
// patch
// prerelease (this will be alpha/beta followed by a ".", followed by 1 or more digits (alpha.5)
var release_regex *regexp.Regexp = regexp.MustCompile("^v(?P<major>[[:digit:]]+)\\.(?P<minor>[[:digit:]]+)\\.(?P<patch>[[:digit:]]+)(-{1}(?P<prerelease>(alpha|beta)\\.[[:digit:]]+))*")
// This small program exists because checking the VELERO_VERSION rules in bash is difficult, and difficult to test for correctness.
// Calling it with --verify will verify whether or not the VELERO_VERSION environment variable is a valid version string, without parsing for its components.
// Calling it without --verify will try to parse the version into its component pieces.
func main() {
velero_version := os.Getenv("VELERO_VERSION")
submatches := reSubMatchMap(release_regex, velero_version)
// Didn't match the regex, exit.
if len(submatches) == 0 {
fmt.Printf("VELERO_VERSION of %s was not valid. Please correct the value and retry.", velero_version)
os.Exit(1)
}
if len(os.Args) > 1 && os.Args[1] == "--verify" {
os.Exit(0)
}
// Send these in a bash variable format to stdout, so that they can be consumed by bash scripts that call the go program.
fmt.Printf("VELERO_MAJOR=%s\n", submatches["major"])
fmt.Printf("VELERO_MINOR=%s\n", submatches["minor"])
fmt.Printf("VELERO_PATCH=%s\n", submatches["patch"])
fmt.Printf("VELERO_PRERELEASE=%s\n", submatches["prerelease"])
}
// reSubMatchMap returns a map with the named submatches within a regular expression populated as keys, and their matched values within a given string as values.
// If no matches are found, a nil map is returned
func reSubMatchMap(r *regexp.Regexp, s string) map[string]string {
match := r.FindStringSubmatch(s)
submatches := make(map[string]string)
if len(match) == 0 {
return submatches
}
for i, name := range r.SubexpNames() {
// 0 will always be empty from the return values of SubexpNames's documentation, so skip it.
if i != 0 {
submatches[name] = match[i]
}
}
return submatches
}

View File

@ -0,0 +1,71 @@
/*
Copyright 2020 the Velero contributors.
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 (
"fmt"
"testing"
)
func TestRegexMatching(t *testing.T) {
tests := []struct {
version string
expectMatch bool
}{
{
version: "v1.4.0",
expectMatch: true,
},
{
version: "v2.0.0",
expectMatch: true,
},
{
version: "v1.5.0-alpha.1",
expectMatch: true,
},
{
version: "v1.16.1320-beta.14",
expectMatch: true,
},
{
version: "1.0.0",
expectMatch: false,
},
{
// this is true because while the "--" is invalid, v1.0.0 is a valid part of the regex
version: "v1.0.0--beta.1",
expectMatch: true,
},
}
for _, test := range tests {
name := fmt.Sprintf("Testing version string %s", test.version)
t.Run(name, func(t *testing.T) {
results := reSubMatchMap(release_regex, test.version)
if len(results) == 0 && test.expectMatch {
t.Fail()
}
if len(results) > 0 && !test.expectMatch {
fmt.Printf("%v", results)
t.Fail()
}
})
}
}

118
hack/release-tools/tag-release.sh Executable file
View File

@ -0,0 +1,118 @@
#!/usr/bin/env bash
# Copyright 2020 the Velero contributors.
#
# 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.
# This script will do the necessary checks and actions to create a release of Velero.
# It will first validate that all prerequisites are met, then verify the version string is what the user expects.
# A git tag will be created and pushed to GitHub, and GoReleaser will be invoked.
# This script is meant to be a combination of documentation and executable.
# If you have questions at any point, please stop and ask!
# Directory in which the script itself resides, so we can use it for calling programs that are in the same directory.
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
function tag_and_push() {
echo "Tagging and pushing $VELERO_VERSION"
git tag $VELERO_VERSION
git push $VELERO_VERSION
}
# For now, have the person doing the release pass in the VELERO_VERSION variable as an environment variable.
# In the future, we might be able to inspect git via `git describe --abbrev=0` to get a hint for it.
if [[ -z "$VELERO_VERSION" ]]; then
printf "The \$VELERO_VERSION environment variable is not set. Please set it with\n\texport VELERO_VERSION=v<version.to.release>\nthen try again."
exit 1
fi
# Make sure the user's provided their github token, so we can give it to goreleaser.
if [[ -z "$GITHUB_TOKEN" ]]; then
printf "The GITHUB_TOKEN environment variable is not set. Please set it with\n\t export GITHUB_TOKEN=<your github token>\n then try again."
exit 1
fi
Ensure that we have a clean working tree before we let any changes happen, especially important for cutting release branches.
if [[ -n $(git status --short) ]]; then
echo "Your git working directory is dirty! Please clean up untracked files and stash any changes before proceeding."
exit 3
fi
# Make sure that there's no issue with the environment variable's format before trying to eval the parsed version.
if ! go run $DIR/chk_version.go --verify; then
exit 2
fi
# Since we're past the validation of the VELERO_VERSION, parse the version's individual components.
eval $(go run $DIR/chk_version.go)
printf "To clarify, you've provided a version string of $VELERO_VERSION.\n"
printf "Based on this, the following assumptions have been made: \n"
[[ "$VELERO_PATCH" != 0 ]] && printf "*\t This is a patch release.\n"
# -n is "string is non-empty"
[[ -n $VELERO_PRERELEASE ]] && printf "*\t This is a pre-release.\n"
# -z is "string is empty"
[[ -z $VELERO_PRERELEASE ]] && printf "*\t This is a GA release.\n"
echo "If this is all correct, press enter/return to proceed to TAG THE RELEASE and UPLOAD THE TAG TO GITHUB."
echo "Otherwise, press ctrl-c to CANCEL the process without making any changes."
read -p "Ready to continue? "
echo "Alright, let's go."
echo "Pulling down all git tags and branches before doing any work."
git fetch upstream --all --tags
# If we've got a patch release, we'll need to create a release branch for it.
if [[ "$VELERO_PATCH" > 0 ]]; then
release_branch_name=release-$VELERO_MAJOR.$VELERO_MINOR
# Check if the branch exists, creating it if not.
# The fetch command above should have gotten all the upstream branches, so we can safely assume this check is local & upstream branches.
if [[ -z $(git branch | grep $release_branch_name) ]]; then
git checkout -b $release_branch_name
echo "Release branch made."
else
echo "Release branch $release_branch_name exists already."
git checkout $release_branch_name
fi
echo "Now you'll need to cherry-pick any relevant git commits into this release branch."
echo "Either pause this script with ctrl-z, or open a new terminal window and do the cherry-picking."
read -p "Press enter when you're done cherry-picking. THIS WILL MAKE A TAG PUSH THE BRANCH TO UPSTREAM"
# TODO can/should we add a way to review the cherry-picked commits before the push?
echo "Pushing $release_branch_name to upstream remote"
git push --set-upstream upstream/$release_branch_name $release_branch_name
tag_and_push
else
echo "Checking out upstream/master."
git checkout upstream/master
tag_and_push
fi
echo "Invoking Goreleaser to create the GitHub release."
RELEASE_NOTES_FILE=changelogs/CHANGELOG-$VELERO_MAJOR.$VELERO_MINOR.md \
PUBLISH=TRUE \
make release

View File

@ -54,18 +54,8 @@ You may regenerate the token for every release if you prefer.
This process is the same for both pre-release and GA, except for the fact that there will not be a blog post PR to merge for pre-release versions.
1. Merge the changelog + docs PR, so that it's included in the release tag.
1. Make sure your working directory is clean: `git status` should show `nothing to commit, working tree clean`.
1. Run `git fetch upstream master && git checkout upstream/master`.
1. Run `git tag $VELERO_VERSION (e.g. `git tag v1.2.0` or `git tag v1.2.0-beta.1`).
1. Run `git push upstream $VELERO_VERSION` (e.g. `git push upstream v1.2.0` or `git push upstream v1.2.0-beta.1`). This will trigger the github action that builds/publishes the Docker images.
1. Generate the GitHub release (it will be created in "Draft" status, which means it's not visible to the outside world until you click "Publish"):
```bash
GITHUB_TOKEN=your-github-token \
RELEASE_NOTES_FILE=changelogs/CHANGELOG-<major>.<minor>.md \
PUBLISH=true \
make release
```
1. Set your GitHub token as an environment variable. `export GITHUB_TOKEN=<your token value>`
1. Run `/hack/release-tools/tag-release.sh` and follow the instructions.
1. Navigate to the draft GitHub release, at https://github.com/vmware-tanzu/velero/releases.
1. If this is a patch release (e.g. `v1.2.1`), note that the full `CHANGELOG-1.2.md` contents will be included in the body of the GitHub release. You need to delete the previous releases' content (e.g. `v1.2.0`'s changelog) so that only the latest patch release's changelog shows.