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