aboutsummaryrefslogtreecommitdiffhomepage
path: root/scripts/release/release.sh
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/release/release.sh')
-rwxr-xr-xscripts/release/release.sh264
1 files changed, 146 insertions, 118 deletions
diff --git a/scripts/release/release.sh b/scripts/release/release.sh
index 3b18fd80c2..1e8cbfeb90 100755
--- a/scripts/release/release.sh
+++ b/scripts/release/release.sh
@@ -18,42 +18,73 @@ set -eu
# Generate the release branches and handle the release tags.
-# Repositories to push the release branch and the release tag.
-: ${RELEASE_REPOSITORIES:="git@github.com:bazelbuild/bazel"}
+# Name of the default editor.
+: ${EDITOR=vi}
-# Repositories to push the master branch
-: ${MASTER_REPOSITORIES:="https://bazel.googlesource.com/bazel"}
+# Repositories to push the release branch and the release tag.
+RELEASE_REPOSITORY="git@github.com:bazelbuild/bazel"
-# Name of the default editor
-: ${EDITOR=vi}
+# Repositories to push the master branch.
+MASTER_REPOSITORY="https://bazel.googlesource.com/bazel"
-# Author of the release commits
-: ${RELEASE_AUTHOR="Bazel Release System <noreply@google.com>"}
+# Author of the release commits.
+RELEASE_AUTHOR="Bazel Release System <noreply@google.com>"
-# Load relnotes.sh
+# Load relnotes.sh.
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
source ${SCRIPT_DIR}/relnotes.sh
-# Load common.sh
+# Load common.sh.
source ${SCRIPT_DIR}/common.sh
-# Editing release notes info for the user
+# Editing release notes info for the user.
RELEASE_NOTE_MESSAGE='# Editing release notes
# Modify the release notes to make them suitable for the release.
# Every line starting with a # will be removed as well as every
# empty line at the start and at the end.
'
-# Fetch everything from remote repositories to avoid conflicts
-function fetch() {
- for i in ${RELEASE_REPOSITORIES}; do
- git fetch -f $i &>/dev/null || true
- git fetch -f $i refs/notes/*:refs/notes/* &>/dev/null || true
- done
+# Merge three release notes using branch $1 as a base.
+# Args:
+# $1 the branch name to use to play on
+# $2 the new generated release notes
+# $3 the last generated release notes
+# $4 the last edited release notes
+function __merge_release_notes() {
+ local branch_name="$1"
+ local relnotes="$2"
+ local last_relnotes="$3"
+ local last_savedrelnotes="$4"
+ if [ "${last_relnotes}" == "${last_savedrelnotes}" ]; then
+ echo "${relnotes}"
+ else
+ # Merge the three release notes using "git merge".
+ git checkout -q -b "${branch_name}-merge-notes-1"
+ echo "${last_relnotes}" >.relnotes
+ git add .relnotes
+ git commit -q -m "last_relnotes" --allow-empty
+
+ echo "${last_savedrelnotes}" >.relnotes
+ git add .relnotes
+ git commit -q -m "last_savedrelnotes" --allow-empty
+ git checkout -q -b "${branch_name}-merge-notes-2" HEAD~
+
+ echo "${relnotes}" >.relnotes
+ git add .relnotes
+ git commit -q -m "relnotes" --allow-empty
+
+ git merge -q --no-commit "${branch_name}-merge-notes-1" &>/dev/null || true
+ cat .relnotes
+
+ # Clean-up
+ git merge --abort || true &>/dev/null
+ git checkout -q "${branch_name}"
+ git branch -D ${branch_name}-merge-notes-{1,2} >/dev/null
+ fi
}
-# Set the release name $1 (and eventually the candidate number $2).
-function set_release_name() {
+# Set the release name to $1 (and eventually the candidate number to $2).
+function __set_release_name() {
git notes --ref=release remove 2>/dev/null || true
git notes --ref=release-candidate remove 2>/dev/null || true
git notes --ref=release append -m "$1"
@@ -63,63 +94,59 @@ function set_release_name() {
}
# Trim empty lines at the beginning and the end of the buffer.
-function trim_empty_lines() {
- local f="$(echo $'\f')" # linefeed because OSX sed does not support \f
+function __trim_empty_lines() {
# Replace all new line by a linefeed, then using sed, remove the leading
# and trailing linefeeds and convert them back to newline
- tr '\n' '\f' | sed -e "s/^$f*//" -e "s/$f*$//" | tr '\f' '\n'
+ tr '\n' '\f' | sed -e "s/^\f*//" -e "s/\f*$//" | tr '\f' '\n'
}
-# Launch the editor and return the edited release notes
-function release_note_editor() {
+# Launch the editor and return the edited release notes.
+function __release_note_editor() {
local tmpfile="$1"
local branch_name="${2-}"
+
$EDITOR ${tmpfile} || {
echo "Editor failed, cancelling release creation..." >&2
return 1
}
- # Stripping the release notes
- local relnotes="$(cat ${tmpfile} | grep -v '^#' | trim_empty_lines)"
+
+ # Strip the release notes.
+ local relnotes="$(cat ${tmpfile} | grep -v '^#' | __trim_empty_lines)"
if [ -z "${relnotes}" ]; then
echo "Release notes are empty, cancelling release creation..." >&2
return 1
fi
- echo "${relnotes}" >${tmpfile}
+
+ echo "${relnotes}" > "${tmpfile}"
}
# Create the release commit by changing the CHANGELOG file
-function create_release_commit() {
+function __create_release_commit() {
local infos=$(generate_release_message "${1}" HEAD '```')
local changelog_path="$PWD/CHANGELOG.md"
- local master=$(get_master_ref)
- # Get the changelog from master to avoid missing release notes
- # from release that were in-between
- git checkout -q ${master} CHANGELOG.md || true
+ # Get the CHANGELOG.md from master to avoid missing release notes from release
+ # that were in-between.
+ git checkout master CHANGELOG.md
# CHANGELOG.md
- local tmpfile="$(mktemp ${TMPDIR:-/tmp}/relnotes-XXXXXXXX)"
- trap "rm -f ${tmpfile}" EXIT
- echo -n "## ${infos}" >${tmpfile}
- if [ -f "${changelog_path}" ]; then
- {
- echo
- echo
- cat "${changelog_path}"
- echo
- } >> ${tmpfile}
- fi
- cat "${tmpfile}" > ${changelog_path}
- git add ${changelog_path}
- rm -f "${tmpfile}"
- trap - EXIT
+ local tmpfile="$(mktemp --tmpdir relnotes-XXXXXXXX)"
+ {
+ echo -n "## ${infos}"
+ echo
+ echo
+ cat "${changelog_path}"
+ echo
+ } >> ${tmpfile}
+ mv "${tmpfile}" "${changelog_path}"
+ git add "${changelog_path}"
# Commit
infos="$(echo "${infos}" | grep -Ev '^```$')"
git commit --no-verify -m "${infos}" --no-edit --author "${RELEASE_AUTHOR}"
}
-function apply_cherry_picks() {
+function __apply_cherry_picks() {
echo "Applying cherry-picks"
# Apply cherry-picks
for commit in "$@"; do
@@ -142,10 +169,9 @@ function apply_cherry_picks() {
return 0
}
-# Find out the last release since the fork between "${1:-HEAD}" from master.
-function find_last_release() {
- local branch="${1:-HEAD}"
- local baseline="${2:-$(get_release_baseline "${branch}")}"
+# Find out the last release since the fork between HEAD from master.
+function __find_last_release() {
+ local baseline="${1:-$(get_release_baseline "HEAD")}"
local changes="$(git log --pretty=format:%H "${baseline}~".."${branch}")"
for change in ${changes}; do
if git notes --ref=release show ${change} &>/dev/null; then
@@ -157,7 +183,7 @@ function find_last_release() {
# Execute the create command:
# Create a new release named "$1" with "$2" as the baseline commit.
-function create_release() {
+function __create_release() {
local force_rc=
if [[ "$1" =~ ^--force_rc=([0-9]*)$ ]]; then
force_rc=${BASH_REMATCH[1]}
@@ -166,23 +192,24 @@ function create_release() {
local release_name="$1"
local baseline="$2"
shift 2
- local origin_branch=$(git_get_branch)
local branch_name="release-${release_name}"
- fetch
+ # Fetch everything from remote repositories to avoid conflicts
+ git fetch -f "${RELEASE_REPOSITORY}"
+ git fetch -f "${RELEASE_REPOSITORY}" 'refs/notes/*:refs/notes/*'
local last_release="$(git rev-parse --verify "${branch_name}" 2>/dev/null || true)"
echo "Creating new release branch ${branch_name} for release ${release_name}"
git checkout -B ${branch_name} ${baseline}
- apply_cherry_picks $@ || {
- git checkout ${origin_branch}
+ __apply_cherry_picks $@ || {
+ git checkout master
git branch -D ${branch_name}
exit 1
}
- setup_git_notes "${force_rc}" "${release_name}" "${last_release}" || {
- git checkout ${origin_branch}
+ __setup_git_notes "${force_rc}" "${release_name}" "${last_release}" || {
+ git checkout master
git branch -D ${branch_name}
exit 1
}
@@ -203,16 +230,16 @@ function create_release() {
# last release candidate or the branch name, e.g. if the branch
# name is release-v1, then the name will be 'v1').
# $3: (optional) Specify the commit for last release.
-function setup_git_notes() {
+function __setup_git_notes() {
local force_rc="$1"
local branch_name="$(git_get_branch)"
# Figure out where we are in release: find the rc, the baseline, cherrypicks
# and release name.
local rc=${force_rc:-1}
- local baseline="$(get_release_baseline)"
+ local baseline="$(get_release_baseline "HEAD")"
local cherrypicks="$(get_cherrypicks "HEAD" "${baseline}")"
- local last_release="${3-$(find_last_release "HEAD" "${baseline}")}"
+ local last_release="${3-$(__find_last_release "${baseline}")}"
local release_name="${2-}"
if [ -n "${last_release}" ]; then
if [ -z "${force_rc}" ]; then
@@ -232,13 +259,14 @@ function setup_git_notes() {
fi
# Edit the release notes
- local tmpfile=$(mktemp ${TMPDIR:-/tmp}/relnotes-XXXXXXXX)
+ local tmpfile=$(mktemp --tmpdir relnotes-XXXXXXXX)
trap "rm -f ${tmpfile}" EXIT
echo "Creating release notes"
# Save the changelog so we compute the relnotes against HEAD.
- git show master:CHANGELOG.md >${tmpfile} 2>/dev/null || echo >${tmpfile}
+ git show master:CHANGELOG.md > "${tmpfile}"
+
# Compute the new release notes
local relnotes="$(create_release_notes "${tmpfile}" "${baseline}" ${cherrypicks})"
@@ -250,66 +278,63 @@ function setup_git_notes() {
local last_relnotes="$(create_release_notes "${tmpfile}")"
git checkout -q "${branch_name}"
local last_savedrelnotes="$(get_release_notes "${last_release}")"
- relnotes="$(merge_release_notes "${branch_name}" "${relnotes}" \
+ relnotes="$(__merge_release_notes "${branch_name}" "${relnotes}" \
"${last_relnotes}" "${last_savedrelnotes}")"
fi
- echo "${RELEASE_NOTE_MESSAGE}" > ${tmpfile}
- echo "# $(get_release_title "${release_name}rc${rc}")" >> ${tmpfile}
- echo >> ${tmpfile}
- echo "${relnotes}" >>"${tmpfile}"
- release_note_editor ${tmpfile} "${branch_name}" || return 1
+
+ echo "${RELEASE_NOTE_MESSAGE}" > "${tmpfile}"
+ echo "# $(get_release_title "${release_name}rc${rc}")" >> "${tmpfile}"
+ echo >> "${tmpfile}"
+ echo "${relnotes}" >> "${tmpfile}"
+
+ __release_note_editor "${tmpfile}" "${branch_name}" || return 1
relnotes="$(cat ${tmpfile})"
- # Add the git notes
- set_release_name "${release_name}" "${rc}"
+ # Add the git notes.
+ git notes --ref=release add -f -m "${release_name}"
+ git notes --ref=release-candidate add -f -m "${rc}"
git notes --ref=release-notes add -f -m "${relnotes}"
- # Clean-up
- rm -f ${tmpfile}
+ # Clean-up.
+ rm -f "${tmpfile}"
trap - EXIT
}
# Force push a ref $2 to repo $1 if exists
-function push_if_exists() {
+function __push_if_exists() {
if git show-ref -q "${2}"; then
git push -f "${1}" "+${2}"
fi
}
# Push release notes refs but also a given ref
-function push_notes_and_ref() {
+function __push_notes_and_ref() {
local ref="$1"
- for repo in ${RELEASE_REPOSITORIES}; do
- push_if_exists "${repo}" "${ref}"
- push_if_exists "${repo}" "refs/notes/release"
- push_if_exists "${repo}" "refs/notes/release-candidate"
- push_if_exists "${repo}" "refs/notes/release-notes"
- push_if_exists "${repo}" "refs/notes/cherrypick"
- done
+ __push_if_exists "${RELEASE_REPOSITORY}" "${ref}"
+ __push_if_exists "${RELEASE_REPOSITORY}" "refs/notes/release"
+ __push_if_exists "${RELEASE_REPOSITORY}" "refs/notes/release-candidate"
+ __push_if_exists "${RELEASE_REPOSITORY}" "refs/notes/release-notes"
+ __push_if_exists "${RELEASE_REPOSITORY}" "refs/notes/cherrypick"
}
# Push the release branch to the release repositories so a release
# candidate can be created.
-function push_release_candidate() {
- push_notes_and_ref "$(get_release_branch)"
+function __push_release_candidate() {
+ __push_notes_and_ref "$(get_release_branch)"
}
# Deletes the release branch after a release or abandoning the release
-function cleanup_branches() {
+function __cleanup_branches() {
local tag_name=$1
- local i
echo "Destroying the release branches for release ${tag_name}"
- # Destroy branch, ignoring if it doesn't exist.
- git branch -D release-${tag_name} &>/dev/null || true
- for i in $RELEASE_REPOSITORIES; do
- git push -f $i :release-${tag_name} &>/dev/null || true
- done
+ git branch -D "release-${tag_name}" &>/dev/null || true
+ git push -f "${RELEASE_REPOSITORY}" ":release-${tag_name}" &>/dev/null || true
}
# Releases the current release branch, creating the necessary tag,
# destroying the release branch, updating the master's CHANGELOG.md
# and pushing everything to GitHub.
-function do_release() {
+function __do_release() {
local branch=$(get_release_branch)
local tag_name=$(get_release_name)
@@ -317,52 +342,55 @@ function do_release() {
read answer
if [ "$answer" = "y" ] || [ "$answer" = "Y" ]; then
echo "Creating the release commit"
- create_release_commit "${tag_name}"
- set_release_name "${tag_name}"
+ __create_release_commit "${tag_name}"
+ __set_release_name "${tag_name}"
+ git notes --ref=release add -f -m "${tag_name}"
+ git notes --ref=release-candidate remove || true
+
echo "Creating the tag"
git tag ${tag_name}
echo "Cherry-picking CHANGELOG.md modification into master"
git checkout master
+
# Ensuring we are up to date for master
- git pull --rebase $(echo "$MASTER_REPOSITORIES" | cut -d " " -f 1) master
+ git pull --rebase "$MASTER_REPOSITORY" master
+
# We do not cherry-pick because we might have conflict if the baseline
# does not contains the latest CHANGELOG.md file, so trick it.
local changelog_path="$PWD/CHANGELOG.md"
- git show ${branch}:CHANGELOG.md >${changelog_path}
- local tmpfile=$(mktemp ${TMPDIR:-/tmp}/relnotes-XXXXXXXX)
+ git show "${branch}:CHANGELOG.md" > "${changelog_path}"
+ local tmpfile=$(mktemp --tmpdir relnotes-XXXXXXXX)
trap 'rm -f ${tmpfile}' EXIT
- git_commit_msg ${branch} >${tmpfile}
- git add ${changelog_path}
- git commit --no-verify -F ${tmpfile} --no-edit --author "${RELEASE_AUTHOR}"
- rm -f ${tmpfile}
+ git_commit_msg "${branch}" > "${tmpfile}"
+ git add "${changelog_path}"
+ git commit --no-verify -F "${tmpfile}" --no-edit --author "${RELEASE_AUTHOR}"
+ rm -f "${tmpfile}"
trap - EXIT
echo "Pushing the change to remote repositories"
- for i in $MASTER_REPOSITORIES; do
- git push $i +master
- done
- push_notes_and_ref "refs/tags/${tag_name}"
- cleanup_branches ${tag_name}
+ git push "${MASTER_REPOSITORY}" +master
+ __push_notes_and_ref "refs/tags/${tag_name}"
+ __cleanup_branches "${tag_name}"
fi
}
# Abandon the current release, deleting the branch on the local
# repository and on GitHub, discarding all changes
-function abandon_release() {
+function __abandon_release() {
local branch_info=$(get_release_branch)
local tag_name=$(get_release_name)
echo -n "You are about to abandon release ${tag_name}, confirm? [y/N] "
read answer
if [ "$answer" = "y" ] || [ "$answer" = "Y" ]; then
- git notes --ref=release remove 2>/dev/null || true
- git notes --ref=release-candidate remove 2>/dev/null || true
+ git notes --ref=release remove || true
+ git notes --ref=release-candidate remove || true
git checkout -q master >/dev/null
- cleanup_branches ${tag_name}
+ __cleanup_branches ${tag_name}
fi
}
-function usage() {
+function __usage() {
cat >&2 <<EOF
Usage: $1 command [arguments]
Available commands are:
@@ -418,13 +446,13 @@ shift || usage $progname
case $cmd in
create)
(( $# >= 2 )) || usage $progname
- create_release "$@"
+ __create_release "$@"
;;
push)
- push_release_candidate
+ __push_release_candidate
;;
release)
- do_release
+ __do_release
;;
generate-rc)
force_rc=
@@ -432,12 +460,12 @@ case $cmd in
force_rc=${BASH_REMATCH[1]}
shift 1
fi
- setup_git_notes "${force_rc}"
+ __setup_git_notes "${force_rc}"
;;
abandon)
- abandon_release
+ __abandon_release
;;
*)
- usage $progname
+ __usage $progname
;;
esac