]> git.sur5r.net Git - openocd/blob - tools/release.sh
Add git2cl from repo.or.cz as a submodule in tools/git2cl.
[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/-dev/}
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}     Bump the specified version number, and
286                                reset less-significant numbers to zero.
287   bump tag <label>             Add or bump a versioned tag (e.g. -rcN).
288   bump final <label>           Remove a versioned tag (e.g. -rcN).
289 USAGE
290 }
291
292 do_version_sed() {
293         local OLD_VERSION="${PACKAGE_VERSION}"
294         local NEW_VERSION="$1"
295         local MSG="$2"
296
297         sed -i -e "/AC_INIT/ s|${OLD_VERSION}|${NEW_VERSION}|" configure.in
298         package_info_load
299         echo "${MSG}: ${OLD_VERSION} -> ${NEW_VERSION}"
300 }
301 do_version_bump_sed() {
302         local NEW_VERSION="$1"
303         [ -z "${PACKAGE_VERSION_TAGS}" ] || \
304                 NEW_VERSION="${NEW_VERSION}${PACKAGE_VERSION_TAGS}"
305
306         do_version_sed "${NEW_VERSION}" \
307                 "Bump ${CMD} package version number"
308 }
309 do_version_bump_major() {
310         do_version_bump_sed "$((PACKAGE_MAJOR + 1)).0.0"
311 }
312 do_version_bump_minor() {
313         do_version_bump_sed "${PACKAGE_MAJOR}.$((PACKAGE_MINOR + 1)).0"
314 }
315 do_version_bump_micro() {
316         do_version_bump_sed "${PACKAGE_MAJOR_AND_MINOR}.$((PACKAGE_MICRO + 1))"
317 }
318 do_version_bump_tag() {
319         local TAG="$1"
320         [ "${TAG}" ] || die "TAG argument is missing"
321         local TAGS="${PACKAGE_VERSION_TAGS}"
322         if has_version_tag "${TAG}"; then
323                 local RC=$(do_version_tag_value "${TAG}")
324                 RC=$((${RC} + 1))
325                 TAGS=$(echo ${TAGS} | perl -npe "s/-${TAG}[\\d]*/-${TAG}${RC}/")
326         else
327                 TAGS="-${TAG}1${PACKAGE_VERSION_TAGS}"
328         fi
329         PACKAGE_VERSION_TAGS="${TAGS}"
330         do_version_bump_sed "${PACKAGE_VERSION_BASE}"
331 }
332 do_version_bump_final() {
333         local TAG="$1"
334         [ "${TAG}" ] || die "TAG argument is missing"
335         has_version_tag "${TAG}" || die "-${TAG} tag is missing"
336         do_version_tag_remove "${TAG}$(do_version_tag_value "${TAG}")"
337 }
338 do_version_bump() {
339         CMD="$1"
340         shift
341         case "${CMD}" in
342         major|minor|micro|final|tag)
343                 eval "do_version_bump_${CMD}" "$@"
344                 ;;
345         *)
346                 do_version_usage
347                 ;;
348         esac
349 }
350
351 has_version_tag() {
352         test "${PACKAGE_VERSION/-${1}/}" != "${PACKAGE_VERSION}"
353 }
354 do_version_tag_value() {
355         local TAG="$1"
356         echo ${PACKAGE_VERSION_TAGS} | perl -ne "/-${TAG}"'(\d+)/ && print $1'
357 }
358 do_version_tag_add() {
359         local TAG="$1"
360         has_version_tag "${TAG}" && \
361                 die "error: tag '-${TAG}' exists in '${PACKAGE_VERSION}'"
362         do_version_sed "${PACKAGE_VERSION}-${TAG}" \
363                 "Add '-${TAG}' version tag"
364 }
365 do_version_tag_remove() {
366         local TAG="$1"
367         has_version_tag "${TAG}" || \
368                 die "error: tag '-${TAG}' missing from '${PACKAGE_VERSION}'"
369         do_version_sed "${PACKAGE_VERSION/-${TAG}/}" \
370                 "Remove '-${TAG}' version tag"
371 }
372 do_version_tag() {
373         CMD="$1"
374         shift
375         case "${CMD}" in
376         add|remove)
377                 local i=
378                 for i in "$@"; do 
379                         eval "do_version_tag_${CMD}" "${i}"
380                 done
381                 ;;
382         *)
383                 do_version_usage
384                 ;;
385         esac
386 }
387
388 do_version_commit() {
389         [ "$(svn diff configure.in | wc -l)" -gt 0 ] || \
390                 die "error: no version changes to commit"
391         do_svn commit -m "$1" configure.in
392 }
393
394 do_version() {
395         package_info_load
396         CMD="$1"
397         shift
398         case "${CMD}" in
399         tag|bump)
400                 do_version_commit "$(eval "do_version_${CMD}" "$@")"
401                 ;;
402         commit)
403                 local MSG="$1"
404                 [ "${MSG}" ] || die "usage: $0 version commit <message>"
405                 do_version_commit "${MSG}"
406                 ;;
407         *)
408                 do_version_usage
409                 ;;
410         esac
411 }
412
413
414 do_branch() {
415         package_info_load
416         svn_setup_load
417         do_svn copy -m "Branching version ${PACKAGE_VERSION}" \
418                 "${SVN_TRUNK}" "${PACKAGE_BRANCH}"
419 }
420 do_tag() {
421         package_info_load
422         svn_setup_load
423         do_svn copy -m "Tagging version ${PACKAGE_VERSION}" \
424                 "${PACKAGE_BRANCH}" "${PACKAGE_TAG}"
425 }
426 do_commit() {
427         package_info_load
428         svn_setup_load
429
430         [ "${PACKAGE_VERSION/dev/}" = "${PACKAGE_VERSION}" ] || \
431                 die "'${PACKAGE_NAME}-${PACKAGE_VERSION}' cannot be released"
432
433         [ "${PACKAGE_VERSION%.0}" = "${PACKAGE_VERSION}" ] || \
434                 do_branch
435         do_tag
436 }
437
438
439 do_release_step_prep() {
440         do_version tag remove dev
441         # reset RELEASE_VERSION now to allow release version to be detected
442         export RELEASE_VERSION=
443 }
444 do_release_step_commit() { do_commit; }
445
446 do_release_step_branch_bump() {
447         local TYPE="$1"
448         echo "Bump ${TYPE} version and add tag:"
449         do_version_bump ${TYPE}
450         do_version_tag_add dev
451 }
452 do_release_step_branch() {
453         do_svn_switch "${PACKAGE_BRANCH}"
454         do_version_commit "$(do_release_step_branch_bump micro)"
455         do_svn_switch "${SVN_URL}"
456 }
457 do_release_step_news_msg() {
458         cat <<MSG
459 Archive released NEWS file: NEWS -> NEWS-${RELEASE_VERSION}
460 Create new NEWS file from relesse script template.
461 MSG
462 }
463 do_release_step_news() {
464         # archive NEWS and create new one from template
465         do_svn move "NEWS" "NEWS-${RELEASE_VERSION}"
466
467         [ "${RELEASE_DRY_RUN}" ] || cat >NEWS <<NEWS
468 This file should include items worth mentioning in the
469 OpenOCD ${PACKAGE_RELEASE} source archive release.
470
471 The following areas of OpenOCD functionality changed in this release:
472
473 JTAG Layer:
474 Target Layer:
475 Flash Layer:
476 Board, Target, and Interface Configuration Scripts:
477 Documentation:
478 Build and Release:
479
480 For more details about what has changed since the last release,
481 see the ChangeLog associated with this source archive.  For older NEWS,
482 see the NEWS files associated with each release (i.e. NEWS-<version>).
483
484 For more information about contributing test reports, bug fixes, or new
485 features and device support, please read the new Developer Manual (or
486 the BUGS and PATCHES files in the source archive).
487 NEWS
488         do_svn add NEWS
489
490         local MSG="$(do_release_step_news_msg)"
491         do_svn commit -m "${MSG}" NEWS NEWS-${RELEASE_VERSION}
492 }
493 do_release_step_bump() {
494         # major and minor releases require branch version update too
495         [ "${RELEASE_TYPE}" = "micro" ] || do_release_step_branch
496         # bump the current tree version as required.
497         do_version_commit "$(do_release_step_branch_bump "${RELEASE_TYPE}")"
498
499         [ "${RELEASE_TYPE}" = "micro" ] || do_release_step_news
500 }
501 do_release_step_package() {
502         local A=${PACKAGE_TAG}
503         local B=${A/https/http}
504         local PACKAGE_BUILD=${B/${USER}@/}
505
506         do_svn_switch "${PACKAGE_TAG}"
507         do_svn_switch --relocate "${PACKAGE_TAG}" "${PACKAGE_BUILD}"
508
509         # required to force SVN to update the in-source URL keyword
510         [ "${RELEASE_DRY_RUN}" ] || rm -v -f src/openocd.c
511         do_svn revert src/openocd.c
512
513         do_stage
514         do_clean
515
516         do_svn_switch --relocate "${PACKAGE_BUILD}" "${PACKAGE_TAG}"
517         do_svn_switch "${SVN_URL}"
518 }
519
520 do_release_step_1() { do_release_step_prep; }
521 do_release_step_2() { do_release_step_commit; }
522 do_release_step_3() { do_release_step_bump; }
523 do_release_step_4() { do_release_step_package; }
524
525 do_release_check() {
526         echo -n "Are you sure you want to release '${PACKAGE_RELEASE}'?"
527         read ANSWER
528         if [ "${ANSWER}" != 'y' ]; then
529                 echo "Live release aborted!"
530                 exit 0
531         fi
532 }
533 do_countdown() {
534         echo -n "$1 in "
535         for i in $(seq 5 -1 1); do
536                 echo -n "$i, "
537         done
538         echo "go!"
539 }
540
541 do_release() {
542         package_info_load
543         package_info_show
544
545         if [ -z "${RELEASE_DRY_RUN}" ]; then
546                 do_release_check
547                 do_countdown "Starting live release"
548         fi
549
550         local i=
551         for i in $(seq 1 4); do
552                 eval "do_release_step_${i}"
553         done
554 }
555 do_all() { do_release "$@"; }
556
557 do_reset() {
558         maybe_bootstrap
559         maybe_configure
560         do_clean_all
561         svn revert configure.in
562 }
563
564 OPTIONS=$(getopt -o V --long live -n $0 -- "$@")
565 if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
566 eval set -- "${OPTIONS}"
567 while true; do
568         case "$1" in
569         --live)
570                 export RELEASE_DRY_RUN=
571                 shift
572                 ;;
573         -V)
574                 exec $0 info
575                 ;;
576         --)
577                 shift
578                 break
579                 ;;
580         *)
581                 echo "Internal error"
582                 exit 1
583                 ;;
584         esac
585 done
586
587 CMD=$1
588 [ "${CMD}" ] || usage
589 shift
590
591 ACTION_CMDS="bootstrap|configure|build|changelog|package|stage|clean"
592 MISC_CMDS="all|info|version|tag|branch|commit|release|reset|help|usage"
593 CLEAN_CMDS="build_clean|changelog_clean|package_clean|stage_clean|clean_all"
594 CMDS="|${ACTION_CMDS}|${CLEAN_CMDS}|${MISC_CMDS}|"
595 is_command() { echo "${CMDS}" | grep "|$1|" >/dev/null; }
596
597 if is_command "${CMD}"; then
598         eval "do_${CMD}" "$@"
599 else
600         echo "error: unknown command: '${CMD}'"
601         usage
602 fi