Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v14] release: Add new changelog generation script #39148

Merged
merged 1 commit into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1486,7 +1486,12 @@ rustup-install-target-toolchain:
# changelog generates PR changelog between the provided base tag and the tip of
# the specified branch.
#
# usage: make changelog
# usage: make changelog BASE_BRANCH=branch/v13 BASE_TAG=13.2.0
# usage: BASE_BRANCH=branch/v13 BASE_TAG=13.2.0 make changelog
#
# BASE_BRANCH and BASE_TAG will be automatically determined if not specified.
# See ./build.assets/changelog.sh
.PHONY: changelog
changelog:
@python3 ./build.assets/changelog.py BASE_BRANCH=$(BASE_BRANCH) BASE_TAG=$(BASE_TAG)
@BASE_BRANCH=$(BASE_BRANCH) BASE_TAG=$(BASE_TAG) ./build.assets/changelog.sh
31 changes: 0 additions & 31 deletions build.assets/changelog.py

This file was deleted.

204 changes: 204 additions & 0 deletions build.assets/changelog.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
#!/bin/bash
#
# This script generates a changelog for a release.
#
# It can optionally take two input variables: BASE_BRANCH: The base release
# branch to generate the changelog for. It will be of the form "branch/v*".
# BASE_TAG: The tag/version to generate the changelog from. It will be of the
# form "vXX.Y.Z", e.g. "v15.1.1"
#
# If neither are provided, the values will be automatically determined if
# possible:
# * The current branch will be used as the base branch if it matches the
# pattern branch/v*
# * If the current branch is forked from a base branch, the base branch will be
# used. e.g. if you create release/15.1.1 from branch/v15, the branch/v15
# will be the base branch.
# * The base tag will be determined by running "make print-version" from the
# root of the repo.
#
# Enterprise PR changelogs will be listed after the OSS changelogs. You need to
# determine if it is suitable to include them. If you do, remove the markdown
# link from each changelog when adding the changelog to CHANGELOG.md. These
# links wont work for the general public. Keep the links when adding the
# changelog to the release PR so that the enterprise PRs will link to the
# release PR.
#
# A changelog line may be marked with "NOCL:". This means there was no
# "Changelog:" line on the PR, and the PR title was used instead. You will
# likely need to reword these.
#
# If you reword changelogs, it is best to go to the source PR and change it
# there and then regenerate the changelog.
#
# Caveats:
# * If you update the "e" ref in your release PR, and you also run `make
# changelog` from the release PR branch, if you have already updated the
# version in the makefile, you will need to run `make changelog
# BASE_TAG=X.Y.Z` as this script will determine the base tag to be the
# current release version not the last released version.
#
# One preferred way of using this script is to run `make changelog` from the
# base branch and save it: `make changelog > /tmp/changelog`. If any PRs are
# merged to the base branch after you have created your release PR but before
# you have merged it, you can see any new entries with:
#
# git checkout branch/vNN
# diff -u /tmp/changelog $(make changelog)
#
# If there are changes, you can update your changelog and rebase your branch:
#
# git pull # on branch/vNN
# make changelog > /tmp/changelog
# git checkout release/XX.Y.Z
# git rebase branch/vNN
# <include /tmp/changelog in CHANGELOG.md>
# git add CHANGELOG.md && git commit --amend --no-edit && git push -f
#
# Ensure you update the PR body with the new changelog, doable on the command
# line with `gh`:
#
# gh pr edit --body-file /tmp/changelog
#

# Set by check_prereq - either jq or gojq
JQ=

main() {
set -eu

check_prereq || exit 1

local branch last_version
branch=$(get_branch) || exit 1
last_version=$(get_last_version) || exit 1

since=$(git show -s --date=format:'%Y-%m-%dT%H:%M:%S%z' --format=%cd "${last_version}") || {
echo 'Cant get timestamp of last release' >&2
return 1
}
since_e=$(git -C e show -s --date=format:'%Y-%m-%dT%H:%M:%S%z' --format=%cd "${last_version}") || {
echo 'Cant get enterprise timestamp of last release' >&2
return 1
}
e_time=$(git log -n 1 --date=format:'%Y-%m-%dT%H:%M:%S%z' --format=%cd e) || {
echo 'Cant get last modified time of "e" ref' >&2
return 1
}

list_prs "${branch}" "${since}"
printf '\nEnterprise:\n'
(cd e && list_prs "${branch}" "${since_e}" "${e_time}")

return 0
}

check_prereq() {
if command -v jq >/dev/null 2>&1; then
JQ=jq
return 0
fi
if command -v gojq >/dev/null 2>&1; then
JQ=gojq
return 0
fi

echo 'jq or gojq not installed. Install gojq easily with:' >&2
echo 'go install github.com/itchyny/gojq/cmd/gojq@latest' >&2
return 1
}

get_branch() {
# If BASE_BRANCH is set, just use that. Otherwise try to figure it out.
if [[ -n "${BASE_BRANCH-}" ]]; then
echo "${BASE_BRANCH}"
return 0
fi

local ref branch
ref=$(git symbolic-ref HEAD 2>/dev/null) || {
echo 'Not on a branch' >&2
return 1
}
branch="${ref#refs/heads/}"
if [[ "${ref}" == "${branch}" ]]; then
echo "Not on a branch: ${ref}" >&2
return 1
fi

if [[ "${branch}" != branch/v* ]]; then
# If we're already on the branch cut for the release, try to
# determine the root branch name
local fbranch
fbranch=$(
git branch \
--list 'branch/v*' \
--contains "$(git merge-base --fork-point HEAD)" \
--format '%(refname:short)'
)
branch="${fbranch:-${branch}}" # Don't overwrite $branch with empty
fi
if ! [[ "${branch}" == branch/v* ]]; then
echo "Not on a release branch: ${branch}" >&2
return 1
fi

echo "${branch}"
return 0
}

get_last_version() {
# If BASE_TAG is set, just use that, otherwise figure out the last version
if [[ -n "${BASE_TAG-}" ]]; then
echo "${BASE_TAG}"
return 0
fi

cd "$(git rev-parse --show-toplevel 2>/dev/null)" || {
echo 'ugh. cant cd to repo root' >&2
return 1
}
local last_version
last_version=$(make -s print-version) || {
echo 'Cant get last released version' >&2
return 1
}

echo "v${last_version}"
return 0
}

list_prs() {
local branch="$1" from="$2" to="${3-}"
local merged_query
if [[ -z "${to}" ]]; then
merged_query="merged:>${from}"
else
merged_query="merged:${from}..${to}"
fi

local gh_query="base:${branch} ${merged_query} -label:no-changelog"

# shellcheck disable=SC2016 # We're not trying to expand in single quotes
jq_expr='
def extract_cl: gsub("\r"; "") | split("\n") | [.[] | scan("^[Cc]hangelog: +(.*)$") | .[]];
def promote_title: {number, url, changelog: (if .changelog == [] then ["NOCL: " + .title] else .changelog end)};
def flatten_changes: . as $pr | .changelog[] | $pr + {changelog: .};
def clean: sub("\\s*$"; "") | if . | endswith(".") then . else . + "." end;
def as_entry: "* \(.changelog | clean) [#\(.number)](\(.url))";
map({number, url, title, changelog: .body | extract_cl}) |
map(promote_title) |
map(flatten_changes) |
map(as_entry) |
join("\n")
'

gh pr list \
--search "${gh_query}" \
--limit 200 \
--json number,url,title,body |
"${JQ}" -r "${jq_expr}"
}

# Only run main if executed as a script and not sourced.
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then main "$@"; fi
Loading