]> git.sur5r.net Git - openocd/blob - tools/release.sh
c6ab64fe7c824ba564628be2b8d2c5f5eaca5796
[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 maybe_rebuild() {
216         if [ -f "configure" ]; then
217                 echo "Re-running autoconf..."
218                 autoconf
219                 echo "Re-running automake..."
220                 automake
221         fi
222         if [ -f "Makefile" ]; then
223                 do_configure
224                 do_build
225         fi
226 }
227
228 do_changelog() {
229         echo "Updating working copy to HEAD..."
230         do_svn update
231         echo "Creating ChangeLog..."
232         svn2cl -i --authors AUTHORS.ChangeLog
233 }
234 maybe_changelog() {
235         if [ -z "${RELEASE_DRY_RUN}" ] \
236                 || [ ! -f ChangeLog ] \
237                 || [ "$(cat ChangeLog | wc -l)" -lt 2 ]
238         then
239                 do_changelog
240         fi
241 }
242 do_changelog_clean() {
243         do_svn revert ChangeLog
244 }
245
246 do_package() {
247         package_info_load
248         maybe_changelog
249         maybe_build
250         echo "Building distribution packages..."
251         make ${MAKE_OPTS} distcheck 2>&1 | perl tools/logger.pl > "release-pkg.log"
252 }
253 maybe_package() { [ -f "${PACKAGE_RELEASE}.zip" ] || do_package; }
254 do_package_clean() {
255         for EXT in tar.gz tar.bz2 zip; do
256                 rm -v -f *.${EXT}
257         done
258 }
259
260 do_stage() {
261         maybe_package
262         echo "Staging package archives:"
263         mkdir -p archives
264         for EXT in tar.gz tar.bz2 zip; do
265                 local FILE="${PACKAGE_RELEASE}.${EXT}"
266                 # create archive signatures
267                 for HASH in md5 sha1; do
268                         echo "sign: ${FILE}.${HASH}"
269                         ${HASH}sum "${FILE}" > "archives/${FILE}.${HASH}"
270                 done
271                 # save archive
272                 mv -v "${FILE}" archives/
273         done
274         cp -a NEWS archives/
275         cp -a ChangeLog archives/
276 }
277 do_stage_clean() { rm -v -f -r archives; }
278
279 do_clean() {
280         do_build_clean
281         do_package_clean
282         rm -v -f configure
283
284         svn revert configure.in
285         rm -v -f release-*.log
286 }
287 do_clean_all() {
288         do_clean
289         do_changelog_clean
290         do_stage_clean
291 }
292
293 do_version_usage() {
294         cat << USAGE
295 usage: $0 version <command>
296 Version Commands:
297   tag {add|remove} <label>     Add or remove the specified tag.
298   bump {major|minor|micro|rc}  Bump the specified version number;
299                                resets less-significant numbers to zero.
300                                All but 'rc' releases drop that tag.
301 USAGE
302 }
303
304 do_version_sed() {
305         local OLD_VERSION="${PACKAGE_VERSION}"
306         local NEW_VERSION="$1"
307         local MSG="$2"
308
309         sed -i -e "/AC_INIT/ s|${OLD_VERSION}|${NEW_VERSION}|" configure.in
310         package_info_load
311         echo "${MSG}: ${OLD_VERSION} -> ${NEW_VERSION}"
312 }
313 do_version_bump_sed() {
314         local NEW_VERSION="$1"
315         [ -z "${PACKAGE_VERSION_TAGS}" ] || \
316                 NEW_VERSION="${NEW_VERSION}${PACKAGE_VERSION_TAGS}"
317
318         do_version_sed "${NEW_VERSION}" \
319                 "Bump ${CMD} package version number"
320 }
321 do_version_bump_major() {
322         has_version_tag 'rc\d' do_version_
323         do_version_bump_sed "$((PACKAGE_MAJOR + 1)).0.0"
324 }
325 do_version_bump_minor() {
326         do_version_bump_sed "${PACKAGE_MAJOR}.$((PACKAGE_MINOR + 1)).0"
327 }
328 do_version_bump_micro() {
329         do_version_bump_sed "${PACKAGE_MAJOR_AND_MINOR}.$((PACKAGE_MICRO + 1))"
330 }
331 do_version_bump_rc() {
332         die "patch missing: -rc support is not implemented"
333 }
334 do_version_bump() {
335         CMD="$1"
336         shift
337         case "${CMD}" in
338         major|minor|micro|rc)
339                 eval "do_version_bump_${CMD}"
340                 ;;
341         *)
342                 do_version_usage
343                 ;;
344         esac
345 }
346
347 has_version_tag() {
348         test "${PACKAGE_VERSION/-${TAG}/}" != "${PACKAGE_VERSION}"
349 }
350
351 do_version_tag_add() {
352         local TAG="$1"
353         has_version_tag && die "error: tag '-${TAG}' exists in '${PACKAGE_VERSION}'"
354         do_version_sed "${PACKAGE_VERSION}-${TAG}" \
355                 "Add '-${TAG}' version tag"
356 }
357 do_version_tag_remove() {
358         local TAG="$1"
359         has_version_tag || 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                 maybe_rebuild
393                 ;;
394         commit)
395                 local MSG="$1"
396                 [ "${MSG}" ] || die "usage: $0 version commit <message>"
397                 do_version_commit "${MSG}"
398                 maybe_rebuild
399                 ;;
400         *)
401                 do_version_usage
402                 ;;
403         esac
404 }
405
406
407 do_branch() {
408         package_info_load
409         svn_setup_load
410         do_svn copy -m "Branching version ${PACKAGE_VERSION}" \
411                 "${SVN_TRUNK}" "${PACKAGE_BRANCH}"
412 }
413 do_tag() {
414         package_info_load
415         svn_setup_load
416         do_svn copy -m "Tagging version ${PACKAGE_VERSION}" \
417                 "${PACKAGE_BRANCH}" "${PACKAGE_TAG}"
418 }
419 do_commit() {
420         package_info_load
421         svn_setup_load
422
423         [ "${PACKAGE_VERSION/in-development/}" = "${PACKAGE_VERSION}" ] || \
424                 die "'${PACKAGE_NAME}-${PACKAGE_VERSION}' cannot be released"
425
426         [ "${PACKAGE_VERSION%.0}" = "${PACKAGE_VERSION}" ] || \
427                 do_branch
428         do_tag
429 }
430
431
432 do_release_step_prep() {
433         do_version tag remove in-development
434         # reset RELEASE_VERSION now to allow release version to be detected
435         export RELEASE_VERSION=
436 }
437 do_release_step_commit() { do_commit; }
438
439 do_release_step_branch_bump() {
440         local TYPE="$1"
441         echo "Bump ${TYPE} version and add tag:"
442         do_version_bump ${TYPE}
443         do_version_tag_add in-development
444 }
445 do_release_step_branch() {
446         do_svn_switch "${PACKAGE_BRANCH}"
447         do_version_commit "$(do_release_step_branch_bump micro)"
448         do_svn_switch "${SVN_URL}"
449 }
450 do_release_step_news_msg() {
451         cat <<MSG
452 Archive released NEWS file: NEWS -> NEWS-${RELEASE_VERSION}
453 Create new NEWS file from relesse script template.
454 MSG
455 }
456 do_release_step_news() {
457         # archive NEWS and create new one from template
458         do_svn move "NEWS" "NEWS-${RELEASE_VERSION}"
459
460         [ "${RELEASE_DRY_RUN}" ] || cat >NEWS <<NEWS
461 This file should include items worth mentioning in the
462 OpenOCD ${PACKAGE_RELEASE} source archive release.
463
464 The following areas of OpenOCD functionality changed in this release:
465
466 JTAG Layer:
467 Target Layer:
468 Flash Layer:
469 Board, Target, and Interface Configuration Scripts:
470 Documentation:
471 Build and Release:
472
473 For more details about what has changed since the last release,
474 see the ChangeLog associated with this source archive.  For older NEWS,
475 see the NEWS files associated with each release (i.e. NEWS-<version>).
476
477 For more information about contributing test reports, bug fixes, or new
478 features and device support, please read the new Developer Manual (or
479 the BUGS and PATCHES files in the source archive).
480 NEWS
481         do_svn add NEWS
482
483         local MSG="$(do_release_step_news_msg)"
484         do_svn commit -m "${MSG}" NEWS NEWS-${RELEASE_VERSION}
485 }
486 do_release_step_bump() {
487         # major and minor releases require branch version update too
488         [ "${RELEASE_TYPE}" = "micro" ] || do_release_step_branch
489         # bump the current tree version as required.
490         do_version_commit "$(do_release_step_branch_bump "${RELEASE_TYPE}")"
491
492         [ "${RELEASE_TYPE}" = "micro" ] || do_release_step_news
493 }
494 do_release_step_package() {
495         local A=${PACKAGE_TAG}
496         local B=${A/https/http}
497         local PACKAGE_BUILD=${B/${USER}@/}
498
499         do_svn_switch "${PACKAGE_TAG}"
500         do_svn_switch --relocate "${PACKAGE_TAG}" "${PACKAGE_BUILD}"
501
502         # required to force SVN to update the in-source URL keyword
503         [ "${RELEASE_DRY_RUN}" ] || rm -v -f src/openocd.c
504         do_svn revert src/openocd.c
505
506         do_stage
507         do_clean
508
509         do_svn_switch --relocate "${PACKAGE_BUILD}" "${PACKAGE_TAG}"
510         do_svn_switch "${SVN_URL}"
511 }
512
513 do_release_step_1() { do_release_step_prep; }
514 do_release_step_2() { do_release_step_commit; }
515 do_release_step_3() { do_release_step_bump; }
516 do_release_step_4() { do_release_step_package; }
517
518 do_release_check() {
519         echo -n "Are you sure you want to release '${PACKAGE_RELEASE}'?"
520         read ANSWER
521         if [ "${ANSWER}" != 'y' ]; then
522                 echo "Live release aborted!"
523                 exit 0
524         fi
525 }
526 do_countdown() {
527         echo -n "$1 in "
528         for i in $(seq 5 -1 1); do
529                 echo -n "$i, "
530         done
531         echo "go!"
532 }
533
534 do_release() {
535         package_info_load
536         package_info_show
537
538         if [ -z "${RELEASE_DRY_RUN}" ]; then
539                 do_release_check
540                 do_countdown "Starting live release"
541         fi
542
543         local i=
544         for i in $(seq 1 4); do
545                 eval "do_release_step_${i}"
546         done
547 }
548 do_all() { do_release "$@"; }
549
550 do_reset() {
551         maybe_bootstrap
552         maybe_configure
553         do_clean_all
554         svn revert configure.in
555 }
556
557 OPTIONS=$(getopt -o V --long live -n $0 -- "$@")
558 if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
559 eval set -- "${OPTIONS}"
560 while true; do
561         case "$1" in
562         --live)
563                 export RELEASE_DRY_RUN=
564                 shift
565                 ;;
566         -V)
567                 exec $0 info
568                 ;;
569         --)
570                 shift
571                 break
572                 ;;
573         *)
574                 echo "Internal error"
575                 exit 1
576                 ;;
577         esac
578 done
579
580 CMD=$1
581 [ "${CMD}" ] || usage
582 shift
583
584 ACTION_CMDS="bootstrap|configure|build|changelog|package|stage|clean"
585 MISC_CMDS="all|info|version|tag|branch|commit|release|reset|help|usage"
586 CLEAN_CMDS="build_clean|changelog_clean|package_clean|stage_clean|clean_all"
587 CMDS="|${ACTION_CMDS}|${CLEAN_CMDS}|${MISC_CMDS}|"
588 is_command() { echo "${CMDS}" | grep "|$1|" >/dev/null; }
589
590 if is_command "${CMD}"; then
591         eval "do_${CMD}" "$@"
592 else
593         echo "error: unknown command: '${CMD}'"
594         usage
595 fi