diff --git a/scripts/README.md b/scripts/README.md index dad0e69af..cc56edfbf 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -59,6 +59,7 @@ The core `apache-hamilton` package must be released first. The other four packag |---|---| | `scripts/apache_release_helper.py` | Build artifacts, sign, tag, upload RC to SVN, generate vote email | | `scripts/promote_rc.sh` | Move a voted RC from SVN dev to SVN release | +| `scripts/tag_release.sh` | Create the final (non-RC) `-incubating` release tag on the merge commit | | `scripts/verify_apache_artifacts.py` | Verify GPG signatures, checksums, and license headers | | `scripts/verification-script.sh` | End-to-end RC validation (download, verify, build, test, examples) | | `scripts/generate_announce_email.py` | Generate vote result, announcement email, and Slack message | @@ -226,6 +227,39 @@ git commit -m "Release apache-hamilton ${VERSION}" git push origin main ``` +#### 7. Create the final (non-RC) release tag + +The RC build created an `-incubating-RC${RC}` tag pointing at the commit the +artifacts were built from (typically on the release branch). After the vote +passes and the release branch is squash-merged to main (step 6), create a +clean, RC-less release tag on the merge commit. This keeps the release tag +reachable from `main` and gives GitHub release pages a sensible name -- +ASF mentors flag `-RC`-suffixed tags as poor release names. + +`scripts/tag_release.sh` creates an annotated `-v-incubating` +tag on the target commit (default: current `HEAD`, i.e. the squash-merge +commit on main). The original `-RC` tag is left intact as the record of +exactly what was voted on. + +```bash +# On main, at the squash-merge commit from step 6: +scripts/tag_release.sh --push ${PACKAGE} ${VERSION} + +# Or tag a specific commit / hold off on pushing: +scripts/tag_release.sh --commit ${PACKAGE} ${VERSION} # creates locally +git push origin refs/tags/${PACKAGE}-v${VERSION}-incubating # push when ready +``` + +For a multi-package release (e.g. the apache-hamilton-{sdk,lsp,ui,contrib} +sub-packages), run it once per package against the same merge commit: + +```bash +scripts/tag_release.sh --push apache-hamilton-sdk 0.9.0 +scripts/tag_release.sh --push apache-hamilton-lsp 0.2.0 +scripts/tag_release.sh --push apache-hamilton-ui 0.0.18 +scripts/tag_release.sh --push apache-hamilton-contrib 0.0.9 +``` + # For Voters: Verifying a Release If you're voting on a release, you can either use the automated script or follow the diff --git a/scripts/tag_release.sh b/scripts/tag_release.sh new file mode 100755 index 000000000..857c5ff84 --- /dev/null +++ b/scripts/tag_release.sh @@ -0,0 +1,135 @@ +#!/usr/bin/env bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +# Create the final (non-RC) release tag(s) for a voted Apache Hamilton release. +# +# During the RC build, apache_release_helper.py creates a tag like +# -v-incubating-RC +# pointing at the commit the artifacts were built from. That RC commit often +# lives only on the release branch. After the vote passes and the release +# branch is squash-merged to main, the canonical release should be tagged +# with a clean, RC-less name on the merge commit so: +# - the tag is reachable from main +# - GitHub release pages / archives resolve to a sensible name +# (ASF mentors flag RC-suffixed tags as poor release names) +# +# This script creates (and optionally pushes) an annotated tag +# -v-incubating +# on a target commit (default: current HEAD, expected to be main at the +# squash-merge commit). The original -RC tag is left intact as the record +# of exactly what was voted on. +# +# Usage: +# scripts/tag_release.sh [--commit ] [--remote ] [--push] \ +# +# +# package : one of apache-hamilton, apache-hamilton-sdk, +# apache-hamilton-lsp, apache-hamilton-ui, apache-hamilton-contrib +# version : e.g. 0.9.0 +# +# Examples: +# # dry: create the tag locally on current HEAD, don't push +# scripts/tag_release.sh apache-hamilton-sdk 0.9.0 +# +# # create on a specific commit and push to origin +# scripts/tag_release.sh --commit 1a2b3c4 --push apache-hamilton-sdk 0.9.0 + +set -eu + +REMOTE="origin" +COMMIT="HEAD" +PUSH="false" + +while [ $# -gt 0 ]; do + case "$1" in + --commit) COMMIT="$2"; shift 2 ;; + --remote) REMOTE="$2"; shift 2 ;; + --push) PUSH="true"; shift ;; + --) shift; break ;; + -*) echo "Unknown option: $1" >&2; exit 1 ;; + *) break ;; + esac +done + +if [ $# -ne 2 ]; then + echo "Usage: $0 [--commit ] [--remote ] [--push] " >&2 + echo " package: apache-hamilton | apache-hamilton-sdk | apache-hamilton-lsp | apache-hamilton-ui | apache-hamilton-contrib" >&2 + exit 1 +fi + +PACKAGE="$1" +VERSION="$2" + +case "$PACKAGE" in + apache-hamilton|apache-hamilton-sdk|apache-hamilton-lsp|apache-hamilton-ui|apache-hamilton-contrib) ;; + *) echo "ERROR: unknown package '$PACKAGE'" >&2; exit 1 ;; +esac + +TAG="${PACKAGE}-v${VERSION}-incubating" +RC_TAG_GLOB="${PACKAGE}-v${VERSION}-incubating-RC*" + +# Resolve target commit +TARGET_SHA="$(git rev-parse --verify "${COMMIT}^{commit}")" + +echo "Package: ${PACKAGE}" +echo "Version: ${VERSION}" +echo "Release tag: ${TAG}" +echo "Target commit ${TARGET_SHA}" +echo + +# Sanity: warn if the target commit is not on main +if git rev-parse --verify main >/dev/null 2>&1; then + if git merge-base --is-ancestor "${TARGET_SHA}" main; then + echo " OK: target commit is reachable from main." + else + echo " WARNING: target commit is NOT reachable from 'main'." + echo " The final release tag should point at the squash-merge" + echo " commit on main. Continue only if you know what you're doing." + fi +fi + +# Show the RC tag(s) for reference (what was voted on) +RC_TAGS="$(git tag -l "${RC_TAG_GLOB}" || true)" +if [ -n "${RC_TAGS}" ]; then + echo " RC tag(s) for this release (kept as-is):" + for t in ${RC_TAGS}; do + echo " ${t} -> $(git rev-list -n1 "$t")" + done +fi +echo + +# Refuse to clobber an existing final tag +if git rev-parse --verify "refs/tags/${TAG}" >/dev/null 2>&1; then + echo "ERROR: tag '${TAG}' already exists (-> $(git rev-list -n1 "${TAG}"))." >&2 + echo " Delete it first if you intend to move it: git tag -d ${TAG}" >&2 + exit 1 +fi + +# Create the annotated tag +git tag -a "${TAG}" "${TARGET_SHA}" -m "Apache ${PACKAGE} ${VERSION}-incubating" +echo "Created annotated tag ${TAG} -> ${TARGET_SHA}" + +if [ "${PUSH}" = "true" ]; then + echo "Pushing ${TAG} to ${REMOTE}..." + git push "${REMOTE}" "refs/tags/${TAG}" + echo "Pushed." +else + echo + echo "Tag created locally. To push:" + echo " git push ${REMOTE} refs/tags/${TAG}" +fi