diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index b2aaf69c2d3..270535db637 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -28,7 +28,7 @@ jobs: Name-Email: github-action@etcd.io Expire-Date: 0 EOF - DRY_RUN=true ./scripts/release.sh --no-upload --no-docker-push --in-place 3.6.99 + DRY_RUN=true ./scripts/release.sh --no-upload --no-docker-push --no-gh-release --in-place 3.6.99 - name: test-image run: | VERSION=3.6.99 ./scripts/test_images.sh diff --git a/Documentation/contributor-guide/release.md b/Documentation/contributor-guide/release.md index 122f266be87..6875adaf1c9 100644 --- a/Documentation/contributor-guide/release.md +++ b/Documentation/contributor-guide/release.md @@ -62,6 +62,8 @@ which don't need to be executed before releasing each version. 4. Authenticate the image registry, refer to [Authentication methods](https://cloud.google.com/container-registry/docs/advanced-authentication). - `gcloud auth login` - `gcloud auth configure-docker` +5. Install gh, refer to [GitHub's documentation](https://github.com/cli/cli#installation). Ensure that running + `gh auth login` succeeds for the GitHub account you use to contribute to etcd. ### Release steps @@ -85,13 +87,8 @@ which don't need to be executed before releasing each version. It generates all release binaries under the directory `/tmp/etcd-release-${VERSION}/etcd/release/` and images. Binaries are pushed to the Google Cloud bucket under project `etcd-development`, and images are pushed to `quay.io` and `gcr.io`. 7. Publish the release page on GitHub - - Set the release title as the version name - - Choose the correct release tag (generated from step #4) - - Follow the format of previous release pages - - Attach the generated binaries and signature file - - Verify the historical binary size for each architecture. If there's a big difference, verify that it works for that architecture - - Select whether it's a pre-release - - Publish the release + - Open the **draft** release URL shown by the release script + - Review that it looks correct, then publish the release 8. Announce to the etcd-dev googlegroup Follow the format of previous release emails sent to etcd-dev@googlegroups.com, see an example below. After sending out the email, ask one of the mailing list maintainers to approve the email from the pending list. Additionally, label the release email as `Release`. diff --git a/scripts/release.sh b/scripts/release.sh index c2940a1fcd1..d2aeb25fc96 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -33,15 +33,15 @@ help() { echo "WARNING: This does not perform the 'Add API capabilities', 'Performance testing' " echo " or 'Documentation' steps. These steps must be performed manually BEFORE running this tool." echo "" - echo "WARNING: This script does not sign releases, publish releases to github or sent announcement" - echo " emails. These steps must be performed manually AFTER running this tool." + echo "WARNING: This script does not send announcement emails. This step must be performed manually AFTER running this tool." echo "" echo " args:" echo " version: version of etcd to release, e.g. 'v3.2.18'" echo " flags:" - echo " --no-upload: skip gs://etcd binary artifact uploads." - echo " --no-docker-push: skip docker image pushes." echo " --in-place: build binaries using current branch." + echo " --no-docker-push: skip docker image pushes." + echo " --no-gh-release: skip creating the GitHub release using gh." + echo " --no-upload: skip gs://etcd binary artifact uploads." echo "" echo "One can perform a (dry-run) test release from any (uncommitted) branch using:" echo " DRY_RUN=true REPOSITORY=\`pwd\` BRANCH='local-branch-name' ./scripts/release 3.5.0-foobar.2" @@ -119,6 +119,21 @@ main() { exit 1 fi + if [ "${NO_GH_RELEASE}" == 1 ]; then + log_callout "Skipping gh verification, --no-gh-release is set" + else + # Check that gh is installed and logged in. + log_callout "Check gh installation" + if ! command -v gh >/dev/null; then + log_error "Cannot find gh. Please follow the installation instructions at https://github.com/cli/cli#installation" + exit 1 + fi + if ! gh auth status &>/dev/null; then + log_error "GitHub authentication failed for gh. Please run gh auth login." + exit 1 + fi + fi + # If the release tag does not already exist remotely, create it. log_callout "Create tag if not present" if [ "${remote_tag_exists}" -eq 0 ]; then @@ -314,10 +329,70 @@ main() { exit 1 fi - # TODO: signing process - log_warning "" - log_warning "WARNING: The release has not been signed and published to github. This must be done manually." - log_warning "" + if [ "${DRY_RUN}" == "true" ] || [ "${NO_GH_RELEASE}" == 1 ]; then + log_warning "" + log_warning "WARNING: Skipping creating GitHub release, --no-gh-release is set." + log_warning "WARNING: If not running on DRY_MODE, please do the GitHub release manually." + log_warning "" + else + local gh_repo + local release_notes_temp_file + local release_url + local gh_release_args=() + + # For the main branch (v3.6), we should mark the release as a prerelease. + # The release-3.5 (v3.5) branch, should be marked as latest. And release-3.4 (v3.4) + # should be left without any additional mark (therefore, it doesn't need a special argument). + if [ "${BRANCH}" = "main" ]; then + gh_release_args=(--prerelease) + elif [ "${BRANCH}" = "release-3.5" ]; then + gh_release_args=(--latest) + fi + + if [ "${REPOSITORY}" = "$(pwd)" ]; then + gh_repo=$(git remote get-url origin) + else + gh_repo="${REPOSITORY}" + fi + + gh_repo=$(echo "${gh_repo}" | sed 's/^[^@]\+@//' | sed 's/https\?:\/\///' | sed 's/\.git$//' | tr ':' '/') + log_callout "Creating GitHub release for ${RELEASE_VERSION} on ${gh_repo}" + + release_notes_temp_file=$(mktemp) + + local release_version=${RELEASE_VERSION#v} # Remove the v prefix from the release version (i.e., v3.6.1 -> 3.6.1) + local release_version_major_minor=${release_version%.*} # Remove the patch from the version (i.e., 3.6) + local release_version_major=${release_version_major_minor%.*} # Extract the major (i.e., 3) + local release_version_minor=${release_version_major_minor/*./} # Extract the minor (i.e., 6) + + # Disable sellcheck SC2016, the single quoted syntax for sed is intentional. + # shellcheck disable=SC2016 + sed 's/${RELEASE_VERSION}/'"${RELEASE_VERSION}"'/g' ./scripts/release_notes.tpl.txt | + sed 's/${RELEASE_VERSION_MAJOR_MINOR}/'"${release_version_major_minor}"'/g' | + sed 's/${RELEASE_VERSION_MAJOR}/'"${release_version_major}"'/g' | + sed 's/${RELEASE_VERSION_MINOR}/'"${release_version_minor}"'/g' > "${release_notes_temp_file}" + + if ! gh --repo "${gh_repo}" release view "${RELEASE_VERSION}" &>/dev/null; then + maybe_run gh release create "${RELEASE_VERSION}" \ + --repo "${gh_repo}" \ + --draft \ + --title "${RELEASE_VERSION}" \ + --notes-file "${release_notes_temp_file}" \ + "${gh_release_args[@]}" + fi + + # Upload files one by one, as gh doesn't support passing globs as input. + maybe_run find ./release '(' -name '*.tar.gz' -o -name '*.zip' ')' -exec \ + gh --repo "${gh_repo}" release upload "${RELEASE_VERSION}" {} --clobber \; + maybe_run gh --repo "${gh_repo}" release upload "${RELEASE_VERSION}" ./release/SHA256SUMS --clobber + + release_url=$(gh --repo "${gh_repo}" release view "${RELEASE_VERSION}" --json url --jq '.url') + + log_warning "" + log_warning "WARNING: The GitHub release for ${RELEASE_VERSION} has been created as a draft, please go to ${release_url} and release it." + log_warning "" + fi + log_success "Success." exit 0 } @@ -326,6 +401,7 @@ POSITIONAL=() NO_UPLOAD=0 NO_DOCKER_PUSH=0 IN_PLACE=0 +NO_GH_RELEASE=0 while test $# -gt 0; do case "$1" in @@ -346,6 +422,10 @@ while test $# -gt 0; do NO_DOCKER_PUSH=1 shift ;; + --no-gh-release) + NO_GH_RELEASE=1 + shift + ;; *) POSITIONAL+=("$1") # save it in an array for later shift # past argument diff --git a/scripts/release_notes.tpl.txt b/scripts/release_notes.tpl.txt new file mode 100644 index 00000000000..1038a504d6f --- /dev/null +++ b/scripts/release_notes.tpl.txt @@ -0,0 +1,91 @@ +Please check out [CHANGELOG](https://github.com/etcd-io/etcd/blob/main/CHANGELOG/CHANGELOG-${RELEASE_VERSION_MAJOR_MINOR}.md) for a full list of changes. And make sure to read [upgrade guide](https://etcd.io/docs/v${RELEASE_VERSION_MAJOR_MINOR}/upgrades/upgrade_${RELEASE_VERSION_MAJOR}_${RELEASE_VERSION_MINOR}/) before upgrading etcd (there may be breaking changes). + +For installation guides, please check out [play.etcd.io](http://play.etcd.io) and [operating etcd](https://etcd.io/docs/v${RELEASE_VERSION_MAJOR_MINOR}/op-guide/). Latest support status for common architectures and operating systems can be found at [supported platforms](https://etcd.io/docs/v${RELEASE_VERSION_MAJOR_MINOR}/op-guide/supported-platform/). + +###### Linux + +```sh +ETCD_VER=${RELEASE_VERSION} + +# choose either URL +GOOGLE_URL=https://storage.googleapis.com/etcd +GITHUB_URL=https://github.com/etcd-io/etcd/releases/download +DOWNLOAD_URL=${GOOGLE_URL} + +rm -f /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz +rm -rf /tmp/etcd-download-test && mkdir -p /tmp/etcd-download-test + +curl -L ${DOWNLOAD_URL}/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gz -o /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz +tar xzvf /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz -C /tmp/etcd-download-test --strip-components=1 +rm -f /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz + +/tmp/etcd-download-test/etcd --version +/tmp/etcd-download-test/etcdctl version +/tmp/etcd-download-test/etcdutl version + +# start a local etcd server +/tmp/etcd-download-test/etcd + +# write,read to etcd +/tmp/etcd-download-test/etcdctl --endpoints=localhost:2379 put foo bar +/tmp/etcd-download-test/etcdctl --endpoints=localhost:2379 get foo +``` + +###### macOS (Darwin) + +```sh +ETCD_VER=${RELEASE_VERSION} + +# choose either URL +GOOGLE_URL=https://storage.googleapis.com/etcd +GITHUB_URL=https://github.com/etcd-io/etcd/releases/download +DOWNLOAD_URL=${GOOGLE_URL} + +rm -f /tmp/etcd-${ETCD_VER}-darwin-amd64.zip +rm -rf /tmp/etcd-download-test && mkdir -p /tmp/etcd-download-test + +curl -L ${DOWNLOAD_URL}/${ETCD_VER}/etcd-${ETCD_VER}-darwin-amd64.zip -o /tmp/etcd-${ETCD_VER}-darwin-amd64.zip +unzip /tmp/etcd-${ETCD_VER}-darwin-amd64.zip -d /tmp && rm -f /tmp/etcd-${ETCD_VER}-darwin-amd64.zip +mv /tmp/etcd-${ETCD_VER}-darwin-amd64/* /tmp/etcd-download-test && rm -rf mv /tmp/etcd-${ETCD_VER}-darwin-amd64 + +/tmp/etcd-download-test/etcd --version +/tmp/etcd-download-test/etcdctl version +/tmp/etcd-download-test/etcdutl version +``` + +###### Docker + +etcd uses [`gcr.io/etcd-development/etcd`](https://gcr.io/etcd-development/etcd) as a primary container registry, and [`quay.io/coreos/etcd`](https://quay.io/coreos/etcd) as secondary. + +```sh +ETCD_VER=${RELEASE_VERSION} + +rm -rf /tmp/etcd-data.tmp && mkdir -p /tmp/etcd-data.tmp && \ + docker rmi gcr.io/etcd-development/etcd:${ETCD_VER} || true && \ + docker run \ + -p 2379:2379 \ + -p 2380:2380 \ + --mount type=bind,source=/tmp/etcd-data.tmp,destination=/etcd-data \ + --name etcd-gcr-${ETCD_VER} \ + gcr.io/etcd-development/etcd:${ETCD_VER} \ + /usr/local/bin/etcd \ + --name s1 \ + --data-dir /etcd-data \ + --listen-client-urls http://0.0.0.0:2379 \ + --advertise-client-urls http://0.0.0.0:2379 \ + --listen-peer-urls http://0.0.0.0:2380 \ + --initial-advertise-peer-urls http://0.0.0.0:2380 \ + --initial-cluster s1=http://0.0.0.0:2380 \ + --initial-cluster-token tkn \ + --initial-cluster-state new \ + --log-level info \ + --logger zap \ + --log-outputs stderr + +docker exec etcd-gcr-${ETCD_VER}/usr/local/bin/etcd --version +docker exec etcd-gcr-${ETCD_VER}/usr/local/bin/etcdctl version +docker exec etcd-gcr-${ETCD_VER}/usr/local/bin/etcdutl version +docker exec etcd-gcr-${ETCD_VER}/usr/local/bin/etcdctl endpoint health +docker exec etcd-gcr-${ETCD_VER}/usr/local/bin/etcdctl put foo bar +docker exec etcd-gcr-${ETCD_VER}/usr/local/bin/etcdctl get foo +```