Skip to content

Commit

Permalink
Release notes script
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
DirectXMan12 committed Dec 7, 2018
1 parent 8f98fb7 commit a090929
Show file tree
Hide file tree
Showing 5 changed files with 275 additions and 35 deletions.
2 changes: 1 addition & 1 deletion .github/pull_request_template.md
@@ -1,4 +1,4 @@
<!-- please add a icon to the title of this PR (see VERSIONING.md), and delete this line and similar ones -->
<!-- the icon will be either :warning: (major), :sparkles: (minor), :bug: (patch), :book: (docs), or :running: (other) -->
<!-- the icon will be either ⚠ (:warning:, major), ✨ (:sparkles, minor), 🐛 (:bug:, patch), 📖 (:book:, docs), or 🏃 (:running:, other) -->

<!-- What does this do, and why do we need it? -->
8 changes: 0 additions & 8 deletions RELEASE.md

This file was deleted.

81 changes: 55 additions & 26 deletions VERSIONING.md
Expand Up @@ -21,9 +21,9 @@
changes will go into an semi-immediate patch or minor release

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

### Mantainers

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

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

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

### Tooling

* [gen-release-notes.sh](hack/gen-release-notes.sh): generate release
notes for a range of commits, and check for next version type
(***TODO***)

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

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

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

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

### Commands and Workflow

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

Major releases are done once a sufficient amount of breaking changes are
accrued. Since we don't intend to have a ton of these, the maintainers
will evaluate when to do a major release as it comes up.
Minor releases will only be tagged on the *most recent* major release
branch, except in exceptional circumstances. Patches will be backported
to maintained stable versions, as needed.

Major releases are done shortly after a breaking change is merged -- once
a breaking change is merged, the next release *must* be a major revison.
We don't intend to have a lot of these, so we may put off merging breaking
PRs until a later date.

### Exact Steps

1. (*if doing a minor or patch release*) Update the release-X branch with
the latest set of changes using the cherrypick tooling (***TODO***)
Follow the release-specific steps below, then follow the general steps
after that.

#### Minor and patch releases

1. Update the release-X branch with the latest set of changes by calling
`git rebase master` from the release branch.

#### Major releases

2. Generate release notes using the release note tooling (***TODO***)
1. Create a new release branch named `release-X` (where `X` is the new
version) off of master.

#### General

2. Generate release notes using the release note tooling.

3. Add a release for controller-runtime on GitHub, using those release
notes, with a title of `vX.Y.Z`.

4. Do a similar process for
[controller-tools](https://github.com/kubernetes-sigs/controller-tools)

4. Announce the release in `#kubebuilder` on Slack with a pinned message.
5. Announce the release in `#kubebuilder` on Slack with a pinned message.

6. Potentially update
[kubebuilder](https://github.com/kubernetes-sigs/kubebuilder) as well.

### Breaking Changes

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

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

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

Note that API breakage in public APIs due to dependencies will trigger
a major revision, so you may occaisonally need to have a major release
anyway, due to changes in libraries like `k8s.io/client-go` or
`k8s.io/apimachinery`.

*NB*: Pre-1.0 releases treat breaking changes a bit more lightly. We'll
still consider carefully, but the pre-1.0 timeframe is useful for
converging on a ergonomic API.
Expand Down
116 changes: 116 additions & 0 deletions hack/release/common.sh
@@ -0,0 +1,116 @@
#!/usr/bin/env bash
shopt -s extglob

cr_major_pattern=":warning:|$(printf "\xe2\x9a\xa0")"
cr_minor_pattern=":sparkles:|$(printf "\xe2\x9c\xa8")"
cr_patch_pattern=":bug:|$(printf "\xf0\x9f\x90\x9b")"
cr_docs_pattern=":book:|$(printf "\xf0\x9f\x93\x96")"
cr_other_pattern=":running:|$(printf "\xf0\x9f\x8f\x83")"

# cr::symbol-type-raw turns :xyz: and the corresponding emoji
# into one of "major", "minor", "patch", "docs", "other", or
# "unknown", ignoring the '!'
cr::symbol-type-raw() {
case $1 in
@(${cr_major_pattern})?('!'))
echo "major"
;;
@(${cr_minor_pattern})?('!'))
echo "minor"
;;
@(${cr_patch_pattern})?('!'))
echo "patch"
;;
@(${cr_docs_pattern})?('!'))
echo "docs"
;;
@(${cr_other_pattern})?('!'))
echo "other"
;;
*)
echo "unknown"
;;
esac
}

# cr::symbol-type turns :xyz: and the corresponding emoji
# into one of "major", "minor", "patch", "docs", "other", or
# "unknown".
cr::symbol-type() {
local type_raw=$(cr::symbol-type-raw $1)
if [[ ${type_raw} == "unknown" ]]; then
echo "unknown"
return
fi

if [[ $1 == *'!' ]]; then
echo "major"
return
fi

echo ${type_raw}
}

