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