3 # Creates and upload a git module tarball
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.
13 #------------------------------------------------------------------------------
14 # Function: check_local_changes
15 #------------------------------------------------------------------------------
17 check_local_changes
() {
18 git
diff --quiet HEAD
> /dev
/null
2>&1
21 echo "Uncommitted changes found. Did you forget to commit? Aborting."
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."
27 echo "Alternatively, you can clone the module in another directory"
28 echo "and run ./configure. No need to build if testing was finished."
35 #------------------------------------------------------------------------------
36 # Function: check_option_args
37 #------------------------------------------------------------------------------
39 # perform sanity checks on cmdline args which require arguments
41 # $1 - the option being examined
42 # $2 - the argument to the option
44 # if it returns, everything is good
50 # check for an argument
51 if [ x
"$arg" = x
]; then
53 echo "Error: the '$option' option is missing its required argument."
59 # does the argument look like an option?
60 echo $arg |
$GREP "^-" > /dev
/null
63 echo "Error: the argument '$arg' of option '$option' looks like an option itself."
70 #------------------------------------------------------------------------------
71 # Function: check_modules_specification
72 #------------------------------------------------------------------------------
74 check_modules_specification
() {
76 if [ x
"$MODFILE" = x
]; then
77 if [ x
"${INPUT_MODULES}" = x
]; then
79 echo "Error: no modules specified (blank command line)."
87 #------------------------------------------------------------------------------
88 # Function: generate_announce
89 #------------------------------------------------------------------------------
94 Subject: [ANNOUNCE] $pkg_name $pkg_version
98 `git log --no-merges "$tag_range" | git shortlog`
104 for tarball
in $tarbz2 $targz $tarxz; do
106 http://$host_current/$section_path/$tarball
107 MD5: `$MD5SUM $tarball`
108 SHA1: `$SHA1SUM $tarball`
109 SHA256: `$SHA256SUM $tarball`
110 PGP: http://${host_current}/${section_path}/${tarball}.sig
116 #------------------------------------------------------------------------------
117 # Function: read_modfile
118 #------------------------------------------------------------------------------
120 # Read the module names from the file and set a variable to hold them
121 # This will be the same interface as cmd line supplied modules
125 if [ x
"$MODFILE" != x
]; then
126 # Make sure the file is sane
127 if [ ! -r "$MODFILE" ]; then
128 echo "Error: module file '$MODFILE' is not readable or does not exist."
131 # read from input file, skipping blank and comment lines
134 if [ x
"$line" = x
]; then
138 if echo "$line" |
$GREP -q "^#" ; then
141 INPUT_MODULES
="$INPUT_MODULES $line"
147 #------------------------------------------------------------------------------
148 # Function: print_epilog
149 #------------------------------------------------------------------------------
153 epilog
="======== Successful Completion"
154 if [ x
"$NO_QUIT" != x
]; then
155 if [ x
"$failed_modules" != x
]; then
156 epilog
="======== Partial Completion"
158 elif [ x
"$failed_modules" != x
]; then
159 epilog
="======== Stopped on Error"
163 echo "$epilog `date`"
165 # Report about modules that failed for one reason or another
166 if [ x
"$failed_modules" != x
]; then
167 echo " List of failed modules:"
168 for mod
in $failed_modules; do
176 #------------------------------------------------------------------------------
177 # Function: process_modules
178 #------------------------------------------------------------------------------
180 # Loop through each module to release
181 # Exit on error if --no-quit was not specified
184 for MODULE_RPATH
in ${INPUT_MODULES}; do
185 if ! process_module
; then
186 echo "Error: processing module \"$MODULE_RPATH\" failed."
187 failed_modules
="$failed_modules $MODULE_RPATH"
188 if [ x
"$NO_QUIT" = x
]; then
196 #------------------------------------------------------------------------------
197 # Function: get_section
198 #------------------------------------------------------------------------------
199 # Code 'return 0' on success
200 # Code 'return 1' on error
201 # Sets global variable $section
204 local full_module_url
206 # Obtain the git url in order to find the section to which this module belongs
207 full_module_url
=`git config --get remote.$remote_name.url | sed 's:\.git$::'`
208 if [ $?
-ne 0 ]; then
209 echo "Error: unable to obtain git url for remote \"$remote_name\"."
213 # The last part of the git url will tell us the section. Look for xorg first
214 echo "$full_module_url"
215 module_url
=`echo "$full_module_url" | $GREP -o "/xorg/.*"`
216 if [ $?
-eq 0 ]; then
217 module_url
=`echo $module_url | cut -d'/' -f3,4`
219 # The look for mesa, xcb, etc...
220 module_url
=`echo "$full_module_url" | $GREP -o -e "/mesa/.*" -e "/xcb/.*" -e "/xkeyboard-config" -e "/nouveau/xf86-video-nouveau" -e "/libevdev" -e "/wayland/.*" -e "/evemu"`
221 if [ $?
-eq 0 ]; then
222 module_url
=`echo $module_url | cut -d'/' -f2,3`
224 echo "Error: unable to locate a valid project url from \"$full_module_url\"."
225 echo "Cannot establish url as one of xorg, mesa, xcb, xf86-video-nouveau, xkeyboard-config or wayland"
231 # Find the section (subdirs) where the tarballs are to be uploaded
232 # The module relative path can be app/xfs, xserver, or mesa/drm for example
233 section
=`echo $module_url | cut -d'/' -f1`
234 if [ $?
-ne 0 ]; then
235 echo "Error: unable to extract section from $module_url first field."
239 if [ x
"$section" = xmesa
]; then
240 section
=`echo $module_url | cut -d'/' -f2`
241 if [ $?
-ne 0 ]; then
242 echo "Error: unable to extract section from $module_url second field."
244 elif [ x
"$section" != xdrm
]; then
245 echo "Error: section $section is not supported, only libdrm is."
250 if [ x
"$section" = xwayland
]; then
251 section
=`echo $module_url | cut -d'/' -f2`
252 if [ $?
-ne 0 ]; then
253 echo "Error: unable to extract section from $module_url second field."
261 # Function: sign_or_fail
262 #------------------------------------------------------------------------------
264 # Sign the given file, if any
265 # Output the name of the signature generated to stdout (all other output to
267 # Return 0 on success, 1 on fail
274 if [ $?
-ne 0 ]; then
275 echo "Error: failed to sign $1." >&2
283 #------------------------------------------------------------------------------
284 # Function: process_module
285 #------------------------------------------------------------------------------
286 # Code 'return 0' on success to process the next module
287 # Code 'return 1' on error to process next module if invoked with --no-quit
293 echo "======== Processing \"$top_src/$MODULE_RPATH\""
295 # This is the location where the script has been invoked
296 if [ ! -d $MODULE_RPATH ] ; then
297 echo "Error: $MODULE_RPATH cannot be found under $top_src."
301 # Change directory to be in the git module
303 if [ $?
-ne 0 ]; then
304 echo "Error: failed to cd to $MODULE_RPATH."
308 # ----- Now in the git module *root* directory ----- #
310 # Check that this is indeed a git module
311 if [ ! -d .git
]; then
312 echo "Error: there is no git module here: `pwd`"
316 # Change directory to be in the git build directory (could be out-of-source)
317 # More than one can be found when distcheck has run and failed
318 configNum
=`find . -name config.status -type f | wc -l | sed 's:^ *::'`
319 if [ $?
-ne 0 ]; then
320 echo "Error: failed to locate config.status."
321 echo "Has the module been configured?"
324 if [ x
"$configNum" = x0
]; then
325 echo "Error: failed to locate config.status, has the module been configured?"
328 if [ x
"$configNum" != x1
]; then
329 echo "Error: more than one config.status file was found,"
330 echo " clean-up previously failed attempts at distcheck"
333 status_file
=`find . -name config.status -type f`
334 if [ $?
-ne 0 ]; then
335 echo "Error: failed to locate config.status."
336 echo "Has the module been configured?"
339 build_dir
=`dirname $status_file`
341 if [ $?
-ne 0 ]; then
342 echo "Error: failed to cd to $MODULE_RPATH/$build_dir."
347 # ----- Now in the git module *build* directory ----- #
349 # Check for uncommitted/queued changes.
351 if [ $?
-ne 0 ]; then
356 # Determine what is the current branch and the remote name
357 current_branch
=`git branch | $GREP "\*" | sed -e "s/\* //"`
358 remote_name
=`git config --get branch.$current_branch.remote`
359 remote_branch
=`git config --get branch.$current_branch.merge | cut -d'/' -f3,4`
360 echo "Info: working off the \"$current_branch\" branch tracking the remote \"$remote_name/$remote_branch\"."
364 if [ $?
-ne 0 ]; then
369 # Run 'make dist/distcheck' to ensure the tarball matches the git module content
370 # Important to run make dist/distcheck before looking in Makefile, may need to reconfigure
371 echo "Info: running \"make $MAKE_DIST_CMD\" to create tarballs:"
372 ${MAKE} $MAKEFLAGS $MAKE_DIST_CMD > /dev
/null
373 if [ $?
-ne 0 ]; then
374 echo "Error: \"$MAKE $MAKEFLAGS $MAKE_DIST_CMD\" failed."
379 # Find out the tarname from the makefile
380 pkg_name
=`$GREP '^PACKAGE = ' Makefile | sed 's|PACKAGE = ||'`
381 pkg_version
=`$GREP '^VERSION = ' Makefile | sed 's|VERSION = ||'`
382 tar_name
="$pkg_name-$pkg_version"
383 targz
=$tar_name.
tar.gz
384 tarbz2
=$tar_name.
tar.bz2
385 tarxz
=$tar_name.
tar.xz
387 [ -e $targz ] && ls -l $targz ||
unset targz
388 [ -e $tarbz2 ] && ls -l $tarbz2 ||
unset tarbz2
389 [ -e $tarxz ] && ls -l $tarxz ||
unset tarxz
391 if [ -z "$targz" -a -z "$tarbz2" -a -z "$tarxz" ]; then
392 echo "Error: no compatible tarballs found."
397 # wayland/weston/libinput tag with the version number only
399 if [ x
"$section" = xwayland
] ||
400 [ x
"$section" = xweston
] ||
401 [ x
"$section" = xlibinput
]; then
402 tag_name
="$pkg_version"
405 # evemu tag with the version number prefixed by 'v'
406 if [ x
"$section" = xevemu
]; then
407 tag_name
="v$pkg_version"
411 siggz
="$(sign_or_fail ${targz})"
412 gpgsignerr
=$
((${gpgsignerr} + $?
))
413 sigbz2
="$(sign_or_fail ${tarbz2})"
414 gpgsignerr
=$
((${gpgsignerr} + $?
))
415 sigxz
="$(sign_or_fail ${tarxz})"
416 gpgsignerr
=$
((${gpgsignerr} + $?
))
417 if [ ${gpgsignerr} -ne 0 ]; then
418 echo "Error: unable to sign at least one of the tarballs."
423 # Obtain the top commit SHA which should be the version bump
424 # It should not have been tagged yet (the script will do it later)
425 local_top_commit_sha
=`git rev-list --max-count=1 HEAD`
426 if [ $?
-ne 0 ]; then
427 echo "Error: unable to obtain the local top commit id."
432 # Check that the top commit looks like a version bump
433 git
diff --unified=0 HEAD^ |
$GREP -F $pkg_version >/dev
/null
2>&1
434 if [ $?
-ne 0 ]; then
435 # Wayland repos use m4_define([wayland_major_version], [0])
436 git
diff --unified=0 HEAD^ |
$GREP -E "(major|minor|micro)_version" >/dev
/null
2>&1
437 if [ $?
-ne 0 ]; then
438 echo "Error: the local top commit does not look like a version bump."
439 echo " the diff does not contain the string \"$pkg_version\"."
440 local_top_commit_descr
=`git log --oneline --max-count=1 $local_top_commit_sha`
441 echo " the local top commit is: \"$local_top_commit_descr\""
447 # Check that the top commit has been pushed to remote
448 remote_top_commit_sha
=`git rev-list --max-count=1 $remote_name/$remote_branch`
449 if [ $?
-ne 0 ]; then
450 echo "Error: unable to obtain top commit from the remote repository."
454 if [ x
"$remote_top_commit_sha" != x
"$local_top_commit_sha" ]; then
455 echo "Error: the local top commit has not been pushed to the remote."
456 local_top_commit_descr
=`git log --oneline --max-count=1 $local_top_commit_sha`
457 echo " the local top commit is: \"$local_top_commit_descr\""
462 # If a tag exists with the the tar name, ensure it is tagging the top commit
463 # It may happen if the version set in configure.ac has been previously released
464 tagged_commit_sha
=`git rev-list --max-count=1 $tag_name 2>/dev/null`
465 if [ $?
-eq 0 ]; then
466 # Check if the tag is pointing to the top commit
467 if [ x
"$tagged_commit_sha" != x
"$remote_top_commit_sha" ]; then
468 echo "Error: the \"$tag_name\" already exists."
469 echo " this tag is not tagging the top commit."
470 remote_top_commit_descr
=`git log --oneline --max-count=1 $remote_top_commit_sha`
471 echo " the top commit is: \"$remote_top_commit_descr\""
472 local_tag_commit_descr
=`git log --oneline --max-count=1 $tagged_commit_sha`
473 echo " tag \"$tag_name\" is tagging some other commit: \"$local_tag_commit_descr\""
477 echo "Info: module already tagged with \"$tag_name\"."
480 # Tag the top commit with the tar name
481 if [ x
"$DRY_RUN" = x
]; then
482 git tag
-s -m $tag_name $tag_name
483 if [ $?
-ne 0 ]; then
484 echo "Error: unable to tag module with \"$tag_name\"."
488 echo "Info: module tagged with \"$tag_name\"."
491 echo "Info: skipping the commit tagging in dry-run mode."
495 # --------- Now the tarballs are ready to upload ----------
497 # The hostname which is used to connect to the development resources
498 hostname
="annarchy.freedesktop.org"
500 # Some hostnames are also used as /srv subdirs
501 host_fdo
="www.freedesktop.org"
502 host_xorg
="xorg.freedesktop.org"
503 host_dri
="dri.freedesktop.org"
504 host_wayland
="wayland.freedesktop.org"
506 # Mailing lists where to post the all [Announce] e-mails
507 list_to
="xorg-announce@lists.freedesktop.org"
509 # Mailing lists to be CC according to the project (xorg|dri|xkb)
510 list_xorg_user
="xorg@lists.freedesktop.org"
511 list_dri_devel
="dri-devel@lists.freedesktop.org"
512 list_xkb
="xkb@listserv.bat.ru"
513 list_xcb
="xcb@lists.freedesktop.org"
514 list_nouveau
="nouveau@lists.freedesktop.org"
515 list_wayland
="wayland-devel@lists.freedesktop.org"
517 # nouveau is very special.. sigh
518 if [ x
"$section" = xnouveau
]; then
520 list_cc
=$list_nouveau
522 list_cc
=$list_xorg_user
525 host_current
=$host_xorg
526 section_path
=archive
/individual
/$section
527 srv_path
="/srv/$host_current/$section_path"
529 # Handle special cases such as non xorg projects or migrated xorg projects
530 # Xcb has a separate mailing list
531 if [ x
"$section" = xxcb
]; then
535 # Module mesa/drm goes in the dri "libdrm" section
536 if [ x
"$section" = xdrm
]; then
537 host_current
=$host_dri
539 srv_path
="/srv/$host_current/www/$section_path"
540 list_cc
=$list_dri_devel
543 # Module xkeyboard-config goes in a subdir of the xorg "data" section
544 if [ x
"$section" = xxkeyboard-config
]; then
545 host_current
=$host_xorg
546 section_path
=archive
/individual
/data
/$section
547 srv_path
="/srv/$host_current/$section_path"
551 if [ x
"$section" = xlibevdev
]; then
552 host_current
=$host_fdo
553 section_path
="software/$section"
554 srv_path
="/srv/$host_current/www/$section_path"
555 list_to
=input-tools@lists.freedesktop.org
559 if [ x
"$section" = xwayland
] ||
560 [ x
"$section" = xweston
]; then
561 host_current
=$host_wayland
562 section_path
="releases"
563 srv_path
="/srv/$host_current/www/releases"
564 list_to
=$list_wayland
566 elif [ x
"$section" = xlibinput
]; then
567 host_current
=$host_fdo
568 section_path
="software/libinput"
569 srv_path
="/srv/$host_current/www/$section_path"
570 list_to
=$list_wayland
572 elif [ x
"$section" = xevemu
]; then
573 host_current
=$host_fdo
574 section_path
="software/evemu"
575 srv_path
="/srv/$host_current/www/$section_path"
576 list_to
=input-tools@lists.freedesktop.org
580 # Use personal web space on the host for unit testing (leave commented out)
581 # srv_path="~/public_html$srv_path"
583 # Check that the server path actually does exist
584 ssh $USER_NAME$hostname ls $srv_path >/dev
/null
2>&1 ||
585 if [ $?
-ne 0 ]; then
586 echo "Error: the path \"$srv_path\" on the web server does not exist."
591 # Check for already existing tarballs
592 for tarball
in $targz $tarbz2 $tarxz; do
593 ssh $USER_NAME$hostname ls $srv_path/$tarball >/dev
/null
2>&1
594 if [ $?
-eq 0 ]; then
595 if [ "x$FORCE" = "xyes" ]; then
596 echo "Warning: overwriting released tarballs due to --force option."
598 echo "Error: tarball $tar_name already exists. Use --force to overwrite."
605 # Upload to host using the 'scp' remote file copy program
606 if [ x
"$DRY_RUN" = x
]; then
607 echo "Info: uploading tarballs to web server:"
608 scp
$targz $tarbz2 $tarxz $siggz $sigbz2 $sigxz $USER_NAME$hostname:$srv_path
609 if [ $?
-ne 0 ]; then
610 echo "Error: the tarballs uploading failed."
615 echo "Info: skipping tarballs uploading in dry-run mode."
616 echo " \"$srv_path\"."
619 # Pushing the top commit tag to the remote repository
620 if [ x
$DRY_RUN = x
]; then
621 echo "Info: pushing tag \"$tag_name\" to remote \"$remote_name\":"
622 git push
$remote_name $tag_name
623 if [ $?
-ne 0 ]; then
624 echo "Error: unable to push tag \"$tag_name\" to the remote repository."
625 echo " it is recommended you fix this manually and not run the script again"
630 echo "Info: skipped pushing tag \"$tag_name\" to the remote repository in dry-run mode."
633 MD5SUM
=`which md5sum || which gmd5sum`
634 SHA1SUM
=`which sha1sum || which gsha1sum`
635 SHA256SUM
=`which sha256sum || which gsha256sum`
637 # --------- Generate the announce e-mail ------------------
638 # Failing to generate the announce is not considered a fatal error
640 # Git-describe returns only "the most recent tag", it may not be the expected one
641 # However, we only use it for the commit history which will be the same anyway.
642 tag_previous
=`git describe --abbrev=0 HEAD^ 2>/dev/null`
643 # Git fails with rc=128 if no tags can be found prior to HEAD^
644 if [ $?
-ne 0 ]; then
645 if [ $?
-ne 0 ]; then
646 echo "Warning: unable to find a previous tag."
647 echo " perhaps a first release on this branch."
648 echo " Please check the commit history in the announce."
651 if [ x
"$tag_previous" != x
]; then
652 # The top commit may not have been tagged in dry-run mode. Use commit.
653 tag_range
=$tag_previous..
$local_top_commit_sha
657 generate_announce
> "$tar_name.announce"
658 echo "Info: [ANNOUNCE] template generated in \"$tar_name.announce\" file."
659 echo " Please pgp sign and send it."
661 # --------- Update the JH Build moduleset -----------------
662 # Failing to update the jh moduleset is not considered a fatal error
663 if [ x
"$JH_MODULESET" != x
]; then
664 for tarball
in $targz $tarbz2 $tarxz; do
665 if [ x
$DRY_RUN = x
]; then
666 sha1sum=`$SHA1SUM $tarball | cut -d' ' -f1`
667 $top_src/util
/modular
/update-moduleset.sh
$JH_MODULESET $sha1sum $tarball
668 echo "Info: updated jh moduleset: \"$JH_MODULESET\""
670 echo "Info: skipping jh moduleset \"$JH_MODULESET\" update in dry-run mode."
673 # $tar* may be unset, so simply loop through all of them and the
674 # first one that is set updates the module file
680 # --------- Successful completion --------------------------
686 #------------------------------------------------------------------------------
688 #------------------------------------------------------------------------------
689 # Displays the script usage and exits successfully
692 basename="`expr "//$0" : '.*/\([^/]*\)'`"
695 Usage: $basename [options] path...
697 Where "path" is a relative path to a git module, including '.'.
700 --dist make 'dist' instead of 'distcheck'; use with caution
701 --distcheck Default, ignored for compatibility
702 --dry-run Does everything except tagging and uploading tarballs
703 --force Force overwriting an existing release
704 --help Display this help and exit successfully
705 --modfile <file> Release the git modules specified in <file>
706 --moduleset <file> The jhbuild moduleset full pathname to be updated
707 --no-quit Do not quit after error; just print error message
708 --user <name>@ Username of your fdo account if not configured in ssh
710 Environment variables defined by the "make" program and used by release.sh:
711 MAKE The name of the make command [make]
712 MAKEFLAGS: Options to pass to all \$(MAKE) invocations
717 #------------------------------------------------------------------------------
719 #------------------------------------------------------------------------------
722 # Choose which make program to use (could be gmake)
725 # Choose which grep program to use (on Solaris, must be gnu grep)
726 if [ "x$GREP" = "x" ] ; then
727 if [ -x /usr
/gnu
/bin
/grep ] ; then
728 GREP
=/usr
/gnu
/bin
/grep
734 # Find path for GnuPG v2
735 if [ "x$GPG" = "x" ] ; then
736 if [ -x /usr
/bin
/gpg2
] ; then
743 # Set the default make tarball creation command
744 MAKE_DIST_CMD
=distcheck
746 # Process command line args
750 # Use 'dist' rather than 'distcheck' to create tarballs
751 # You really only want to do this if you're releasing a module you can't
752 # possibly build-test. Please consider carefully the wisdom of doing so.
756 # Use 'distcheck' to create tarballs
758 MAKE_DIST_CMD
=distcheck
760 # Does everything except uploading tarball
764 # Force overwriting an existing release
765 # Use only if nothing changed in the git repo
769 # Display this help and exit successfully
774 # Release the git modules specified in <file>
776 check_option_args
$1 $2
780 # The jhbuild moduleset to update with relase info
782 check_option_args
$1 $2
786 # Do not quit after error; just print error message
790 # Username of your fdo account if not configured in ssh
792 check_option_args
$1 $2
798 echo "Error: unknown option: $1"
805 echo "Error: unknown option: $1"
811 if [ x
"${MODFILE}" != x
]; then
813 echo "Error: specifying both modules and --modfile is not permitted"
818 INPUT_MODULES
="${INPUT_MODULES} $1"
825 # If no modules specified (blank cmd line) display help
826 check_modules_specification
828 # Read the module file and normalize input in INPUT_MODULES
831 # Loop through each module to release
832 # Exit on error if --no-quit no specified
835 # Print the epilog with final status