-
-
Notifications
You must be signed in to change notification settings - Fork 10.4k
Expand file tree
/
Copy pathrelease-notes.sh
More file actions
executable file
·153 lines (141 loc) · 5.78 KB
/
Copy pathrelease-notes.sh
File metadata and controls
executable file
·153 lines (141 loc) · 5.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#!/bin/sh
# Generates release notes for an nvm release, in the format used in the
# annotated release tags and version bump commits.
#
# Usage: ./release-notes.sh <new-version> [<prev-ref>] [<end-ref>]
# <new-version> the version being released, e.g. `v0.40.7` (the leading `v` is optional)
# <prev-ref> list commits since this ref; defaults to the most recent `vX.Y.Z` tag
# <end-ref> list commits up to this ref; defaults to HEAD
#
# Commits from <prev-ref> to <end-ref> are grouped into sections by the leading
# `[bracket]` tag of their subject line, in `git log` order (newest first).
# Category tags ([New], [Fix], ...) are stripped from the bullet text;
# context tags ([readme], [actions], [meta], ...) are kept.
# Commits without a recognized tag land under "Misc", with a warning on stderr.
#
# Each bullet gets a ` (#123)` reference to the pull request that introduced
# the commit, looked up via `gh` from GitHub's commit->PR association, unless
# the subject already ends with one. Set NVM_RELEASE_NOTES_SKIP_PRS to a
# nonempty value to skip the lookup (references will be omitted).
NEW_VERSION="${1-}"
PREV_REF="${2-}"
END_REF="${3:-HEAD}"
if [ -z "${NEW_VERSION}" ]; then
printf >&2 'Usage: %s <new-version> [<prev-ref>] [<end-ref>]\n' "$0"
exit 2
fi
case "${NEW_VERSION}" in
v*) ;;
*) NEW_VERSION="v${NEW_VERSION}" ;;
esac
if [ -z "${PREV_REF}" ]; then
PREV_REF="$(git describe --abbrev=0 --tags --match 'v[0-9]*.[0-9]*.[0-9]*')" || {
printf >&2 'release-notes: failed to determine the previous version tag.\n'
exit 1
}
fi
for REF in "${PREV_REF}" "${END_REF}"; do
git rev-parse --quiet --verify "${REF}^{commit}" >/dev/null || {
# shellcheck disable=SC2016
printf >&2 'release-notes: `%s` is not a valid ref.\n' "${REF}"
exit 1
}
done
LOOKUP_PRS=1
if [ -n "${NVM_RELEASE_NOTES_SKIP_PRS-}" ]; then
LOOKUP_PRS=0
elif ! command -v gh >/dev/null 2>&1; then
# shellcheck disable=SC2016
printf >&2 'release-notes: `gh` is not available; PR references will be omitted.\n'
LOOKUP_PRS=0
fi
# the repo to look up commit->PR associations in, e.g. `nvm-sh/nvm`
REPO_SLUG="${GH_REPO-}"
if [ -z "${REPO_SLUG}" ]; then
ORIGIN_URL="$(git remote get-url origin 2>/dev/null)" || ORIGIN_URL=''
case "${ORIGIN_URL}" in
*github.com[:/]*) REPO_SLUG="$(printf '%s' "${ORIGIN_URL}" | sed 's#^.*github\.com[:/]##;s#\.git$##')" ;;
esac
fi
: "${REPO_SLUG:=nvm-sh/nvm}"
TAB="$(printf '\t')"
COMMITS_FILE="$(mktemp)" || exit 1
GH_ERR_FILE="$(mktemp)" || exit 1
trap 'rm -f "${COMMITS_FILE}" "${GH_ERR_FILE}"' EXIT
# each line: <section key> TAB <sha> TAB <bullet text>, in git log order
git log --no-merges --format='%H %s' "${PREV_REF}..${END_REF}" | while IFS=' ' read -r SHA SUBJECT; do
LOWERED="$(printf '%s' "${SUBJECT}" | tr '[:upper:]' '[:lower:]')"
STRIPPED="${SUBJECT#\[*\] }"
case "${LOWERED}" in
v[0-9]*.[0-9]*.[0-9]*)
case "${SUBJECT}" in
*' '*) # not a bump commit, just a subject that starts with a version
printf >&2 'release-notes: no category prefix, filing under Misc: %s\n' "${SUBJECT}"
KEY='misc' TEXT="${SUBJECT}"
;;
*) continue ;; # a version bump commit
esac
;;
'[new] '*) KEY='new' TEXT="${STRIPPED}" ;;
'[fix] '* | '[patch] '*) KEY='fix' TEXT="${STRIPPED}" ;;
'[refactor] '*) KEY='refactor' TEXT="${STRIPPED}" ;;
'[shellcheck] '*) KEY='refactor' TEXT="${SUBJECT}" ;;
'[robustness] '*) KEY='robustness' TEXT="${STRIPPED}" ;;
'[perf] '* | '[performance] '*) KEY='perf' TEXT="${STRIPPED}" ;;
'[dockerfile] '*) KEY='dockerfile' TEXT="${STRIPPED}" ;;
'[docs] '* | '[doc] '*) KEY='docs' TEXT="${STRIPPED}" ;;
'[readme] '*) KEY='docs' TEXT="${SUBJECT}" ;;
'[meta] '* | '[security] '* | '[dev deps] '*)
KEY='misc' TEXT="${SUBJECT}" ;;
'[tests] '* | '[test] '*) KEY='tests' TEXT="${STRIPPED}" ;;
'[actions] '* | '[debug] '*) KEY='tests' TEXT="${SUBJECT}" ;;
*)
printf >&2 'release-notes: no category prefix, filing under Misc: %s\n' "${SUBJECT}"
KEY='misc' TEXT="${SUBJECT}"
;;
esac
printf '%s\t%s\t%s\n' "${KEY}" "${SHA}" "${TEXT}"
done > "${COMMITS_FILE}"
if ! [ -s "${COMMITS_FILE}" ]; then
printf >&2 'release-notes: no commits found in %s..HEAD.\n' "${PREV_REF}"
fi
print_section() {
if ! grep -q "^$1${TAB}" "${COMMITS_FILE}"; then
return 0
fi
printf '\n%s\n\n' "$2"
grep "^$1${TAB}" "${COMMITS_FILE}" | while IFS="${TAB}" read -r _KEY SHA TEXT; do
REF=''
case "${TEXT}" in
*'(#'[0-9]*')') ;; # the subject already carries a reference
*)
if [ "${LOOKUP_PRS}" = 1 ]; then
PR="$(gh api "repos/${REPO_SLUG}/commits/${SHA}/pulls" --jq '.[0].number // empty' 2>"${GH_ERR_FILE}")" || {
if grep -q 'No commit found' "${GH_ERR_FILE}"; then
PR='' # not pushed to GitHub yet, so it can not have come from a PR
else
printf >&2 'release-notes: failed to look up the PR for commit %s:\n' "${SHA}"
cat >&2 "${GH_ERR_FILE}"
exit 1
fi
}
case "${PR}" in
'' | *[!0-9]*) ;;
*) REF=" (#${PR})" ;;
esac
fi
;;
esac
printf ' - %s%s\n' "${TEXT}" "${REF}"
done || exit 1
}
printf '%s\n' "${NEW_VERSION}"
print_section 'new' 'New Stuff' &&
print_section 'fix' 'Bug Fixes' &&
print_section 'refactor' 'Refactors' &&
print_section 'robustness' 'Robustness' &&
print_section 'perf' 'Performance' &&
print_section 'dockerfile' 'Dockerfile' &&
print_section 'docs' 'Docs' &&
print_section 'misc' 'Misc' &&
print_section 'tests' 'Tests'