Skip to content

Commit a090929

Browse files
committed
Release notes script
Reads through the current branch, grabbing merge commits and turning them into fancy release notes. Also updates the VERSIONING.md doc with simplifications to the process.
1 parent 8f98fb7 commit a090929

File tree

5 files changed

+275
-35
lines changed

5 files changed

+275
-35
lines changed

.github/pull_request_template.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
<!-- please add a icon to the title of this PR (see VERSIONING.md), and delete this line and similar ones -->
2-
<!-- the icon will be either :warning: (major), :sparkles: (minor), :bug: (patch), :book: (docs), or :running: (other) -->
2+
<!-- the icon will be either ⚠ (:warning:, major), ✨ (:sparkles, minor), 🐛 (:bug:, patch), 📖 (:book:, docs), or 🏃 (:running:, other) -->
33

44
<!-- What does this do, and why do we need it? -->

RELEASE.md

Lines changed: 0 additions & 8 deletions
This file was deleted.

VERSIONING.md

Lines changed: 55 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@
2121
changes will go into an semi-immediate patch or minor release
2222

2323
- Please *try* to avoid breaking changes when you can. They make users
24-
face difficult decisions (when do I go through the pain of upgrading),
25-
and make life hard for maintainers and contributors (dealing with
26-
differences on stable branches).
24+
face difficult decisions ("when do I go through the pain of
25+
upgrading?"), and make life hard for maintainers and contributors
26+
(dealing with differences on stable branches).
2727

2828
### Mantainers
2929

@@ -65,20 +65,18 @@ greatest code, including breaking changes, happens on master.
6565

6666
The *release-X* branches contain stable, backwards compatible code. Every
6767
major (X) release, a new such branch is created. It is from these
68-
branches that minor and patch releases are tagged.
68+
branches that minor and patch releases are tagged. If some cases, it may
69+
be neccessary open PRs for bugfixes directly against stable branches, but
70+
this should generally not be the case.
6971

7072
The maintainers are responsible for updating the contents of this branch;
7173
generally, this is done just before a release using release tooling that
7274
filters and checks for changes tagged as breaking (see below).
7375

7476
### Tooling
7577

76-
* [gen-release-notes.sh](hack/gen-release-notes.sh): generate release
77-
notes for a range of commits, and check for next version type
78-
(***TODO***)
79-
80-
* [cherrypick-minor-version.sh](hack/cherrypick-minor-version.sh): update
81-
a stable branch with appropriate commits from the master (***TODO***).
78+
* [release-notes.sh](hack/release-notes.sh): generate release notes
79+
for a range of commits, and check for next version type (***TODO***)
8280

8381
* [verify-commit-messages.sh](hack/verify-commit-messages.sh): check that
8482
your PR and/or commit messages have the right versioning icon
@@ -95,14 +93,14 @@ a:
9593
- Docs: :book: (`:book:`)
9694
- Infra/Tests/Other: :running: (`:running:`)
9795

98-
Individual commits may be tagged separately, but will generally be assumed
99-
to match the PR. For instance, if you have a bugfix in with a breaking
100-
change, it's generally encouraged to submit the bugfix separately, but if
101-
you must put them in one PR, mark the commit separately.
96+
You can also use the equivalent emoji directly, since GitHub doesn't
97+
render the `:xyz:` aliases in PR titles.
10298

103-
*Commits marked separately will be treated similiarly to a distinct PR by
104-
the cherrypick scripts*. Therefore, only use them if you want the given
105-
commit to be considered separately.
99+
Individual commits should not be tagged separately, but will generally be
100+
assumed to match the PR. For instance, if you have a bugfix in with
101+
a breaking change, it's generally encouraged to submit the bugfix
102+
separately, but if you must put them in one PR, mark the commit
103+
separately.
106104

107105
### Commands and Workflow
108106

@@ -124,21 +122,44 @@ a command reference.
124122
Minor and patch releases are generally done immediately after a feature or
125123
bugfix is landed, or sometimes a series of features tied together.
126124

127-
Major releases are done once a sufficient amount of breaking changes are
128-
accrued. Since we don't intend to have a ton of these, the maintainers
129-
will evaluate when to do a major release as it comes up.
125+
Minor releases will only be tagged on the *most recent* major release
126+
branch, except in exceptional circumstances. Patches will be backported
127+
to maintained stable versions, as needed.
128+
129+
Major releases are done shortly after a breaking change is merged -- once
130+
a breaking change is merged, the next release *must* be a major revison.
131+
We don't intend to have a lot of these, so we may put off merging breaking
132+
PRs until a later date.
130133

131134
### Exact Steps
132135

133-
1. (*if doing a minor or patch release*) Update the release-X branch with
134-
the latest set of changes using the cherrypick tooling (***TODO***)
136+
Follow the release-specific steps below, then follow the general steps
137+
after that.
138+
139+
#### Minor and patch releases
140+
141+
1. Update the release-X branch with the latest set of changes by calling
142+
`git rebase master` from the release branch.
143+
144+
#### Major releases
135145

136-
2. Generate release notes using the release note tooling (***TODO***)
146+
1. Create a new release branch named `release-X` (where `X` is the new
147+
version) off of master.
148+
149+
#### General
150+
151+
2. Generate release notes using the release note tooling.
137152

138153
3. Add a release for controller-runtime on GitHub, using those release
139154
notes, with a title of `vX.Y.Z`.
155+
156+
4. Do a similar process for
157+
[controller-tools](https://github.com/kubernetes-sigs/controller-tools)
140158

141-
4. Announce the release in `#kubebuilder` on Slack with a pinned message.
159+
5. Announce the release in `#kubebuilder` on Slack with a pinned message.
160+
161+
6. Potentially update
162+
[kubebuilder](https://github.com/kubernetes-sigs/kubebuilder) as well.
142163

143164
### Breaking Changes
144165

@@ -148,8 +169,11 @@ maintainers/contributors, who have to deal with differences between master
148169
and stable branches.
149170

150171
That being said, we'll occaisonally want to make breaking changes. They'll
151-
be merged onto master, but won't make it into a release immediately (see
152-
[Release Proccess](#release-process)).
172+
be merged onto master, and will then trigger a major release (see [Release
173+
Proccess](#release-process)). Because breaking changes induce a major
174+
revision, the maintainers may delay a particular breaking change until
175+
a later date when they are ready to make a major revision with a few
176+
breaking changes.
153177

154178
If you're going to make a breaking change, please make sure to explain in
155179
detail why it's helpful. Is it necessary to cleanly resolve an issue?
@@ -158,6 +182,11 @@ Does it improve API ergonomics?
158182
Maintainers should treat breaking changes with caution, and evaluate
159183
potential non-breaking solutions (see below).
160184

185+
Note that API breakage in public APIs due to dependencies will trigger
186+
a major revision, so you may occaisonally need to have a major release
187+
anyway, due to changes in libraries like `k8s.io/client-go` or
188+
`k8s.io/apimachinery`.
189+
161190
*NB*: Pre-1.0 releases treat breaking changes a bit more lightly. We'll
162191
still consider carefully, but the pre-1.0 timeframe is useful for
163192
converging on a ergonomic API.

hack/release/common.sh

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#!/usr/bin/env bash
2+
shopt -s extglob
3+
4+
cr_major_pattern=":warning:|$(printf "\xe2\x9a\xa0")"
5+
cr_minor_pattern=":sparkles:|$(printf "\xe2\x9c\xa8")"
6+
cr_patch_pattern=":bug:|$(printf "\xf0\x9f\x90\x9b")"
7+
cr_docs_pattern=":book:|$(printf "\xf0\x9f\x93\x96")"
8+
cr_other_pattern=":running:|$(printf "\xf0\x9f\x8f\x83")"
9+
10+
# cr::symbol-type-raw turns :xyz: and the corresponding emoji
11+
# into one of "major", "minor", "patch", "docs", "other", or
12+
# "unknown", ignoring the '!'
13+
cr::symbol-type-raw() {
14+
case $1 in
15+
@(${cr_major_pattern})?('!'))
16+
echo "major"
17+
;;
18+
@(${cr_minor_pattern})?('!'))
19+
echo "minor"
20+
;;
21+
@(${cr_patch_pattern})?('!'))
22+
echo "patch"
23+
;;
24+
@(${cr_docs_pattern})?('!'))
25+
echo "docs"
26+
;;
27+
@(${cr_other_pattern})?('!'))
28+
echo "other"
29+
;;
30+
*)
31+
echo "unknown"
32+
;;
33+
esac
34+
}
35+
36+
# cr::symbol-type turns :xyz: and the corresponding emoji
37+
# into one of "major", "minor", "patch", "docs", "other", or
38+
# "unknown".
39+
cr::symbol-type() {
40+
local type_raw=$(cr::symbol-type-raw $1)
41+
if [[ ${type_raw} == "unknown" ]]; then
42+
echo "unknown"
43+
return
44+
fi
45+
46+
if [[ $1 == *'!' ]]; then
47+
echo "major"
48+
return
49+
fi
50+
51+
echo ${type_raw}
52+
}
53+
54+
# git::is-release-branch-name checks if its argument is a release branch name
55+
# (release-0.Y or release-X).
56+
git::is-release-branch-name() {
57+
[[ ${1-must specify release branch name to check} =~ release-((0\.[[:digit:]])|[[:digit:]]+) ]]
58+
}
59+
60+
# git::ensure-release-branch checks that we're on a release branch
61+
git::ensure-release-branch() {
62+
local current_branch=$(git rev-parse --abbrev-ref HEAD)
63+
if ! git::is-release-branch-name ${current_branch}; then
64+
echo "branch ${current_branch} does not appear to be a release branch (release-X)" >&2
65+
exit 1
66+
fi
67+
}
68+
69+
# git::export-current-version outputs the current version
70+
# as exported variables (${maj,min,patch}_ver, last_tag) after
71+
# checking that we're on the right release branch.
72+
git::export-current-version() {
73+
# make sure we're on a release branch
74+
git::ensure-release-branch
75+
76+
# deal with the release-0.1 branch, or similar
77+
local release_ver=${BASH_REMATCH[1]}
78+
maj_ver=${release_ver}
79+
local tag_pattern='v${maj_ver}.([[:digit:]]+).([[:digit]]+)'
80+
if [[ ${maj_ver} =~ 0\.([[:digit:]]+) ]]; then
81+
maj_ver=0
82+
min_ver=${BASH_REMATCH[1]}
83+
local tag_pattern="v0.(${min_ver}).([[:digit:]]+)"
84+
fi
85+
86+
# make sure we've got a tag that matches our release branch
87+
last_tag=$(git describe --tags --abbrev=0) # try to fetch just the "current" tag name
88+
if [[ ! ${last_tag} =~ ${tag_pattern} ]]; then
89+
echo "tag ${last_tag} does not appear to be a release for this release (${release_ver})-- it should be v${maj_ver}.Y.Z" >&2
90+
exit 1
91+
fi
92+
93+
export min_ver=${BASH_REMATCH[1]}
94+
export patch_ver=${BASH_REMATCH[2]}
95+
export maj_ver=${maj_ver}
96+
export last_tag=${last_tag}
97+
}
98+
99+
# git::next-version figures out the next version to tag
100+
# (it also sets the current version variables to the current version)
101+
git::next-version() {
102+
git::export-current-version
103+
104+
local feature_commits=$(git rev-list ${last_tag}..${end_range} --grep="${cr_minor_pattern}")
105+
local breaking_commits=$(git rev-list ${last_tag}..${end_range} --grep="${cr_major_pattern}")
106+
107+
if [[ -z ${breaking_commits} && ${maj_ver} > 0 ]]; then
108+
local next_ver="v$(( maj_ver + 1 )).0.0"
109+
elif [[ -z ${feature_commits} ]]; then
110+
local next_ver="v${maj_ver}.$(( min_ver + 1 )).0"
111+
else
112+
local next_ver="v${maj_ver}.${min_ver}.$(( patch_ver + 1 ))"
113+
fi
114+
115+
echo "${next_ver}"
116+
}

