release: convert some code into checkIsOlsrdGitCheckout function
[olsrd.git] / release / release.bash
1 #!/bin/bash
2
3 set -e
4 set -u
5
6
7 # ##############################################################################
8 # # Settings
9 # ##############################################################################
10
11 # The digit representation of a version can be in the format 0.6.4 or 0.6.4.1
12 declare versionRegexDigits="([[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+)(\.[[:digit:]]+)?"
13
14 # The version for source code can be in the format:
15 # - 0.6.4 or 0.6.4.1 or pre-0.6.4 or pre-0.6.4.1
16 declare versionRegexSources="(|pre-)(${versionRegexDigits})"
17
18 # The version for a release tag is in the format v0.6.4 or v0.6.4.1
19 declare versionRegexReleaseTag="v(${versionRegexDigits})"
20
21 # A release branch is in the format release-0.6.4 or release-0.6.4.1
22 declare relBranchRegex="release-(${versionRegexDigits})"
23
24
25
26
27 # ##############################################################################
28 # # Functions
29 # ##############################################################################
30
31 #
32 # Trim a string: remove spaces from the beginning and end of the string
33 #
34 # 1=string to trim
35 # return=trimmed string
36 function stringTrim() {
37   if [[ -z "${1}" ]]; then
38     return
39   fi
40
41   # remove leading whitespace characters
42   local var="${1#${1%%[![:space:]]*}}"
43
44   # remove trailing whitespace characters
45   echo "${var%${var##*[![:space:]]}}"
46 }
47
48
49 #
50 # Get the canonical path of a file or directory
51 # This is the physical path without any links
52 #
53 # 1=the file or directory
54 function pathCanonicalPath() {
55   local src="$(stringTrim "${1}")"
56
57   if [[ -h "${src}" ]] && [[ -d "${src}" ]]; then
58     # src is a link to a directory
59     pushd . &> /dev/null
60     cd -P "${src}" &> /dev/null
61     pwd -P
62     popd &> /dev/null
63     return
64   fi
65
66   # we're not dealing with a directory here
67   while [[ -h "${src}" ]]; do
68     # keep getting the link target while src is a link
69     src="$(ls -la "${src}" | \
70            sed -r 's#^.*?[[:space:]]+->[[:space:]]+(.*)$#\1#')"
71   done
72   # src is no longer a link here
73
74   pushd . &> /dev/null
75   cd -P "$(dirname "${src}")" &> /dev/null
76   echo "$(pwd -P)/$(basename "${src}")"
77   popd &> /dev/null
78 }
79
80
81 #
82 # Determine whether a given directory is a git repository directory
83 #
84 # 1=directory
85 # return=0 not a git dir, 1 a git dir
86 function gitIsGitDirectory() {
87   local place="$(stringTrim "${1}")"
88
89   local -i result=1
90   if [[ -d "${place}" ]]; then
91     pushd "${place}" &> /dev/null
92     set +e
93     git rev-parse --git-dir &> /dev/null
94     result=${?}
95     set -e
96     popd &> /dev/null
97   fi
98
99   if [[ ${result} -ne 0 ]]; then
100     echo "0"
101   else
102     echo "1"
103   fi
104 }
105
106
107 #
108 # Go into the root of the checkout and check some key files
109 #
110 function checkIsOlsrdGitCheckout() {
111   if [[ "$(gitIsGitDirectory ".")" == "0" ]] || \
112      [[ ! -r ./Makefile.inc ]] || \
113      [[ ! -r ./files/olsrd.conf.default.full ]]; then
114     echo "* You do not appear to be running the script from an olsrd git checkout"
115     exit 1
116   fi
117 }
118
119
120 #
121 # Get the version digits from a release tag version
122 #
123 # 1=release tag version
124 # return=version digits
125 function getVersionDigitsFromReleaseTag() {
126   echo "$(stringTrim "${1}")" | sed -r "s/${versionRegexReleaseTag}/\1/"
127 }
128
129
130 #
131 # Get the next version digits by incrementing the micro digit
132 #
133 # 1=version in format 0.6.4 or 0.6.4.1
134 # return=incremented version in format 0.6.5
135 function getNextVersionDigitsMicro() {
136   local version="$(stringTrim "${1}")"
137   local -a versionDigits=( ${version//\./ } )
138   local -i versionMicroNext=$(( ${versionDigits[2]} + 1 ))
139   echo "${versionDigits[0]}.${versionDigits[1]}.${versionMicroNext}"
140 }
141
142
143 #
144 # Get the next version digits by incrementing the patchlevel digit
145 #
146 # 1=version in format 0.6.4 or 0.6.4.0
147 # return=incremented version in format 0.6.4.1
148 function getNextVersionDigitsPatchLevel() {
149   local version="$(stringTrim "${1}")"
150   local -a versionDigits=( ${version//\./ } )
151   local -i versionPatchLevelNext=1
152   if [[ ${#versionDigits[*]} -ne 3 ]]; then
153     versionPatchLevelNext=$(( ${versionDigits[3]} + 1 ))
154   fi
155   echo "${versionDigits[0]}.${versionDigits[1]}.${versionDigits[2]}.${versionPatchLevelNext}"
156 }
157
158
159 #
160 # Adjust the branch name so that we can release 0.6.4.x from the
161 # release-0.6.4 branch
162 #
163 # prevTagVersionDigits  relBranchVersionDigits  relBranchVersionDigits (adjusted)
164 #       0.6.4                   0.6.4                   0.6.4.1
165 #       0.6.4                   0.6.5                   -
166 #       0.6.4                   0.6.4.5                 -
167 #       0.6.4                   0.6.5.5                 -
168 #       0.6.4.5                 0.6.4                   0.6.4.6
169 #       0.6.4.5                 0.6.5                   -
170 #       0.6.4.5                 0.6.4.6                 -
171 #       0.6.4.5                 0.6.5.6                 -
172 function adjustBranchName() {
173   local -a prevTagVersionDigitsArray=( ${prevTagVersionDigits//\./ } )
174   local -a relBranchVersionDigitsArray=( ${relBranchVersionDigits//\./ } )
175   local -i prevTagVersionDigitsCount=${#prevTagVersionDigitsArray[*]}
176   local -i relBranchVersionDigitsCount=${#relBranchVersionDigitsArray[*]}
177   local prevTagVersionTrain="$(echo "$(stringTrim "${prevTagVersionDigits}")" | \
178                                sed -r "s/${versionRegexDigits}/\1/")"
179
180   if  [[ "${prevTagVersionDigits}" == "${relBranchVersionDigits}" ]] || \
181      ([[ "${prevTagVersionTrain}"  == "${relBranchVersionDigits}" ]] && \
182       [[ ${prevTagVersionDigitsCount}   -eq 4 ]] && \
183       [[ ${relBranchVersionDigitsCount} -eq 3 ]]); then
184     relBranchVersionDigits="$(getNextVersionDigitsPatchLevel "${prevTagVersionDigits}")"
185   fi
186 }
187
188
189 #
190 # Check that the new version is incrementing
191 #
192 # 1=last version
193 # 2=new version
194 function checkVersionIncrementing() {
195   local lastVersion="$(stringTrim "${1}")"
196   local newVersion="$(stringTrim "${2}")"
197   local errorstr="* The new version ${newVersion} is not greater than the previous version ${lastVersion}"
198
199   local -a lastVersionDigits=( ${lastVersion//\./ } )
200   local -a newVersionDigits=( ${newVersion//\./ } )
201
202   # if the last version is in the format 0.6.4 then assume 0.6.4.0
203   if [[ ${#lastVersionDigits[*]} -ne 4 ]]; then
204     lastVersionDigits[3]=0
205   fi
206
207   # if the new version is in the format 0.6.4 then assume 0.6.4.0
208   if [[ ${#newVersionDigits[*]} -ne 4 ]]; then
209     newVersionDigits[3]=0
210   fi
211
212   # major
213   if [[ ${newVersionDigits[0]} -lt ${lastVersionDigits[0]} ]]; then
214     echo "${errorstr}"
215     exit 1
216   fi
217   if [[ ${newVersionDigits[0]} -gt ${lastVersionDigits[0]} ]]; then
218     return
219   fi
220
221   # minor
222   if [[ ${newVersionDigits[1]} -lt ${lastVersionDigits[1]} ]]; then
223     echo "${errorstr}"
224     exit 1
225   fi
226   if [[ ${newVersionDigits[1]} -gt ${lastVersionDigits[1]} ]]; then
227     return
228   fi
229
230   # micro
231   if [[ ${newVersionDigits[2]} -lt ${lastVersionDigits[2]} ]]; then
232     echo "${errorstr}"
233     exit 1
234   fi
235   if [[ ${newVersionDigits[2]} -gt ${lastVersionDigits[2]} ]]; then
236     return
237   fi
238
239   # patch level
240   if [[ ${newVersionDigits[3]} -lt ${lastVersionDigits[3]} ]]; then
241     echo "${errorstr}"
242     exit 1
243   fi
244   if [[ ${newVersionDigits[3]} -gt ${lastVersionDigits[3]} ]]; then
245     return
246   fi
247
248   # everything is equal
249   echo "${errorstr}"
250   exit 1
251 }
252
253
254 #
255 # Commit the current changes, allow an empty commit, or amend (when the commit
256 # message is the same as that of the last commit)
257 #
258 # 1=commit message
259 function commitChanges() {
260   local -i allowEmpty=${1}
261   local msg="$(stringTrim "${2}")"
262
263   local lastMsg="$(git log -1 --format="%s")"
264   lastMsg="$(stringTrim "${lastMsg}")"
265   local extra=""
266   if [[ ${allowEmpty} -ne 0 ]]; then
267     extra="${extra} --allow-empty"
268   fi
269   if [[ "${msg}" == "${lastMsg}" ]]; then
270     extra="${extra} --amend"
271   fi
272   set +e
273   git commit -s -q ${extra} -m "${msg}" &> /dev/null
274   set -e
275 }
276
277
278 #
279 # Update the version in all relevant files
280 #
281 # 1=the new version (in the format of versionRegexSources)
282 function updateVersions() {
283   local newVersion="$(stringTrim "${1}")"
284
285   #
286   # Adjust debug settings in Makefile.inc
287   #
288   local src="Makefile.inc"
289   sed -ri \
290    -e 's/^[[:space:]]*DEBUG[[:space:]]*?=.*$/DEBUG ?= 1/' \
291    -e 's/^[[:space:]]*NO_DEBUG_MESSAGES[[:space:]]*?=.*$/NO_DEBUG_MESSAGES ?= 0/' \
292    "${src}"
293   set +e
294   git add "${src}"
295   set -e
296
297
298   #
299   # Adjust version in Makefile
300   #
301   local src="Makefile"
302   sed -ri "s/^([[:space:]]*VERS[[:space:]]*=[[:space:]]*)${versionRegexSources}[[:space:]]*\$/\1${newVersion}/" "${src}"
303   set +e
304   git add "${src}"
305   set -e
306
307
308   #
309   # Adjust version in win32 gui installer
310   #
311   local src="gui/win32/Inst/installer.nsi"
312   local grepStr="^([[:space:]]*MessageBox[[:space:]]+MB_YESNO[[:space:]]+\".+?[[:space:]]+olsr\.org[[:space:]]+)${versionRegexSources}([[:space:]]+.+?\"[[:space:]]+IDYES[[:space:]]+NoAbort)[[:space:]]*$"
313   local replStr="\1${newVersion}\6"
314   sed -ri "s/${grepStr}/${replStr}/" "${src}"
315   set +e
316   git add "${src}"
317   set -e
318
319
320   #
321   # Adjust version in win32 gui front-end
322   #
323   local src="gui/win32/Main/Frontend.rc"
324   local grepStr="^([[:space:]]*CAPTION[[:space:]]+\"olsr\.org[[:space:]]+Switch[[:space:]]+)${versionRegexSources}([[:space:]]*\")[[:space:]]*\$"
325   local replStr="\1${newVersion}\6"
326   sed -ri "s/${grepStr}/${replStr}/" "${src}"
327   set +e
328   git add "${src}"
329   set -e
330 }
331
332
333 #
334 # Sign a text file
335 #
336 # 1=the text file
337 function signTextFile() {
338   local txtFile="$(stringTrim "${1}")"
339   gpg -u "$(git config --get user.name)" --clearsign "${txtFile}"
340   mv "${txtFile}.asc" "${txtFile}"
341 }
342
343
344
345
346 # ##############################################################################
347 # # Main
348 # ##############################################################################
349
350 declare script="$(pathCanonicalPath "${0}")"
351 declare scriptDir="$(dirname "${script}")"
352 declare baseDir="$(dirname "${scriptDir}")"
353 unset script
354
355 cd "${baseDir}"
356
357
358 #
359 # Check the number of arguments
360 #
361 if [[ ${#} -ne 0 ]]; then
362   echo "* Need no arguments"
363   exit 1
364 fi
365
366
367 checkIsOlsrdGitCheckout
368
369
370 #
371 # Check that a signing key is configured
372 #
373 declare gpgKeyId="$(git config --get user.signingkey)"
374 if [[ -z "${gpgKeyId}" ]]; then
375   cat >&1 << EOF
376 * No signing key is setup for git, please run
377     git config --global user.signingkey <key ID>
378
379   You can get keys and IDs by running 'gpg --list-keys'
380 EOF
381   exit 1
382 fi
383
384
385 #
386 # Check that the signing key is present
387 #
388 set +e
389 gpg --list-key "${gpgKeyId}" &> /dev/null
390 declare -i gpgKeyIdPresentResult=${?}
391 set -e
392 if [[ ${gpgKeyIdPresentResult} -ne 0 ]]; then
393   cat >&1 << EOF
394 * Your signing key with ID ${gpgKeyId} is not found, please run
395     git config --global user.signingkey <key ID>
396   to setup a valid key ID.
397
398   You can get keys and IDs by running 'gpg --list-keys'
399 EOF
400   exit 1
401 fi
402
403
404 #
405 # Get the previous release tag and check
406 #
407 set +e
408 declare prevRelTagVersion="$(git describe --abbrev=0 | \
409                              grep -E "^${versionRegexReleaseTag}$")"
410 set -e
411 if [[ -z "${prevRelTagVersion}" ]]; then
412   echo "* Could not find the previous release tag"
413   exit 1
414 fi
415 declare prevTagVersionDigits="$(getVersionDigitsFromReleaseTag "${prevRelTagVersion}")"
416
417
418 #
419 # Get the current branch and check that we're on a release branch
420 #
421 declare relBranch="$(git rev-parse --abbrev-ref HEAD)"
422 declare relBranch="$(stringTrim "${relBranch}")"
423 if [[ -z "$(echo "${relBranch}" | grep -E "^${relBranchRegex}\$")" ]]; then
424   echo "* You are not on a release branch (format: release-0.6.4 or release-0.6.4.1)"
425   exit 1
426 fi
427
428
429 #
430 # Get the version to release from the current branch
431 #
432 declare relBranchVersionDigits="$(echo "${relBranch}" | \
433                                   sed -r "s/${relBranchRegex}/\1/")"
434
435 adjustBranchName
436
437 declare relTagVersion="v${relBranchVersionDigits}"
438 declare relBranchVersionDigitsNextMicro="$(getNextVersionDigitsMicro "${relBranchVersionDigits}")"
439 declare relBranchVersionDigitsNextPatchLevel="$(getNextVersionDigitsPatchLevel "${relBranchVersionDigits}")"
440
441
442 #
443 # Check that the version is incrementing
444 #
445 checkVersionIncrementing "${prevTagVersionDigits}" "${relBranchVersionDigits}"
446
447
448 #
449 # Confirm the release
450 #
451 cat >&1 << EOF
452
453
454 * All checks pass, ready to release ${relBranchVersionDigits}.
455
456   * The previous version found is: ${prevTagVersionDigits}
457     Note: If this is not the version you were expecting, then maybe that
458           version wasn't merged into this branch.
459   * Continuing will DESTROY any modifications you currently have in your tree!
460
461 EOF
462 read -p "Press [enter] to continue or CTRL-C to exit..."
463 echo ""
464 echo ""
465
466
467 #
468 # Clean up the checkout
469 #
470 echo "Cleaning the git checkout..."
471 git clean -fdq
472 git reset -q --hard
473
474
475 #
476 # Update the versions for release
477 #
478 echo "Updating the version to ${relBranchVersionDigits}..."
479 updateVersions "${relBranchVersionDigits}"
480 commitChanges 1 "Release ${relTagVersion}"
481
482
483
484 #
485 # Generate the changelog
486 #
487 echo "Generating the changelog..."
488 declare src="CHANGELOG"
489 declare dst="mktemp -q -p . -t "${src}.XXXXXXXXXX""
490 cat > "${dst}" << EOF
491 ${relBranchVersionDigits} -------------------------------------------------------------------
492
493 EOF
494 git rev-list --pretty=short "${prevRelTagVersion}..HEAD" | \
495   git shortlog -w80 -- >> "${dst}"
496 cat "${src}" >> "${dst}"
497 mv "${dst}" "${src}"
498 set +e
499 git add "${src}"
500 set -e
501 commitChanges 1 "Release ${relTagVersion}"
502
503
504 #
505 # Tag the release
506 #
507 echo "Tagging ${relTagVersion}..."
508 set +e
509 git tag -d "${relTagVersion}" &> /dev/null
510 set -e
511 git tag -s -m "OLSRd release ${relBranchVersionDigits}" "${relTagVersion}"
512
513
514 #
515 # Update the version to the next release
516 #
517 echo "Updating the version to pre-${relBranchVersionDigitsNextPatchLevel}..."
518 updateVersions "pre-${relBranchVersionDigitsNextPatchLevel}"
519 commitChanges 1 "Update version after release of ${relTagVersion}"
520
521
522 #
523 # Update the version (on the master branch) to the next release
524 #
525 echo "Updating the version to pre-${relBranchVersionDigitsNextMicro} on the master branch..."
526 git checkout -q master
527 git clean -fdq
528 git reset -q --hard
529 updateVersions "pre-${relBranchVersionDigitsNextMicro}"
530 commitChanges 0 "Update version after release of ${relTagVersion}"
531 git checkout -q "${relBranch}"
532 git clean -fdq
533 git reset -q --hard
534
535
536 #
537 # Make the release tarballs
538 #
539 echo "Generating the release tarballs..."
540 declare tarFile="${scriptDir}/olsrd-${relBranchVersionDigits}.tar"
541 declare tarGzFile="${tarFile}.gz"
542 declare tarBz2File="${tarFile}.bz2"
543 git archive --format=tar --output="${tarFile}" "${relTagVersion}"
544 gzip   -c "${tarFile}" > "${tarGzFile}"
545 bzip2  -c "${tarFile}" > "${tarBz2File}"
546 rm -f "${tarFile}"
547 echo "Generating the release tarball checksums..."
548 declare md5File="${scriptDir}/MD5SUM-${relBranchVersionDigits}"
549 declare sha256File="${scriptDir}/SHA256SUM-${relBranchVersionDigits}"
550 md5sum    "${tarGzFile}" "${tarBz2File}" > "${md5File}"
551 sha256sum "${tarGzFile}" "${tarBz2File}" > "${sha256File}"
552 echo "Signing the release tarball checksums..."
553 signTextFile "${md5File}"
554 signTextFile "${sha256File}"
555
556
557 echo "Done."
558
559
560 echo ""
561 echo ""
562 echo "==================="
563 echo "=   Git Updates   ="
564 echo "==================="
565 echo "Branch : master"
566 echo "Branch : ${relBranch}"
567 echo "Tag    : ${relTagVersion}"
568 echo ""
569 echo ""
570
571
572 echo "==================="
573 echo "= Generated Files ="
574 echo "==================="
575 cat >&1 << EOF
576 ${tarGzFile}
577 ${tarGzFile}
578 ${md5File}
579 ${sha256File}"
580 EOF
581 echo ""
582 echo ""
583
584
585 echo "==================="
586 echo "= Manual Actions  ="
587 echo "==================="
588 echo "1. Check that everything is in order. For example, run:"
589 echo "     gitk master ${relBranch} ${relTagVersion} "
590 echo "2. Push. For example, run:"
591 echo "     git push origin master ${relBranch} ${relTagVersion}"
592 echo "3. Upload the generated files to"
593 echo "     http://www.olsr.org/releases/${relBranchVersionDigits}"
594 echo "4. Add a release article on olsr.org."
595 echo ""
596