]> git.sur5r.net Git - openocd/blob - tools/release.sh
de0946b78b33f7e1391c6e78bb4fe1db5d274d1e
[openocd] / tools / release.sh
1 #!/bin/sh -e
2 # release.sh: openocd release process automation
3 # Copyright (C) 2009 by Zachary T Welch <zw@superlucidity.net>
4 # Release under the GNU GPL v2 (or later versions).
5
6 ## set these to control the build process
7 #CONFIG_OPTS=""
8 #MAKE_OPTS=""
9
10 ## DO NOT PERFORM LIVE RELEASES UNLESS YOU ARE THE RELEASE MANAGER!!!
11 RELEASE_DRY_RUN=1
12 ## set this to perform individual steps on past releases
13 RELEASE_VERSION=
14
15 die() {
16         echo "$@" >&2
17         exit 1
18 }
19
20 svn_info_get() {
21         svn info | grep "$1" | cut -d':' -f2- | cut -c2-
22 }
23
24 svn_setup_load() {
25         SVN_ROOT="$(svn_info_get 'Repository Root')"
26         SVN_URL="$(svn_info_get 'URL')"
27
28         SVN_TRUNK="${SVN_ROOT}/trunk"
29
30         SVN_BRANCHES="${SVN_ROOT}/branches"
31         PACKAGE_BRANCH="${SVN_BRANCHES}/${PACKAGE_RELEASE}"
32
33         SVN_TAGS="${SVN_ROOT}/tags"
34         PACKAGE_TAG="${SVN_TAGS}/${PACKAGE_RELEASE}"
35
36         if [ "${SVN_URL}" = "${SVN_TRUNK}" ]; then
37                 RELEASE_TYPE=minor
38         elif [ "${SVN_URL/${SVN_BRANCHES}/}" != "${SVN_URL}" ]; then
39                 RELEASE_TYPE=micro
40         else
41                 echo "error: bad URL: ${SVN_URL}" >&2
42                 die "unable to branch from the current location"
43         fi
44 }
45 svn_setup_show() {
46         cat <<INFO
47 Release Type: ${RELEASE_TYPE}
48   Branch URL: ${PACKAGE_BRANCH}
49      Tag URL: ${PACKAGE_TAG}
50 INFO
51 }
52
53 do_svn_echo_msg() { echo "svn: $1: $3"; }
54 do_svn_echo() {
55         case "$1" in
56         commit)
57                 do_svn_echo_msg "$@"
58                 shift 3
59                 [ "$*" ] && echo "Files: $@"
60                 ;;
61         copy|move)
62                 do_svn_echo_msg "$@"
63                 echo "From: ${4:-$2}"
64                 echo "  To: ${5:-$3}"
65                 ;;
66         *)
67                 local ACTION="$1"
68                 shift
69                 echo "svn: ${ACTION}: $@"
70                 ;;
71         esac
72 }
73 do_svn() {
74         do_svn_echo "$@"
75         [ "${RELEASE_DRY_RUN}" ] || svn "$@"
76 }
77 do_svn_switch() {
78         do_svn switch "$@"
79         package_info_load
80 }
81
82
83 package_info_load_name() {
84         grep AC_INIT configure.in | perl -ne 's/^.+\(\[([-\w]*)\],.+$/$1/ and print'
85 }
86 package_info_load_version() {
87         grep AC_INIT configure.in | perl -ne 's/^.+\[([-\w\.]*)\],$/$1/ and print'
88 }
89
90 package_info_load() {
91         [ -f "configure.in" ] || \
92                 die "package_info_load: configure.in is missing"
93
94         PACKAGE_NAME="$(package_info_load_name)"
95         # todo: fix this
96         PACKAGE_TARNAME="${PACKAGE_NAME}"
97
98         PACKAGE_VERSION="$(package_info_load_version)"
99         [ "${RELEASE_VERSION}" ] || \
100                 RELEASE_VERSION=${PACKAGE_VERSION/-in-development/}
101
102         [ "${PACKAGE_NAME}" -a "${PACKAGE_VERSION}" ] || \
103                 die "package information is missing from configure script"
104
105         PACKAGE_VERSION_TAGS=
106         [ "${PACKAGE_VERSION/-/}" = "${PACKAGE_VERSION}" ] || \
107                 PACKAGE_VERSION_TAGS="-${PACKAGE_VERSION#*-}"
108         PACKAGE_VERSION_BASE="${PACKAGE_VERSION%%-*}"
109         PACKAGE_MICRO="${PACKAGE_VERSION_BASE##*.}"
110         PACKAGE_MAJOR_AND_MINOR="${PACKAGE_VERSION_BASE%.*}"
111         PACKAGE_MAJOR="${PACKAGE_MAJOR_AND_MINOR%.*}"
112         PACKAGE_MINOR="${PACKAGE_MAJOR_AND_MINOR#*.}"
113
114         PACKAGE_STRING="${PACKAGE_NAME} ${PACKAGE_VERSION}"
115         if [ "${RELEASE_DRY_RUN}" ]; then
116                 PACKAGE_RELEASE="${PACKAGE_TARNAME}-${PACKAGE_VERSION}"
117         else
118                 PACKAGE_RELEASE="${PACKAGE_TARNAME}-${RELEASE_VERSION}"
119         fi
120 }
121
122 package_info_show() {
123         cat <<INFO
124 Name: ${PACKAGE_TARNAME}
125 Release: ${RELEASE_VERSION}
126 Version: ${PACKAGE_VERSION}
127    Number: ${PACKAGE_VERSION_BASE}
128    Series: ${PACKAGE_MAJOR_AND_MINOR}
129     Major: ${PACKAGE_MAJOR}
130     Minor: ${PACKAGE_MINOR}
131     Micro: ${PACKAGE_MICRO}
132      Tags: ${PACKAGE_VERSION_TAGS}
133  Branch: ${PACKAGE_RELEASE}
134 Release: ${PACKAGE_TARNAME}-${PACKAGE_VERSION_BASE}${PACKAGE_VERSION_TAGS}
135 INFO
136 }
137
138 usage() {
139         cat << USAGE
140 usage: $0 <command>
141
142 Main Commands:
143   info          Show a summary of the next pending release.
144   release       Release the current tree as an archive.
145   upload        Upload archives to berliOS project site
146
147 Build Commands:
148   bootstrap     Prepare the working copy for configuration and building.
149   configure     Configures the package; runs bootstrap, if needed.
150   build         Compiles the project; runs configure, if needed.
151
152 Packaging Commands:
153   changelog     Generate a new ChangeLog using svn2cl.
154   package       Produce new distributable source archives.
155   stage         Move archives to staging area for upload.
156
157 Repository Commands:
158   commit        Perform branch and tag, as appropriate for the version.
159   branch        Create a release branch from the project trunk.
160   tag           Create a tag for the current release branch.
161
162 Other Commands:
163   version ...   Perform version number and tag manipulations.
164   maryslamb     Mary had a little lamb, but no one noticed.
165   clean         Forces regeneration of results.
166   clean_all     Removes all traces of the release process.
167   help          Provides this list of commands.
168   
169 For more information about this script, see the Release Processes page
170 in the OpenOCD Developer's Manual (doc/manual/release.txt).
171
172 WARNING: This script should be used by the Release Manager ONLY.
173 USAGE
174         exit 0
175 }
176 do_usage() { usage; }
177 do_help()  { usage; }
178
179 do_info_show() {
180         echo "Current Release Analysis:"
181         package_info_show
182         svn_setup_show
183 }
184
185 do_info() {
186         package_info_load
187         svn_setup_load
188         do_info_show
189 }
190
191 do_bootstrap() {
192         echo -n "Bootstrapping..."
193         ./bootstrap 2>&1 | perl tools/logger.pl > "release-bootstrap.log"
194 }
195 maybe_bootstrap() { [ -f "configure" ] || do_bootstrap; }
196
197 do_configure() {
198         maybe_bootstrap
199         echo -n "Configuring..."
200         ./configure ${CONFIG_OPTS} 2>&1 | perl tools/logger.pl > "release-config.log"
201 }
202 maybe_configure() { [ -f "Makefile" ] || do_configure; }
203
204 do_build() {
205         maybe_configure
206         echo -n "Compiling OpenOCD ${PACKAGE_VERSION}"
207         make ${MAKE_OPTS} -C doc stamp-vti 2>&1 \
208                 | perl tools/logger.pl > "release-version.log"
209         make ${MAKE_OPTS} 2>&1 \
210                 | perl tools/logger.pl > "release-make.log"
211 }
212 maybe_build() { [ -f "src/openocd" ] || do_build; }
213 do_build_clean() { [ -f Makefile ] && make maintainer-clean >/dev/null; }
214
215 do_changelog() {
216         echo "Updating working copy to HEAD..."
217         do_svn update
218         echo "Creating ChangeLog..."
219         svn2cl -i --authors AUTHORS.ChangeLog
220 }
221 maybe_changelog() {
222         if [ -z "${RELEASE_DRY_RUN}" ] \
223                 || [ ! -f ChangeLog ] \
224                 || [ "$(cat ChangeLog | wc -l)" -lt 2 ]
225         then
226                 do_changelog
227         fi
228 }
229 do_changelog_clean() {
230         do_svn revert ChangeLog
231 }
232
233 do_package() {
234         package_info_load
235         maybe_changelog
236         maybe_build
237         echo "Building distribution packages..."
238         make ${MAKE_OPTS} distcheck 2>&1 | perl tools/logger.pl > "release-pkg.log"
239 }
240 maybe_package() { [ -f "${PACKAGE_RELEASE}.zip" ] || do_package; }
241 do_package_clean() {
242         for EXT in tar.gz tar.bz2 zip; do
243                 rm -v -f *.${EXT}
244         done
245 }
246
247 do_stage() {
248         maybe_package
249         echo "Staging package archives:"
250         mkdir -p archives
251         for EXT in tar.gz tar.bz2 zip; do
252                 local FILE="${PACKAGE_RELEASE}.${EXT}"
253                 # create archive signatures
254                 for HASH in md5 sha1; do
255                         echo "sign: ${FILE}.${HASH}"
256                         ${HASH}sum "${FILE}" > "archives/${FILE}.${HASH}"
257                 done
258                 # save archive
259                 mv -v "${FILE}" archives/
260         done
261         cp -a NEWS archives/
262         cp -a ChangeLog archives/
263 }
264 do_stage_clean() { rm -v -f -r archives; }
265
266 do_clean() {
267         do_build_clean
268         do_package_clean
269         rm -v -f configure
270
271         svn revert configure.in
272         rm -v -f release-*.log
273 }
274 do_clean_all() {
275         do_clean
276         do_changelog_clean
277         do_stage_clean
278 }
279
280 do_version_usage() {
281         cat << USAGE
282 usage: $0 version <command>
283 Version Commands:
284   tag {add|remove} <label>     Add or remove the specified tag.
285   bump {major|minor|micro|rc}  Bump the specified version number;
286                                resets less-significant numbers to zero.
287                                All but 'rc' releases drop that tag.
288 USAGE
289 }
290
291 do_version_sed() {
292         local OLD_VERSION="${PACKAGE_VERSION}"
293         local NEW_VERSION="$1"
294         local MSG="$2"
295
296         sed -i -e "/AC_INIT/ s|${OLD_VERSION}|${NEW_VERSION}|" configure.in
297         package_info_load
298         echo "${MSG}: ${OLD_VERSION} -> ${NEW_VERSION}"
299 }
300 do_version_bump_sed() {
301         local NEW_VERSION="$1"
302         [ -z "${PACKAGE_VERSION_TAGS}" ] || \
303                 NEW_VERSION="${NEW_VERSION}${PACKAGE_VERSION_TAGS}"
304
305         do_version_sed "${NEW_VERSION}" \
306                 "Bump ${CMD} package version number"
307 }
308 do_version_bump_major() {
309         do_version_bump_sed "$((PACKAGE_MAJOR + 1)).0.0"
310 }
311 do_version_bump_minor() {
312         do_version_bump_sed "${PACKAGE_MAJOR}.$((PACKAGE_MINOR + 1)).0"
313 }
314 do_version_bump_micro() {
315         do_version_bump_sed "${PACKAGE_MAJOR_AND_MINOR}.$((PACKAGE_MICRO + 1))"
316 }
317 do_version_bump_tag() {
318         local TAG="$1"
319         [ "${TAG}" ] || die "TAG argument is missing"
320         local TAGS="${PACKAGE_VERSION_TAGS}"
321         if has_version_tag "${TAG}"; then
322                 local RC=$(echo ${TAGS} | perl -ne "/-${TAG}"'(\d+)/ && print $1')
323                 RC=$((${RC} + 1))
324                 TAGS=$(echo ${TAGS} | perl -npe "s/-${TAG}[\\d]*/-${TAG}${RC}/")
325         else
326                 TAGS="-${TAG}1${PACKAGE_VERSION_TAGS}"
327         fi
328         PACKAGE_VERSION_TAGS="${TAGS}"
329         do_version_bump_sed "${PACKAGE_VERSION_BASE}"
330 }
331 do_version_bump_rc() { do_version_bump_tag 'rc'; }
332 do_version_bump() {
333         CMD="$1"
334         shift
335         case "${CMD}" in
336         major|minor|micro|rc|tag)
337                 eval "do_version_bump_${CMD}" "$@"
338                 ;;
339         *)
340                 do_version_usage
341                 ;;
342         esac
343 }
344
345 has_version_tag() {
346         test "${PACKAGE_VERSION/-${1}/}" != "${PACKAGE_VERSION}"
347 }
348
349 do_version_tag_add() {
350         local TAG="$1"
351         has_version_tag "${TAG}" && \
352                 die "error: tag '-${TAG}' exists in '${PACKAGE_VERSION}'"
353         do_version_sed "${PACKAGE_VERSION}-${TAG}" \
354                 "Add '-${TAG}' version tag"
355 }
356 do_version_tag_remove() {
357         local TAG="$1"
358         has_version_tag "${TAG}" || \
359                 die "error: tag '-${TAG}' missing from '${PACKAGE_VERSION}'"
360         do_version_sed "${PACKAGE_VERSION/-${TAG}/}" \
361                 "Remove '-${TAG}' version tag"
362 }
363 do_version_tag() {
364         CMD="$1"
365         shift
366         case "${CMD}" in
367         add|remove)
368                 local i=
369                 for i in "$@"; do 
370                         eval "do_version_tag_${CMD}" "${i}"
371                 done
372                 ;;
373         *)
374                 do_version_usage
375                 ;;
376         esac
377 }
378
379 do_version_commit() {
380         [ "$(svn diff configure.in | wc -l)" -gt 0 ] || \
381                 die "error: no version changes to commit"
382         do_svn commit -m "$1" configure.in
383 }
384
385 do_version() {
386         package_info_load
387         CMD="$1"
388         shift
389         case "${CMD}" in
390         tag|bump)
391                 do_version_commit "$(eval "do_version_${CMD}" "$@")"
392                 ;;
393         commit)
394                 local MSG="$1"
395                 [ "${MSG}" ] || die "usage: $0 version commit <message>"
396                 do_version_commit "${MSG}"
397                 ;;
398         *)
399                 do_version_usage
400                 ;;
401         esac
402 }
403
404
405 do_branch() {
406         package_info_load
407         svn_setup_load
408         do_svn copy -m "Branching version ${PACKAGE_VERSION}" \
409                 "${SVN_TRUNK}" "${PACKAGE_BRANCH}"
410 }
411 do_tag() {
412         package_info_load
413         svn_setup_load
414         do_svn copy -m "Tagging version ${PACKAGE_VERSION}" \
415                 "${PACKAGE_BRANCH}" "${PACKAGE_TAG}"
416 }
417 do_commit() {
418         package_info_load
419         svn_setup_load
420
421         [ "${PACKAGE_VERSION/in-development/}" = "${PACKAGE_VERSION}" ] || \
422                 die "'${PACKAGE_NAME}-${PACKAGE_VERSION}' cannot be released"
423
424         [ "${PACKAGE_VERSION%.0}" = "${PACKAGE_VERSION}" ] || \
425                 do_branch
426         do_tag
427 }
428
429
430 do_release_step_prep() {
431         do_version tag remove in-development
432         # reset RELEASE_VERSION now to allow release version to be detected
433         export RELEASE_VERSION=
434 }
435 do_release_step_commit() { do_commit; }
436
437 do_release_step_branch_bump() {
438         local TYPE="$1"
439         echo "Bump ${TYPE} version and add tag:"
440         do_version_bump ${TYPE}
441         do_version_tag_add in-development
442 }
443 do_release_step_branch() {
444         do_svn_switch "${PACKAGE_BRANCH}"
445         do_version_commit "$(do_release_step_branch_bump micro)"
446         do_svn_switch "${SVN_URL}"
447 }
448 do_release_step_news_msg() {
449         cat <<MSG
450 Archive released NEWS file: NEWS -> NEWS-${RELEASE_VERSION}
451 Create new NEWS file from relesse script template.
452 MSG
453 }
454 do_release_step_news() {
455         # archive NEWS and create new one from template
456         do_svn move "NEWS" "NEWS-${RELEASE_VERSION}"
457
458         [ "${RELEASE_DRY_RUN}" ] || cat >NEWS <<NEWS
459 This file should include items worth mentioning in the
460 OpenOCD ${PACKAGE_RELEASE} source archive release.
461
462 The following areas of OpenOCD functionality changed in this release:
463
464 JTAG Layer:
465 Target Layer:
466 Flash Layer:
467 Board, Target, and Interface Configuration Scripts:
468 Documentation:
469 Build and Release:
470
471 For more details about what has changed since the last release,
472 see the ChangeLog associated with this source archive.  For older NEWS,
473 see the NEWS files associated with each release (i.e. NEWS-<version>).
474
475 For more information about contributing test reports, bug fixes, or new
476 features and device support, please read the new Developer Manual (or
477 the BUGS and PATCHES files in the source archive).
478 NEWS
479         do_svn add NEWS
480
481         local MSG="$(do_release_step_news_msg)"
482         do_svn commit -m "${MSG}" NEWS NEWS-${RELEASE_VERSION}
483 }
484 do_release_step_bump() {
485         # major and minor releases require branch version update too
486         [ "${RELEASE_TYPE}" = "micro" ] || do_release_step_branch
487         # bump the current tree version as required.
488         do_version_commit "$(do_release_step_branch_bump "${RELEASE_TYPE}")"
489
490         [ "${RELEASE_TYPE}" = "micro" ] || do_release_step_news
491 }
492 do_release_step_package() {
493         local A=${PACKAGE_TAG}
494         local B=${A/https/http}
495         local PACKAGE_BUILD=${B/${USER}@/}
496
497         do_svn_switch "${PACKAGE_TAG}"
498         do_svn_switch --relocate "${PACKAGE_TAG}" "${PACKAGE_BUILD}"
499
500         # required to force SVN to update the in-source URL keyword
501         [ "${RELEASE_DRY_RUN}" ] || rm -v -f src/openocd.c
502         do_svn revert src/openocd.c
503
504         do_stage
505         do_clean
506
507         do_svn_switch --relocate "${PACKAGE_BUILD}" "${PACKAGE_TAG}"
508         do_svn_switch "${SVN_URL}"
509 }
510
511 do_release_step_1() { do_release_step_prep; }
512 do_release_step_2() { do_release_step_commit; }
513 do_release_step_3() { do_release_step_bump; }
514 do_release_step_4() { do_release_step_package; }
515
516 do_release_check() {
517         echo -n "Are you sure you want to release '${PACKAGE_RELEASE}'?"
518         read ANSWER
519         if [ "${ANSWER}" != 'y' ]; then
520                 echo "Live release aborted!"
521                 exit 0
522         fi
523 }
524 do_countdown() {
525         echo -n "$1 in "
526         for i in $(seq 5 -1 1); do
527                 echo -n "$i, "
528         done
529         echo "go!"
530 }
531
532 do_release() {
533         package_info_load
534         package_info_show
535
536         if [ -z "${RELEASE_DRY_RUN}" ]; then
537                 do_release_check
538                 do_countdown "Starting live release"
539         fi
540
541         local i=
542         for i in $(seq 1 4); do
543                 eval "do_release_step_${i}"
544         done
545 }
546 do_all() { do_release "$@"; }
547
548 do_reset() {
549         maybe_bootstrap
550         maybe_configure
551         do_clean_all
552         svn revert configure.in
553 }
554
555 OPTIONS=$(getopt -o V --long live -n $0 -- "$@")
556 if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
557 eval set -- "${OPTIONS}"
558 while true; do
559         case "$1" in
560         --live)
561                 export RELEASE_DRY_RUN=
562                 shift
563                 ;;
564         -V)
565                 exec $0 info
566                 ;;
567         --)
568                 shift
569                 break
570                 ;;
571         *)
572                 echo "Internal error"
573                 exit 1
574                 ;;
575         esac
576 done
577
578 CMD=$1
579 [ "${CMD}" ] || usage
580 shift
581
582 ACTION_CMDS="bootstrap|configure|build|changelog|package|stage|clean"
583 MISC_CMDS="all|info|version|tag|branch|commit|release|reset|help|usage"
584 CLEAN_CMDS="build_clean|changelog_clean|package_clean|stage_clean|clean_all"
585 CMDS="|${ACTION_CMDS}|${CLEAN_CMDS}|${MISC_CMDS}|"
586 is_command() { echo "${CMDS}" | grep "|$1|" >/dev/null; }
587
588 if is_command "${CMD}"; then
589         eval "do_${CMD}" "$@"
590 else
591         echo "error: unknown command: '${CMD}'"
592         usage
593 fi