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