hack/release/release-notes.sh

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#!/usr/bin/env bash
2+
3+
set -e
4+
set -o pipefail
5+
6+
# import our common stuff
7+
source "$(dirname ${BASH_SOURCE})/common.sh"
8+
9+
# TODO: work with both release branch and major release
10+
git::ensure-release-branch
11+
git::export-current-version
12+
# check the next version
13+
next_ver=$(git::next-version)
14+
15+
features=""
16+
bugfixes=""
17+
breaking=""
18+
unknown=""
19+
MERGE_PR="Merge pull request #([[:digit:]]+) from ([[:alnum:]-]+)/.+"
20+
NEWLINE="
21+
"
22+
head_commit=$(git rev-parse HEAD)
23+
while read commit_word commit; do
24+
read title
25+
read # skip the blank line
26+
read prefix body
27+
28+
if [[ ${prefix} == v*.*.* && ( ${commit} == ${head_commit} || $(git tag --points-at ${commit}) == v*.*.* ) ]]; then
29+
# skip version merges
30+
continue
31+
fi
32+
set +x
33+
if [[ ! ${title} =~ ${MERGE_PR} ]]; then
34+
echo "Unable to determine PR number for merge ${commit} with title '${title}', aborting." >&2
35+
exit 1
36+
fi
37+
pr_number=${BASH_REMATCH[1]}
38+
pr_type=$(cr::symbol-type ${prefix})
39+
pr_title=${body}
40+
if [[ ${pr_type} == "unknown" ]]; then
41+
pr_title="${prefix} ${pr_title}"
42+
fi
43+
case ${pr_type} in
44+
major)
45+
breaking="${breaking}- ${pr_title} (#${pr_number})${NEWLINE}"
46+
;;
47+
minor)
48+
features="${features}- ${pr_title} (#${pr_number})${NEWLINE}"
49+
;;
50+
patch)
51+
bugfixes="${bugfixes}- ${pr_title} (#${pr_number})${NEWLINE}"
52+
;;
53+
docs|other)
54+
# skip non-code-changes
55+
;;
56+
unknown)
57+
unknown="${unknown}- ${pr_title} (#${pr_number})${NEWLINE}"
58+
;;
59+
*)
60+
echo "unknown PR type '${pr_type}' on PR '${pr_title}'" >&2
61+
exit 1
62+
esac
63+
done <<<$(git rev-list ${last_tag}..HEAD --merges --pretty=format:%B)
64+
65+
# TODO: sort non merge commits with tags
66+
67+
[[ -n "${breaking}" ]] && printf '\e[1;31mbreaking changes this version\e[0m' >&2
68+
[[ -n "${unknown}" ]] && printf '\e[1;35munknown changes in this release -- categorize manually\e[0m' >&2
69+
70+
echo "" >&2
71+
echo "" >&2
72+
echo "# ${next_ver}"
73+
74+
if [[ -n ${breaking} ]]; then
75+
echo ""
76+
echo "## :warning: Breaking Changes"
77+
echo ""
78+
echo "${breaking}"
79+
fi
80+
81+
if [[ -n ${features} ]]; then
82+
echo ""
83+
echo "## :sparkles: New Features"
84+
echo ""
85+
echo "${features}"
86+
fi
87+
88+
if [[ -n ${bugfixes} ]]; then
89+
echo ""
90+
echo "## :bug: Bug Fixes"
91+
echo ""
92+
echo "${bugfixes}"
93+
fi
94+
95+
if [[ -n ${unknown} ]]; then
96+
echo ""
97+
echo "## :question: *categorize these manually*"
98+
echo ""
99+
echo "${unknown}"
100+
fi
101+
102+
echo ""
103+
echo "*Thanks to all our contributors!*"

0 commit comments

Comments
 (0)