Skip to content

Commit 150f98b

Browse files
authored
Merge pull request kubernetes-sigs#242 from DirectXMan12/infra/release-tooling
🏃 Release notes script
2 parents 43351af + a090929 commit 150f98b

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)