-
Notifications
You must be signed in to change notification settings - Fork 34
/
.make-release-support
378 lines (336 loc) · 11.2 KB
/
.make-release-support
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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
#!/usr/bin/env bash
# Copyright 2015 Xebia Nederland B.V.
# Copyright (c) 2014-2015 François Saint-Jacques <[email protected]>
# for semver regex (https://github.com/fsaintjacques/semver-tool)
SCRIPT_SRC="$(dirname "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}")"
SCRIPT_DIR="$(cd "${SCRIPT_SRC}" >/dev/null 2>&1 && pwd)"
# Some constants for SEMVER regexp
NAT='0|[1-9][0-9]*'
ALPHANUM='[0-9]*[A-Za-z-][0-9A-Za-z-]*'
IDENT="$NAT|$ALPHANUM"
FIELD='[0-9A-Za-z-]+'
SEMVER_REGEX="\
^[vV]?\
($NAT)\\.($NAT)\\.($NAT)\
(\\-(${IDENT})(\\.(${IDENT}))*)?\
(\\+${FIELD}(\\.${FIELD})*)?$"
PRERELEASE_REGEX="^\\-rc(${NAT})$"
# Uses git status in short format to see if there are outstanding files that
# have not been commited
function hasChanges() {
test -n "$(git status -s .)"
}
# This returns the version contained in Cargo.toml
function getRelease() {
local rel=$(cat Cargo.toml | grep '^version' | cut -d '=' -f 2 | tr -d \")
rel="${rel#"${rel%%[![:space:]]*}"}" # remove leading whitespace characters
echo "$rel"
}
function getTag() {
# FIXME Understand what arguments are provided
if [ -z "$1" ] ; then
echo "v$(getRelease)"
else
echo "v$1"
fi
}
function getLastTag() {
# We get the last tag that is not a prerelease (ie does not contain rc)
git tag -l --sort=version:refname v* | grep -v rc | tail -n 1
}
# This function returns the type of release (ie patch, minor, major).
# To do this, we first get the tag of the last rc0.
# We use this tag to display the commit message,
# which is expected to be something like
# [Versioned] new XXX prerelease.
# So by splitting at spaces, we just extract the third field.
# (it brittle)
function getReleaseMessage() {
local rc0=$(git tag -l --sort=version:refname | grep rc0 | tail -n 1)
local release_type=$(git log --format=%B -n 1 ${rc0} | cut -d ' ' -f 3)
echo "new ${release_type} release"
}
# This function returns the tag(s) for use with Docker.
# If the version (from getRelease) is a prerelease (finishes with rcN),
# then we only return this value.
# If the versios is a release (X.Y.Z), then we return an array of strings,
# X X.Y X.Y.Z
function getDockerTags() {
local ORIGINAL=$(getRelease)
if [[ "$ORIGINAL" =~ $SEMVER_REGEX ]]; then
local MAJOR=${BASH_REMATCH[1]}
local MINOR=${BASH_REMATCH[2]}
local PATCH=${BASH_REMATCH[3]}
local PRERE=${BASH_REMATCH[4]}
if [ -n "$PRERE" ]; then
echo "$ORIGINAL"
else
echo "${MAJOR} ${MAJOR}.${MINOR} ${MAJOR}.${MINOR}.${PATCH}"
fi
else
echo "$ORIGINAL"
fi
}
# Updates the version in Cargo.toml
# $1 Version (must match semver)
# This function works in 3 steps:
# 1. validate the argument
# 2. validate the version
# 3. set the version. This part, which is very specific to the project this script is embedded in,
# has been extracted in the function `setReleaseProject`.
function setRelease() {
local VERSION="$1"
echo "Setting the new release to ${VERSION}"
# Check that VERSION is set and non-empty
[[ -z "${VERSION+xxx}" ]] &&
{ echo "The variable \$VERSION is not set. Make sure it is set before using setRelease."; return 1; }
[[ -z "${VERSION}" && "${VERSION+xxx}" = "xxx" ]] &&
{ echo "The variable \$VERSION is set but empty. Make sure it is not empty before using setRelease."; return 1; }
validate_version ${VERSION}
[[ $? != 0 ]] &&
{ echo "${VERSION} is not valid semantic version"; return 1; } ||
{ setReleaseProject "${VERSION}"; }
return 0
}
# This function assumes the version given as argument has been validated.
# It needs to be customized for each project. It writes the version ($1) to file(s).
function setReleaseProject() {
local VERSION="$1"
sed -i -e "s/^version\s*=\s*\".*\"/version = \"$VERSION\"/" Cargo.toml
sed -i -e "s/^version\s*=\s*\".*\"/version = \"$VERSION\"/" libs/bragi/Cargo.toml
sed -i -e "s/^version\s*=\s*\".*\"/version = \"$VERSION\"/" libs/mimir/Cargo.toml
sed -i -e "s/^version\s*=\s*\".*\"/version = \"$VERSION\"/" libs/tools/Cargo.toml
sed -i -e "s/^version\s*=\s*\".*\"/version = \"$VERSION\"/" libs/docker_wrapper/Cargo.toml
}
# Not used
function runPreTagCommand() {
if [ -n "$1" ] ; then
COMMAND=$(sed -n -e "s/@@RELEASE@@/$1/g" -e 's/^pre_tag_command=\(.*\)/\1/p' .release)
if [ -n "$COMMAND" ] ; then
if ! OUTPUT=$(bash -c "$COMMAND" 2>&1) ; then echo $OUTPUT >&2 && exit 1 ; fi
fi
else
echo "ERROR: missing release version parameter " >&2
return 1
fi
}
# This function retrieves the candidate tag from Cargo.toml (with getTag),
# and make sure it is available with git tag.
function tagExists() {
tag=${1:-$(getTag)}
test -n "$tag" && test -n "$(git tag | grep "^$tag\$")"
}
function differsFromRelease() {
tag=$(getTag)
! tagExists $tag || test -n "$(git diff --shortstat -r $tag .)"
}
# This function retrieves the release, and if the git tag differs from
# the version in Cargo.toml, add the commit, and also add 'dirty' if the
# version has outstanding changes.
function getVersion() {
local result=$(getRelease)
if differsFromRelease; then
result="${result}-$(git log -n 1 --format=%h .)"
fi
if hasChanges ; then
result="${result}-dirty"
fi
echo "${result}"
}
# $1 version
# $2 variable to set
# Returns 0 and set $2 if $1 follows semver, returns 1 otherwise
function validate_version() {
local version=$1
[[ "$version" =~ $SEMVER_REGEX ]] && { return 0; } || { return 1; }
}
# $1 version
# $2 variable to set
# Returns 0 and set $2 if $1 follows semver, returns 1 otherwise
function decompose_version() {
if [ "$#" -ne "2" ]; then
echo "You must supply 2 arguments"
return 1
fi
local version=$1
if [[ "$version" =~ $SEMVER_REGEX ]]; then
local major=${BASH_REMATCH[1]}
local minor=${BASH_REMATCH[2]}
local patch=${BASH_REMATCH[3]}
local prere=${BASH_REMATCH[4]}
local build=${BASH_REMATCH[8]}
eval "$2=(\"$major\" \"$minor\" \"$patch\" \"$prere\" \"$build\")"
return 0
else
return 1
fi
}
function validate_prerelease() {
if [ "$#" -ne "2" ]; then
echo "You must supply 2 arguments"
return 1
fi
local prerelease=$1
if [[ "$prerelease" =~ $PRERELEASE_REGEX ]]; then
local iter=${BASH_REMATCH[1]}
eval "$2=$iter"
return 0
else
echo "prerelease $prerelease does not match the expected scheme '-rcN'. See help for more information."
return 1
fi
}
function nextMajorPrerelease() {
local ORIGINAL=$(getRelease)
decompose_version "$ORIGINAL" SEMVER
if [ $? -eq 0 ]; then
local MAJOR="${SEMVER[0]}"
MAJOR=$(($MAJOR + 1))
local VERSION="${MAJOR}.0.0-rc0"
echo "${VERSION}"
else
echo "${ORIGINAL}"
fi
}
function nextMinorPrerelease() {
local ORIGINAL=$(getRelease)
decompose_version "$ORIGINAL" SEMVER
if [ $? -eq 0 ]; then
local MAJOR="${SEMVER[0]}"
local MINOR="${SEMVER[1]}"
MINOR=$(($MINOR + 1))
local VERSION="${MAJOR}.${MINOR}.0-rc0"
echo "${VERSION}"
else
echo "${ORIGINAL}"
fi
}
function nextPatchPrerelease() {
local ORIGINAL=$(getRelease)
decompose_version "$ORIGINAL" SEMVER
if [ $? -eq 0 ]; then
local MAJOR="${SEMVER[0]}"
local MINOR="${SEMVER[1]}"
local PATCH="${SEMVER[2]}"
PATCH=$(($PATCH + 1))
local VERSION="${MAJOR}.${MINOR}.${PATCH}-rc0"
echo "${VERSION}"
else
echo "${ORIGINAL}"
fi
}
function nextPrerelease() {
local ORIGINAL=$(getRelease)
decompose_version "$ORIGINAL" SEMVER
if [ $? -eq 0 ]; then
local MAJOR="${SEMVER[0]}"
local MINOR="${SEMVER[1]}"
local PATCH="${SEMVER[2]}"
local PRERE="${SEMVER[3]}"
validate_prerelease "$PRERE" i
local VERSION="$MAJOR.$MINOR.$PATCH-rc$((i + 1))"
echo "${VERSION}"
else
echo "${ORIGINAL}"
fi
}
function nextRelease() {
local ORIGINAL=$(getRelease)
decompose_version "$ORIGINAL" SEMVER
if [ $? -eq 0 ]; then
local MAJOR="${SEMVER[0]}"
local MINOR="${SEMVER[1]}"
local PATCH="${SEMVER[2]}"
local VERSION="$MAJOR.$MINOR.$PATCH"
echo "${VERSION}"
else
echo "${ORIGINAL}"
fi
}
# $1 tag.
# We will returning the changelog from last tag till HEAD
# We extract the commit msg, followed by a delimiter, followed by the hash
function changeLog() {
git log $1..HEAD --pretty='format:%s -- %h' | sort
}
# We receive the log as a multiline string, one commit per line, containing the message, followed by the hash
# We create a dictionary, which contains, for each type of commit, an array of hash
# But since we can't work with arrays inside a dictionary, it's just a string with -- delimiters between hashes.
function splitLog() {
declare -A assoc
str="$1"
# FIXME SAVE IFS
IFS=$'\n'
for line in $(echo "${str}"); do
# echo "orig: $line"
re="\[([[:alpha:]]+)\] .* -- (.*)"
if [[ $line =~ $re ]]; then
local commit_type="${BASH_REMATCH[1]}"
local commit_hash="${BASH_REMATCH[2]}"
local res="${assoc["${commit_type}"]}"
if [[ -z "$res" && "${res+xxx}" = "xxx" ]]; then
assoc["$commit_type"]="$commit_hash"
else
assoc["$commit_type"]="$res--$commit_hash"
fi
fi
done
# For debug
# for i in ${!assoc[@]}; do
# echo "$i ${assoc[$i]}"
# done
# Now we generate the output. So we iterate through all the commit types,
# and for each commit type, we iterate through the list of hashes.
# For each hash, we use git show to display the information
for commit_type in ${!assoc[@]}; do
# We don't want to list all the Version related commits... so skip them.
if [ "$commit_type" != "Versioned" ]; then
local commits="${assoc["$commit_type"]}"
delimiter="--"
conCatString=$commits$delimiter
splitMultiChar=()
while [[ $conCatString ]]; do
splitMultiChar+=( "${conCatString%%"$delimiter"*}" )
conCatString=${conCatString#*"$delimiter"}
done
echo "### ${commit_type}:" >> /tmp/changelog.md
for commit in "${splitMultiChar[@]}"; do
local change=$(git show -s --pretty='format:%s, %an, %as, %h' ${commit})
# We remove the [xxx] because its redundant with the section
# this syntax means keep everything after first space
change="${change#* }"
echo "- ${change}" >> /tmp/changelog.md
done
echo "\n" >> /tmp/changelog.md
fi
done
}
# $1 current tag
# $2 old tag
function generateChangelog() {
# FIXME Use temp file instead
rm -f /tmp/changelog.md
local day="$(date +%Y-%m-%dT%H:%M:%SZ)"
echo "## $1\n" >> /tmp/changelog.md
echo "Released $day\n">> /tmp/changelog.md
echo "" >> /tmp/changelog.md
local log=$(changeLog $2)
echo "$log"
splitLog "$log"
# Now to generate the log, we remove the header, prepend
# the new changelog on the old one, and put the header back.
# In the following line, the +11 matches the height of the header.
tail -n +11 CHANGELOG.md > /tmp/old-changelog.md
cat /tmp/changelog.md /tmp/old-changelog.md > CHANGELOG.md
addChangelogHeader
}
function addChangelogHeader() {
read -r -d '' header <<'EOF'
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
This file is generated automatically by the release procedure, please do not edit.
EOF
echo -e "${header}\n\n\n$(cat CHANGELOG.md)" > CHANGELOG.md
}