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