release.sh: move fetching the section up so we can do stuff with it
[xorg-util-modular.git] / release.sh
blob6deba0a4c65129fe2cbbd995d86ad12cdaa8bea2
1 #!/bin/sh
3 # Creates and upload a git module tarball
5 # Note on portability:
6 # This script is intended to run on any platform supported by X.Org.
7 # Basically, it should be able to run in a Bourne shell.
11 export LC_ALL=C
13 #------------------------------------------------------------------------------
14 # Function: check_local_changes
15 #------------------------------------------------------------------------------
17 check_local_changes() {
18 git diff --quiet HEAD > /dev/null 2>&1
19 if [ $? -ne 0 ]; then
20 echo ""
21 echo "Uncommitted changes found. Did you forget to commit? Aborting."
22 echo ""
23 echo "You can perform a 'git stash' to save your local changes and"
24 echo "a 'git stash apply' to recover them after the tarball release."
25 echo "Make sure to rebuild and run 'make distcheck' again."
26 echo ""
27 echo "Alternatively, you can clone the module in another directory"
28 echo "and run ./configure. No need to build if testing was finished."
29 echo ""
30 return 1
32 return 0
35 #------------------------------------------------------------------------------
36 # Function: check_option_args
37 #------------------------------------------------------------------------------
39 # perform sanity checks on cmdline args which require arguments
40 # arguments:
41 # $1 - the option being examined
42 # $2 - the argument to the option
43 # returns:
44 # if it returns, everything is good
45 # otherwise it exit's
46 check_option_args() {
47 option=$1
48 arg=$2
50 # check for an argument
51 if [ x"$arg" = x ]; then
52 echo ""
53 echo "Error: the '$option' option is missing its required argument."
54 echo ""
55 usage
56 exit 1
59 # does the argument look like an option?
60 echo $arg | $GREP "^-" > /dev/null
61 if [ $? -eq 0 ]; then
62 echo ""
63 echo "Error: the argument '$arg' of option '$option' looks like an option itself."
64 echo ""
65 usage
66 exit 1
70 #------------------------------------------------------------------------------
71 # Function: check_modules_specification
72 #------------------------------------------------------------------------------
74 check_modules_specification() {
76 if [ x"$MODFILE" = x ]; then
77 if [ x"${INPUT_MODULES}" = x ]; then
78 echo ""
79 echo "Error: no modules specified (blank command line)."
80 usage
81 exit 1
87 #------------------------------------------------------------------------------
88 # Function: generate_announce
89 #------------------------------------------------------------------------------
91 generate_announce()
93 cat <<RELEASE
94 Subject: [ANNOUNCE] $pkg_name $pkg_version
95 To: $list_to
96 Cc: $list_cc
98 `git log --no-merges "$tag_range" | git shortlog`
100 git tag: $tar_name
102 RELEASE
104 for tarball in $tarbz2 $targz $tarxz; do
105 cat <<RELEASE
106 http://$host_current/$section_path/$tarball
107 MD5: `$MD5SUM $tarball`
108 SHA1: `$SHA1SUM $tarball`
109 SHA256: `$SHA256SUM $tarball`
111 RELEASE
112 done
115 #------------------------------------------------------------------------------
116 # Function: read_modfile
117 #------------------------------------------------------------------------------
119 # Read the module names from the file and set a variable to hold them
120 # This will be the same interface as cmd line supplied modules
122 read_modfile() {
124 if [ x"$MODFILE" != x ]; then
125 # Make sure the file is sane
126 if [ ! -r "$MODFILE" ]; then
127 echo "Error: module file '$MODFILE' is not readable or does not exist."
128 exit 1
130 # read from input file, skipping blank and comment lines
131 while read line; do
132 # skip blank lines
133 if [ x"$line" = x ]; then
134 continue
136 # skip comment lines
137 if echo "$line" | $GREP -q "^#" ; then
138 continue;
140 INPUT_MODULES="$INPUT_MODULES $line"
141 done <"$MODFILE"
143 return 0
146 #------------------------------------------------------------------------------
147 # Function: print_epilog
148 #------------------------------------------------------------------------------
150 print_epilog() {
152 epilog="======== Successful Completion"
153 if [ x"$NO_QUIT" != x ]; then
154 if [ x"$failed_modules" != x ]; then
155 epilog="======== Partial Completion"
157 elif [ x"$failed_modules" != x ]; then
158 epilog="======== Stopped on Error"
161 echo ""
162 echo "$epilog `date`"
164 # Report about modules that failed for one reason or another
165 if [ x"$failed_modules" != x ]; then
166 echo " List of failed modules:"
167 for mod in $failed_modules; do
168 echo " $mod"
169 done
170 echo "========"
171 echo ""
175 #------------------------------------------------------------------------------
176 # Function: process_modules
177 #------------------------------------------------------------------------------
179 # Loop through each module to release
180 # Exit on error if --no-quit was not specified
182 process_modules() {
183 for MODULE_RPATH in ${INPUT_MODULES}; do
184 if ! process_module ; then
185 echo "Error: processing module \"$MODULE_RPATH\" failed."
186 failed_modules="$failed_modules $MODULE_RPATH"
187 if [ x"$NO_QUIT" = x ]; then
188 print_epilog
189 exit 1
192 done
195 #------------------------------------------------------------------------------
196 # Function: get_section
197 #------------------------------------------------------------------------------
198 # Code 'return 0' on success
199 # Code 'return 1' on error
200 # Sets global variable $section
201 get_section() {
202 local module_url
203 local full_module_url
205 # Obtain the git url in order to find the section to which this module belongs
206 full_module_url=`git config --get remote.$remote_name.url | sed 's:\.git$::'`
207 if [ $? -ne 0 ]; then
208 echo "Error: unable to obtain git url for remote \"$remote_name\"."
209 return 1
212 # The last part of the git url will tell us the section. Look for xorg first
213 echo "$full_module_url"
214 module_url=`echo "$full_module_url" | $GREP -o "/xorg/.*"`
215 if [ $? -eq 0 ]; then
216 module_url=`echo $module_url | cut -d'/' -f3,4`
217 else
218 # The look for mesa, xcb, etc...
219 module_url=`echo "$full_module_url" | $GREP -o -e "/mesa/.*" -e "/xcb/.*" -e "/xkeyboard-config" -e "/nouveau/xf86-video-nouveau" -e "/libevdev"`
220 if [ $? -eq 0 ]; then
221 module_url=`echo $module_url | cut -d'/' -f2,3`
222 else
223 echo "Error: unable to locate a valid project url from \"$full_module_url\"."
224 echo "Cannot establish url as one of xorg, mesa, xcb, xf86-video-nouveau or xkeyboard-config."
225 return 1
229 # Find the section (subdirs) where the tarballs are to be uploaded
230 # The module relative path can be app/xfs, xserver, or mesa/drm for example
231 section=`echo $module_url | cut -d'/' -f1`
232 if [ $? -ne 0 ]; then
233 echo "Error: unable to extract section from $module_url first field."
234 return 1
237 if [ x"$section" = xmesa ]; then
238 section=`echo $module_url | cut -d'/' -f2`
239 if [ $? -ne 0 ]; then
240 echo "Error: unable to extract section from $module_url second field."
241 return 1
242 elif [ x"$section" != xdrm ]; then
243 echo "Error: section $section is not supported, only libdrm is."
244 return 1
248 return 0
252 #------------------------------------------------------------------------------
253 # Function: process_module
254 #------------------------------------------------------------------------------
255 # Code 'return 0' on success to process the next module
256 # Code 'return 1' on error to process next module if invoked with --no-quit
258 process_module() {
260 top_src=`pwd`
261 echo ""
262 echo "======== Processing \"$top_src/$MODULE_RPATH\""
264 # This is the location where the script has been invoked
265 if [ ! -d $MODULE_RPATH ] ; then
266 echo "Error: $MODULE_RPATH cannot be found under $top_src."
267 return 1
270 # Change directory to be in the git module
271 cd $MODULE_RPATH
272 if [ $? -ne 0 ]; then
273 echo "Error: failed to cd to $MODULE_RPATH."
274 return 1
277 # ----- Now in the git module *root* directory ----- #
279 # Check that this is indeed a git module
280 if [ ! -d .git ]; then
281 echo "Error: there is no git module here: `pwd`"
282 return 1
285 # Change directory to be in the git build directory (could be out-of-source)
286 # More than one can be found when distcheck has run and failed
287 configNum=`find . -name config.status -type f | wc -l | sed 's:^ *::'`
288 if [ $? -ne 0 ]; then
289 echo "Error: failed to locate config.status."
290 echo "Has the module been configured?"
291 return 1
293 if [ x"$configNum" = x0 ]; then
294 echo "Error: failed to locate config.status, has the module been configured?"
295 return 1
297 if [ x"$configNum" != x1 ]; then
298 echo "Error: more than one config.status file was found,"
299 echo " clean-up previously failed attempts at distcheck"
300 return 1
302 status_file=`find . -name config.status -type f`
303 if [ $? -ne 0 ]; then
304 echo "Error: failed to locate config.status."
305 echo "Has the module been configured?"
306 return 1
308 build_dir=`dirname $status_file`
309 cd $build_dir
310 if [ $? -ne 0 ]; then
311 echo "Error: failed to cd to $MODULE_RPATH/$build_dir."
312 cd $top_src
313 return 1
316 # ----- Now in the git module *build* directory ----- #
318 # Check for uncommitted/queued changes.
319 check_local_changes
320 if [ $? -ne 0 ]; then
321 cd $top_src
322 return 1
325 # Determine what is the current branch and the remote name
326 current_branch=`git branch | $GREP "\*" | sed -e "s/\* //"`
327 remote_name=`git config --get branch.$current_branch.remote`
328 remote_branch=`git config --get branch.$current_branch.merge | cut -d'/' -f3,4`
329 echo "Info: working off the \"$current_branch\" branch tracking the remote \"$remote_name/$remote_branch\"."
331 # Obtain the section
332 get_section
333 if [ $? -ne 0 ]; then
334 cd $top_src
335 return 1
338 # Run 'make dist/distcheck' to ensure the tarball matches the git module content
339 # Important to run make dist/distcheck before looking in Makefile, may need to reconfigure
340 echo "Info: running \"make $MAKE_DIST_CMD\" to create tarballs:"
341 ${MAKE} $MAKEFLAGS $MAKE_DIST_CMD > /dev/null
342 if [ $? -ne 0 ]; then
343 echo "Error: \"$MAKE $MAKEFLAGS $MAKE_DIST_CMD\" failed."
344 cd $top_src
345 return 1
348 # Find out the tarname from the makefile
349 pkg_name=`$GREP '^PACKAGE = ' Makefile | sed 's|PACKAGE = ||'`
350 pkg_version=`$GREP '^VERSION = ' Makefile | sed 's|VERSION = ||'`
351 tar_name="$pkg_name-$pkg_version"
352 tag_name="$tar_name"
353 targz=$tar_name.tar.gz
354 tarbz2=$tar_name.tar.bz2
355 tarxz=$tar_name.tar.xz
357 [ -e $targz ] && ls -l $targz || unset targz
358 [ -e $tarbz2 ] && ls -l $tarbz2 || unset tarbz2
359 [ -e $tarxz ] && ls -l $tarxz || unset tarxz
361 if [ -z "$targz" -a -z "$tarbz2" -a -z "$tarxz" ]; then
362 echo "Error: no compatible tarballs found."
363 cd $top_src
364 return 1
367 # Obtain the top commit SHA which should be the version bump
368 # It should not have been tagged yet (the script will do it later)
369 local_top_commit_sha=`git rev-list --max-count=1 HEAD`
370 if [ $? -ne 0 ]; then
371 echo "Error: unable to obtain the local top commit id."
372 cd $top_src
373 return 1
376 # Check that the top commit looks like a version bump
377 git diff --unified=0 HEAD^ | $GREP -F $pkg_version >/dev/null 2>&1
378 if [ $? -ne 0 ]; then
379 echo "Error: the local top commit does not look like a version bump."
380 echo " the diff does not contain the string \"$pkg_version\"."
381 local_top_commit_descr=`git log --oneline --max-count=1 $local_top_commit_sha`
382 echo " the local top commit is: \"$local_top_commit_descr\""
383 cd $top_src
384 return 1
387 # Check that the top commit has been pushed to remote
388 remote_top_commit_sha=`git rev-list --max-count=1 $remote_name/$remote_branch`
389 if [ $? -ne 0 ]; then
390 echo "Error: unable to obtain top commit from the remote repository."
391 cd $top_src
392 return 1
394 if [ x"$remote_top_commit_sha" != x"$local_top_commit_sha" ]; then
395 echo "Error: the local top commit has not been pushed to the remote."
396 local_top_commit_descr=`git log --oneline --max-count=1 $local_top_commit_sha`
397 echo " the local top commit is: \"$local_top_commit_descr\""
398 cd $top_src
399 return 1
402 # If a tag exists with the the tar name, ensure it is tagging the top commit
403 # It may happen if the version set in configure.ac has been previously released
404 tagged_commit_sha=`git rev-list --max-count=1 $tag_name 2>/dev/null`
405 if [ $? -eq 0 ]; then
406 # Check if the tag is pointing to the top commit
407 if [ x"$tagged_commit_sha" != x"$remote_top_commit_sha" ]; then
408 echo "Error: the \"$tag_name\" already exists."
409 echo " this tag is not tagging the top commit."
410 remote_top_commit_descr=`git log --oneline --max-count=1 $remote_top_commit_sha`
411 echo " the top commit is: \"$remote_top_commit_descr\""
412 local_tag_commit_descr=`git log --oneline --max-count=1 $tagged_commit_sha`
413 echo " tag \"$tag_name\" is tagging some other commit: \"$local_tag_commit_descr\""
414 cd $top_src
415 return 1
416 else
417 echo "Info: module already tagged with \"$tag_name\"."
419 else
420 # Tag the top commit with the tar name
421 if [ x"$DRY_RUN" = x ]; then
422 git tag -m $tag_name $tag_name
423 if [ $? -ne 0 ]; then
424 echo "Error: unable to tag module with \"$tag_name\"."
425 cd $top_src
426 return 1
427 else
428 echo "Info: module tagged with \"$tag_name\"."
430 else
431 echo "Info: skipping the commit tagging in dry-run mode."
435 # --------- Now the tarballs are ready to upload ----------
437 # The hostname which is used to connect to the development resources
438 hostname="annarchy.freedesktop.org"
440 # Some hostnames are also used as /srv subdirs
441 host_fdo="www.freedesktop.org"
442 host_xorg="xorg.freedesktop.org"
443 host_dri="dri.freedesktop.org"
445 # Mailing lists where to post the all [Announce] e-mails
446 list_to="xorg-announce@lists.freedesktop.org"
448 # Mailing lists to be CC according to the project (xorg|dri|xkb)
449 list_xorg_user="xorg@lists.freedesktop.org"
450 list_dri_devel="dri-devel@lists.freedesktop.org"
451 list_xkb="xkb@listserv.bat.ru"
452 list_xcb="xcb@lists.freedesktop.org"
453 list_nouveau="nouveau@lists.freedesktop.org"
455 # nouveau is very special.. sigh
456 if [ x"$section" = xnouveau ]; then
457 section=driver
458 list_cc=$list_nouveau
459 else
460 list_cc=$list_xorg_user
463 host_current=$host_xorg
464 section_path=archive/individual/$section
465 srv_path="/srv/$host_current/$section_path"
467 # Handle special cases such as non xorg projects or migrated xorg projects
468 # Xcb has a separate mailing list
469 if [ x"$section" = xxcb ]; then
470 list_cc=$list_xcb
473 # Module mesa/drm goes in the dri "libdrm" section
474 if [ x"$section" = xdrm ]; then
475 host_current=$host_dri
476 section_path=libdrm
477 srv_path="/srv/$host_current/www/$section_path"
478 list_cc=$list_dri_devel
481 # Module xkeyboard-config goes in a subdir of the xorg "data" section
482 if [ x"$section" = xxkeyboard-config ]; then
483 host_current=$host_xorg
484 section_path=archive/individual/data/$section
485 srv_path="/srv/$host_current/$section_path"
486 list_cc=$list_xkb
489 if [ x"$section" = xlibevdev ]; then
490 host_current=$host_fdo
491 section_path="software/$section"
492 srv_path="/srv/$host_current/www/$section_path"
493 list_to=input-tools@lists.freedesktop.org
494 unset list_cc
497 # Use personal web space on the host for unit testing (leave commented out)
498 # srv_path="~/public_html$srv_path"
500 # Check that the server path actually does exist
501 ssh $USER_NAME$hostname ls $srv_path >/dev/null 2>&1 ||
502 if [ $? -ne 0 ]; then
503 echo "Error: the path \"$srv_path\" on the web server does not exist."
504 cd $top_src
505 return 1
508 # Check for already existing tarballs
509 for tarball in $targz $tarbz2 $tarxz; do
510 ssh $USER_NAME$hostname ls $srv_path/$tarball >/dev/null 2>&1
511 if [ $? -eq 0 ]; then
512 if [ "x$FORCE" = "xyes" ]; then
513 echo "Warning: overwriting released tarballs due to --force option."
514 else
515 echo "Error: tarball $tar_name already exists. Use --force to overwrite."
516 cd $top_src
517 return 1
520 done
522 # Upload to host using the 'scp' remote file copy program
523 if [ x"$DRY_RUN" = x ]; then
524 echo "Info: uploading tarballs to web server:"
525 scp $targz $tarbz2 $tarxz $USER_NAME$hostname:$srv_path
526 if [ $? -ne 0 ]; then
527 echo "Error: the tarballs uploading failed."
528 cd $top_src
529 return 1
531 else
532 echo "Info: skipping tarballs uploading in dry-run mode."
533 echo " \"$srv_path\"."
536 # Pushing the top commit tag to the remote repository
537 if [ x$DRY_RUN = x ]; then
538 echo "Info: pushing tag \"$tar_name\" to remote \"$remote_name\":"
539 git push $remote_name $tar_name
540 if [ $? -ne 0 ]; then
541 echo "Error: unable to push tag \"$tar_name\" to the remote repository."
542 echo " it is recommended you fix this manually and not run the script again"
543 cd $top_src
544 return 1
546 else
547 echo "Info: skipped pushing tag \"$tar_name\" to the remote repository in dry-run mode."
550 MD5SUM=`which md5sum || which gmd5sum`
551 SHA1SUM=`which sha1sum || which gsha1sum`
552 SHA256SUM=`which sha256sum || which gsha256sum`
554 # --------- Generate the announce e-mail ------------------
555 # Failing to generate the announce is not considered a fatal error
557 # Git-describe returns only "the most recent tag", it may not be the expected one
558 # However, we only use it for the commit history which will be the same anyway.
559 tag_previous=`git describe --abbrev=0 HEAD^ 2>/dev/null`
560 # Git fails with rc=128 if no tags can be found prior to HEAD^
561 if [ $? -ne 0 ]; then
562 if [ $? -ne 0 ]; then
563 echo "Warning: unable to find a previous tag."
564 echo " perhaps a first release on this branch."
565 echo " Please check the commit history in the announce."
568 if [ x"$tag_previous" != x ]; then
569 # The top commit may not have been tagged in dry-run mode. Use commit.
570 tag_range=$tag_previous..$local_top_commit_sha
571 else
572 tag_range=$tar_name
574 generate_announce > "$tar_name.announce"
575 echo "Info: [ANNOUNCE] template generated in \"$tar_name.announce\" file."
576 echo " Please pgp sign and send it."
578 # --------- Update the JH Build moduleset -----------------
579 # Failing to update the jh moduleset is not considered a fatal error
580 if [ x"$JH_MODULESET" != x ]; then
581 for tarball in $targz $tarbz2 $tarxz; do
582 if [ x$DRY_RUN = x ]; then
583 sha1sum=`$SHA1SUM $tarball | cut -d' ' -f1`
584 $top_src/util/modular/update-moduleset.sh $JH_MODULESET $sha1sum $tarball
585 echo "Info: updated jh moduleset: \"$JH_MODULESET\""
586 else
587 echo "Info: skipping jh moduleset \"$JH_MODULESET\" update in dry-run mode."
590 # $tar* may be unset, so simply loop through all of them and the
591 # first one that is set updates the module file
592 break
593 done
597 # --------- Successful completion --------------------------
598 cd $top_src
599 return 0
603 #------------------------------------------------------------------------------
604 # Function: usage
605 #------------------------------------------------------------------------------
606 # Displays the script usage and exits successfully
608 usage() {
609 basename="`expr "//$0" : '.*/\([^/]*\)'`"
610 cat <<HELP
612 Usage: $basename [options] path...
614 Where "path" is a relative path to a git module, including '.'.
616 Options:
617 --dist make 'dist' instead of 'distcheck'; use with caution
618 --distcheck Default, ignored for compatibility
619 --dry-run Does everything except tagging and uploading tarballs
620 --force Force overwriting an existing release
621 --help Display this help and exit successfully
622 --modfile <file> Release the git modules specified in <file>
623 --moduleset <file> The jhbuild moduleset full pathname to be updated
624 --no-quit Do not quit after error; just print error message
625 --user <name>@ Username of your fdo account if not configured in ssh
627 Environment variables defined by the "make" program and used by release.sh:
628 MAKE The name of the make command [make]
629 MAKEFLAGS: Options to pass to all \$(MAKE) invocations
631 HELP
634 #------------------------------------------------------------------------------
635 # Script main line
636 #------------------------------------------------------------------------------
639 # Choose which make program to use (could be gmake)
640 MAKE=${MAKE:="make"}
642 # Choose which grep program to use (on Solaris, must be gnu grep)
643 if [ "x$GREP" = "x" ] ; then
644 if [ -x /usr/gnu/bin/grep ] ; then
645 GREP=/usr/gnu/bin/grep
646 else
647 GREP=grep
652 # Set the default make tarball creation command
653 MAKE_DIST_CMD=distcheck
655 # Process command line args
656 while [ $# != 0 ]
658 case $1 in
659 # Use 'dist' rather than 'distcheck' to create tarballs
660 # You really only want to do this if you're releasing a module you can't
661 # possibly build-test. Please consider carefully the wisdom of doing so.
662 --dist)
663 MAKE_DIST_CMD=dist
665 # Use 'distcheck' to create tarballs
666 --distcheck)
667 MAKE_DIST_CMD=distcheck
669 # Does everything except uploading tarball
670 --dry-run)
671 DRY_RUN=yes
673 # Force overwriting an existing release
674 # Use only if nothing changed in the git repo
675 --force)
676 FORCE=yes
678 # Display this help and exit successfully
679 --help)
680 usage
681 exit 0
683 # Release the git modules specified in <file>
684 --modfile)
685 check_option_args $1 $2
686 shift
687 MODFILE=$1
689 # The jhbuild moduleset to update with relase info
690 --moduleset)
691 check_option_args $1 $2
692 shift
693 JH_MODULESET=$1
695 # Do not quit after error; just print error message
696 --no-quit)
697 NO_QUIT=yes
699 # Username of your fdo account if not configured in ssh
700 --user)
701 check_option_args $1 $2
702 shift
703 USER_NAME=$1
705 --*)
706 echo ""
707 echo "Error: unknown option: $1"
708 echo ""
709 usage
710 exit 1
713 echo ""
714 echo "Error: unknown option: $1"
715 echo ""
716 usage
717 exit 1
720 if [ x"${MODFILE}" != x ]; then
721 echo ""
722 echo "Error: specifying both modules and --modfile is not permitted"
723 echo ""
724 usage
725 exit 1
727 INPUT_MODULES="${INPUT_MODULES} $1"
729 esac
731 shift
732 done
734 # If no modules specified (blank cmd line) display help
735 check_modules_specification
737 # Read the module file and normalize input in INPUT_MODULES
738 read_modfile
740 # Loop through each module to release
741 # Exit on error if --no-quit no specified
742 process_modules
744 # Print the epilog with final status
745 print_epilog