aboutsummaryrefslogtreecommitdiffhomepage
path: root/scripts/release/relnotes.sh
diff options
context:
space:
mode:
authorGravatar Damien Martin-Guillerez <dmarting@google.com>2015-07-24 12:40:48 +0000
committerGravatar Damien Martin-Guillerez <dmarting@google.com>2015-07-27 08:32:57 +0000
commitd019eea902ac64a73e80dcf55db26f9f85c3e54a (patch)
tree615a7c4081190ca9a9591b946b5bf935a81edb08 /scripts/release/relnotes.sh
parent523bff51edca2e098f2c072325776c0dc6c6a727 (diff)
Bazel release notes creation
This script uses the RELNOTES: tag (RELNOTES for a simple change, RELNOTES[NEW] for a new feature, RELNOTES[INC] for an incompatible change) to create the CHANGELOG.md file. -- Change-Id: If457a0a85f4a9ceddf822393d0aeb8b60c54136b Reviewed-on: https://bazel-review.googlesource.com/#/c/1583/ MOS_MIGRATED_REVID=99020942
Diffstat (limited to 'scripts/release/relnotes.sh')
-rwxr-xr-xscripts/release/relnotes.sh161
1 files changed, 161 insertions, 0 deletions
diff --git a/scripts/release/relnotes.sh b/scripts/release/relnotes.sh
new file mode 100755
index 0000000000..56888c574e
--- /dev/null
+++ b/scripts/release/relnotes.sh
@@ -0,0 +1,161 @@
+#!/bin/bash -eu
+
+# Copyright 2015 Google Inc. All rights reserved.
+#
+# 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.
+
+# Generate the release notes from the git history.
+
+# It uses the RELNOTES tag in the history to knows the important changes to
+# report:
+# RELNOTES: indicates a change important the user.
+# RELNOTES[NEW]: introduces a new feature.
+# RELNOTES[INC]: indicates an incompatible change.
+# The previous releases base is detected using the CHANGELOG file from the
+# repository.
+RELNOTES_TYPES=("INC" "NEW" "")
+RELNOTES_DESC=("Incompatible changes" "New features" "Important changes")
+
+# Get the baseline version and cherry-picks of the previous release
+# Parameter: $1 is the path to the changelog file
+# Output: "${BASELINE} ${CHERRYPICKS}"
+# BASELINE is the hash of the baseline commit of the latest release
+# CHERRYPICKS is the list of hash of cherry-picked commits of the latest release
+# return 1 if there is no initial release
+function get_last_release() {
+ local changelog=$1
+ [ -f "$changelog" ] || return 1 # No changelog = initial release
+ local BASELINE_LINE=$(grep -m 1 -n '^Baseline: ' "$changelog") || return 1
+ [ -n "${BASELINE_LINE}" ] || return 1 # No baseline = initial release
+ local BASELINE_LINENB=$(echo "${BASELINE_LINE}" | cut -d ":" -f 1)
+ BASELINE=$(echo "${BASELINE_LINE}" | cut -d " " -f 2)
+ local CHERRYPICK_LINE=$(($BASELINE_LINENB + 1))
+ # grep -B999 looks for all lines before the empty line and after that we
+ # restrict to only lines with the cherry picked hash then finally we cut
+ # the hash.
+ local CHERRY_PICKS=$(tail -n +${CHERRYPICK_LINE} "$changelog" \
+ | grep -m 1 "^$" -B999 \
+ | grep -E '^ \+ [a-z0-9]+:' \
+ | cut -d ":" -f 1 | cut -d "+" -f 2)
+ echo $BASELINE $CHERRY_PICKS
+ return 0
+}
+
+# Now get the list of commit with a RELNOTES since latest release baseline ($1)
+# discarding cherry_picks ($2..) and rollbacks. The returned list of commits is
+# from the oldest to the newest
+function get_release_notes_commits() {
+ local baseline=$1
+ shift
+ local cherry_picks="$@"
+ local rollback_commits=$(git log --oneline -E --grep='^Rollback of commit [a-z0-9]+.$' ${baseline}.. \
+ | grep -E '^[a-z0-9]+ Rollback of commit [a-z0-9]+.$')
+ local rollback_hashes=$(echo "$rollback_commits" | cut -d " " -f 1)
+ local rolledback_hashes=$(echo "$rollback_commits" | cut -d " " -f 5 | sed -E 's/^(.......).*$/\1/')
+ local exclude_hashes=$(echo $cherry_picks $rollback_hashes $rolledback_hashes | xargs echo | sed 's/ /|/g')
+ git log --reverse --pretty=format:%h ${baseline}.. -E --grep='^RELNOTES(\[[^\]+\])?:' \
+ | grep -Ev "^(${exclude_hashes})"
+}
+
+# Extract the release note from a commit hash ($1). It extracts
+# the RELNOTES([??]): lines. A new empty line ends the relnotes tag.
+# It adds the relnotes, if not "None" ("None.") or "n/a" ("n/a.") to
+# the correct array:
+# RELNOTES_INC for incompatible changes
+# RELNOTES_NEW for new features changes
+# RELNOTES for other changes
+function extract_release_note() {
+ local relnote="$(git show -s $1 --pretty=format:%B | awk '/^RELNOTES(\[[^\]]+\])?:/,/^$/')"
+ local regex="^RELNOTES(\[([a-zA-Z]*)\])?:[[:space:]]*([^[:space:]].*[^[:space:]])[[:space:]]*$"
+ if [[ "$relnote" =~ $regex ]]; then
+ local relnote_kind=${BASH_REMATCH[2]}
+ local relnote_text="${BASH_REMATCH[3]}"
+ if [[ ! "$(echo $relnote_text | awk '{print tolower($0)}')" =~ ^(none|n/a)?.?$ ]]; then
+ eval "RELNOTES_${relnote_kind}+=(\"\${relnote_text}\")"
+ fi
+ fi
+}
+
+# Build release notes arrays from a list of commits ($@) and return the release
+# note in an array of array.
+function get_release_notes() {
+ for i in "${RELNOTES_TYPES[@]}"; do
+ eval "RELNOTES_${i}=()"
+ done
+ for i in $@; do
+ extract_release_note $i
+ done
+}
+
+# Returns the list of release notes in arguments into a list of points in
+# a markdown list. The release notes are wrapped to 70 characters so it
+# displays nicely in a git history.
+function format_release_notes() {
+ local i
+ for (( i=1; $i <= $#; i=$i+1 )); do
+ local relnote="${!i}"
+ local lines=$(echo "$relnote" | fmt -w 66) # wrap to 70 counting the 4 leading spaces.
+ echo " - $lines" | head -1
+ echo "$lines" | tail -n +2 | sed 's/^/ /'
+ done
+}
+
+# Create the release notes since commit $1 ($2...${[#]} are the cherry-picks,
+# so the commits to ignore.
+function release_notes() {
+ local i
+ local commits=$(get_release_notes_commits $@)
+ local length="${#RELNOTES_TYPES[@]}"
+ get_release_notes "$commits"
+ for (( i=0; $i < $length; i=$i+1 )); do
+ local relnotes_title="${RELNOTES_DESC[$i]}"
+ local relnotes_type=${RELNOTES_TYPES[$i]}
+ local relnotes="RELNOTES_${relnotes_type}[@]"
+ local nb_relnotes=$(eval "echo \${#$relnotes}")
+ if (( "${nb_relnotes}" > 0 )); then
+ echo "${relnotes_title}:"
+ echo
+ format_release_notes "${!relnotes}"
+ echo
+ fi
+ done
+}
+
+# A wrapper around all the previous function, using the CHANGELOG.md
+# file in $1 to compute the last release commit hash.
+function create_release_notes() {
+ local last_release=$(get_last_release "$1") || \
+ { echo "Initial release."; return 0; }
+ [ -n "${last_release}" ] || { echo "Initial release."; return 0; }
+ release_notes ${last_release}
+}
+
+# Create the revision information given a list of commits. The first
+# commit should be the baseline, and the other one are the cherry-picks.
+# The result is of the form:
+# Baseline: BASELINE_COMMIT
+# + CHERRY_PICK1: commit message summary of the CHERRY_PICK1. This
+# message will be wrapped into 70 columns.
+# + CHERRY_PICK2: commit message summary of the CHERRY_PICK2.
+function create_revision_information() {
+ echo "Baseline: $1"
+ shift
+ while [ -n "${1-}" ]; do
+ local hash="$1"
+ local subject=$(git show -s --pretty=format:%s $hash)
+ local lines=$(echo "$subject" | fmt -w 56) # 14 leading spaces.
+ echo " + $hash: $lines" | head -1
+ echo "$lines" | tail -n +2 | sed 's/^/ /'
+ shift
+ done
+}