# git::is-release-branch-name checks if its argument is a release branch name
# (release-0.Y or release-X).
git::is-release-branch-name() {
[[ ${1-must specify release branch name to check} =~ release-((0\.[[:digit:]])|[[:digit:]]+) ]]
}
# git::ensure-release-branch checks that we're on a release branch
git::ensure-release-branch() {
local current_branch=$(git rev-parse --abbrev-ref HEAD)
if ! git::is-release-branch-name ${current_branch}; then
echo "branch ${current_branch} does not appear to be a release branch (release-X)" >&2
exit 1
fi
}
# git::export-current-version outputs the current version
# as exported variables (${maj,min,patch}_ver, last_tag) after
# checking that we're on the right release branch.
git::export-current-version() {
# make sure we're on a release branch
git::ensure-release-branch
# deal with the release-0.1 branch, or similar
local release_ver=${BASH_REMATCH[1]}
maj_ver=${release_ver}
local tag_pattern='v${maj_ver}.([[:digit:]]+).([[:digit]]+)'
if [[ ${maj_ver} =~ 0\.([[:digit:]]+) ]]; then
maj_ver=0
min_ver=${BASH_REMATCH[1]}
local tag_pattern="v0.(${min_ver}).([[:digit:]]+)"
fi
# make sure we've got a tag that matches our release branch
last_tag=$(git describe --tags --abbrev=0) # try to fetch just the "current" tag name
if [[ ! ${last_tag} =~ ${tag_pattern} ]]; then
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
exit 1
fi
export min_ver=${BASH_REMATCH[1]}
export patch_ver=${BASH_REMATCH[2]}
export maj_ver=${maj_ver}
export last_tag=${last_tag}
}
# git::next-version figures out the next version to tag
# (it also sets the current version variables to the current version)
git::next-version() {
git::export-current-version
local feature_commits=$(git rev-list ${last_tag}..${end_range} --grep="${cr_minor_pattern}")
local breaking_commits=$(git rev-list ${last_tag}..${end_range} --grep="${cr_major_pattern}")
if [[ -z ${breaking_commits} && ${maj_ver} > 0 ]]; then
local next_ver="v$(( maj_ver + 1 )).0.0"
elif [[ -z ${feature_commits} ]]; then
local next_ver="v${maj_ver}.$(( min_ver + 1 )).0"
else
local next_ver="v${maj_ver}.${min_ver}.$(( patch_ver + 1 ))"
fi
echo "${next_ver}"
}
103 changes: 103 additions & 0 deletions hack/release/release-notes.sh
@@ -0,0 +1,103 @@
#!/usr/bin/env bash

set -e
set -o pipefail

# import our common stuff
source "$(dirname ${BASH_SOURCE})/common.sh"

# TODO: work with both release branch and major release
git::ensure-release-branch
git::export-current-version
# check the next version
next_ver=$(git::next-version)

features=""
bugfixes=""
breaking=""
unknown=""
MERGE_PR="Merge pull request #([[:digit:]]+) from ([[:alnum:]-]+)/.+"
NEWLINE="
"
head_commit=$(git rev-parse HEAD)
while read commit_word commit; do
read title
read # skip the blank line
read prefix body

if [[ ${prefix} == v*.*.* && ( ${commit} == ${head_commit} || $(git tag --points-at ${commit}) == v*.*.* ) ]]; then
# skip version merges
continue
fi
set +x
if [[ ! ${title} =~ ${MERGE_PR} ]]; then
echo "Unable to determine PR number for merge ${commit} with title '${title}', aborting." >&2
exit 1
fi
pr_number=${BASH_REMATCH[1]}
pr_type=$(cr::symbol-type ${prefix})
pr_title=${body}
if [[ ${pr_type} == "unknown" ]]; then
pr_title="${prefix} ${pr_title}"
fi
case ${pr_type} in
major)
breaking="${breaking}- ${pr_title} (#${pr_number})${NEWLINE}"
;;
minor)
features="${features}- ${pr_title} (#${pr_number})${NEWLINE}"
;;
patch)
bugfixes="${bugfixes}- ${pr_title} (#${pr_number})${NEWLINE}"
;;
docs|other)
# skip non-code-changes
;;
unknown)
unknown="${unknown}- ${pr_title} (#${pr_number})${NEWLINE}"
;;
*)
echo "unknown PR type '${pr_type}' on PR '${pr_title}'" >&2
exit 1
esac
done <<<$(git rev-list ${last_tag}..HEAD --merges --pretty=format:%B)

# TODO: sort non merge commits with tags

[[ -n "${breaking}" ]] && printf '\e[1;31mbreaking changes this version\e[0m' >&2
[[ -n "${unknown}" ]] && printf '\e[1;35munknown changes in this release -- categorize manually\e[0m' >&2

echo "" >&2
echo "" >&2
echo "# ${next_ver}"

if [[ -n ${breaking} ]]; then
echo ""
echo "## :warning: Breaking Changes"
echo ""
echo "${breaking}"
fi

if [[ -n ${features} ]]; then
echo ""
echo "## :sparkles: New Features"
echo ""
echo "${features}"
fi

if [[ -n ${bugfixes} ]]; then
echo ""
echo "## :bug: Bug Fixes"
echo ""
echo "${bugfixes}"
fi

if [[ -n ${unknown} ]]; then
echo ""
echo "## :question: *categorize these manually*"
echo ""
echo "${unknown}"
fi

echo ""
echo "*Thanks to all our contributors!*"

0 comments on commit a090929

Please sign in to comment.