Home | History | Annotate | Line # | Download | only in build-aux
gnupload revision 1.1
      1 #!/bin/sh
      2 # Sign files and upload them.
      3 
      4 scriptversion=2012-01-15.15; # UTC
      5 
      6 # Copyright (C) 2004-2012 Free Software Foundation, Inc.
      7 #
      8 # This program is free software; you can redistribute it and/or modify
      9 # it under the terms of the GNU General Public License as published by
     10 # the Free Software Foundation; either version 2, or (at your option)
     11 # any later version.
     12 #
     13 # This program is distributed in the hope that it will be useful,
     14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
     15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16 # GNU General Public License for more details.
     17 #
     18 # You should have received a copy of the GNU General Public License
     19 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
     20 
     21 # Originally written by Alexandre Duret-Lutz <adl (at] gnu.org>.
     22 # The master copy of this file is maintained in the gnulib Git repository.
     23 # Please send bug reports and feature requests to bug-gnulib (at] gnu.org.
     24 
     25 set -e
     26 
     27 GPG='gpg --batch --no-tty'
     28 conffile=.gnuploadrc
     29 to=
     30 dry_run=false
     31 symlink_files=
     32 delete_files=
     33 delete_symlinks=
     34 collect_var=
     35 dbg=
     36 nl='
     37 '
     38 
     39 usage="Usage: $0 [OPTION]... [CMD] FILE... [[CMD] FILE...]
     40 
     41 Sign all FILES, and process them at selected destinations according to CMD.
     42 <http://www.gnu.org/prep/maintain/html_node/Automated-FTP-Uploads.html>
     43 explains further.
     44 
     45 Commands:
     46   --delete                 delete FILES from destination
     47   --symlink                create symbolic links
     48   --rmsymlink              remove symbolic links
     49   --                       treat the remaining arguments as files to upload
     50 
     51 Options:
     52   --help                   print this help text and exit
     53   --to DEST                specify one destination for FILES
     54                            (multiple --to options are allowed)
     55   --user NAME              sign with key NAME
     56   --symlink-regex[=EXPR]   use sed script EXPR to compute symbolic link names
     57   --dry-run                do nothing, show what would have been done
     58   --version                output version information and exit
     59 
     60 If --symlink-regex is given without EXPR, then the link target name
     61 is created by replacing the version information with '-latest', e.g.:
     62 
     63   foo-1.3.4.tar.gz -> foo-latest.tar.gz
     64 
     65 Recognized destinations are:
     66   alpha.gnu.org:DIRECTORY
     67   savannah.gnu.org:DIRECTORY
     68   savannah.nongnu.org:DIRECTORY
     69   ftp.gnu.org:DIRECTORY
     70                            build directive files and upload files by FTP
     71   download.gnu.org.ua:{alpha|ftp}/DIRECTORY
     72                            build directive files and upload files by SFTP
     73   [user@]host:DIRECTORY    upload files with scp
     74 
     75 Options and commands are applied in order.  If the file $conffile exists
     76 in the current working directory, its contents are prepended to the
     77 actual command line options.  Use this to keep your defaults.  Comments
     78 (#) and empty lines in $conffile are allowed.
     79 
     80 Examples:
     81 1. Upload foobar-1.0.tar.gz to ftp.gnu.org:
     82   gnupload --to ftp.gnu.org:foobar foobar-1.0.tar.gz
     83 
     84 2. Upload foobar-1.0.tar.gz and foobar-1.0.tar.xz to ftp.gnu.org:
     85   gnupload --to ftp.gnu.org:foobar foobar-1.0.tar.gz foobar-1.0.tar.xz
     86 
     87 3. Same as above, and also create symbolic links to foobar-latest.tar.*:
     88   gnupload --to ftp.gnu.org:foobar \\
     89            --symlink-regex \\
     90            foobar-1.0.tar.gz foobar-1.0.tar.xz
     91 
     92 4. Upload foobar-0.9.90.tar.gz to two sites:
     93   gnupload --to alpha.gnu.org:foobar \\
     94            --to sources.redhat.com:~ftp/pub/foobar \\
     95            foobar-0.9.90.tar.gz
     96 
     97 5. Delete oopsbar-0.9.91.tar.gz and upload foobar-0.9.91.tar.gz
     98    (the -- terminates the list of files to delete):
     99   gnupload --to alpha.gnu.org:foobar \\
    100            --to sources.redhat.com:~ftp/pub/foobar \\
    101            --delete oopsbar-0.9.91.tar.gz \\
    102            -- foobar-0.9.91.tar.gz
    103 
    104 gnupload uses the ncftpput program to do the transfers; if you don't
    105 happen to have an ncftp package installed, the ncftpput-ftp script in
    106 the build-aux/ directory of the gnulib package
    107 (http://savannah.gnu.org/projects/gnulib) may serve as a replacement.
    108 
    109 Send patches and bug reports to <bug-gnulib (at] gnu.org>."
    110 
    111 # Read local configuration file
    112 if test -r "$conffile"; then
    113   echo "$0: Reading configuration file $conffile"
    114   conf=`sed 's/#.*$//;/^$/d' "$conffile" | tr "\015$nl" '  '`
    115   eval set x "$conf \"\$@\""
    116   shift
    117 fi
    118 
    119 while test -n "$1"; do
    120   case $1 in
    121   -*)
    122     collect_var=
    123     case $1 in
    124     --help)
    125       echo "$usage"
    126       exit $?
    127       ;;
    128     --to)
    129       if test -z "$2"; then
    130         echo "$0: Missing argument for --to" 1>&2
    131         exit 1
    132       else
    133         to="$to $2"
    134         shift
    135       fi
    136       ;;
    137     --user)
    138       if test -z "$2"; then
    139         echo "$0: Missing argument for --user" 1>&2
    140         exit 1
    141       else
    142         GPG="$GPG --local-user $2"
    143         shift
    144       fi
    145       ;;
    146     --delete)
    147       collect_var=delete_files
    148       ;;
    149     --rmsymlink)
    150       collect_var=delete_symlinks
    151       ;;
    152     --symlink-regex=*)
    153       symlink_expr=`expr "$1" : '[^=]*=\(.*\)'`
    154       ;;
    155     --symlink-regex)
    156       symlink_expr='s|-[0-9][0-9\.]*\(-[0-9][0-9]*\)\{0,1\}\.|-latest.|'
    157       ;;
    158     --symlink)
    159       collect_var=symlink_files
    160       ;;
    161     --dry-run|-n)
    162       dry_run=:
    163       ;;
    164     --version)
    165       echo "gnupload $scriptversion"
    166       exit $?
    167       ;;
    168     --)
    169       shift
    170       break
    171       ;;
    172     -*)
    173       echo "$0: Unknown option '$1', try '$0 --help'" 1>&2
    174       exit 1
    175       ;;
    176     esac
    177     ;;
    178   *)
    179     if test -z "$collect_var"; then
    180       break
    181     else
    182       eval "$collect_var=\"\$$collect_var $1\""
    183     fi
    184     ;;
    185   esac
    186   shift
    187 done
    188 
    189 dprint()
    190 {
    191   echo "Running $* ..."
    192 }
    193 
    194 if $dry_run; then
    195   dbg=dprint
    196 fi
    197 
    198 if test -z "$to"; then
    199   echo "$0: Missing destination sites" >&2
    200   exit 1
    201 fi
    202 
    203 if test -n "$symlink_files"; then
    204   x=`echo "$symlink_files" | sed 's/[^ ]//g;s/  //g'`
    205   if test -n "$x"; then
    206     echo "$0: Odd number of symlink arguments" >&2
    207     exit 1
    208   fi
    209 fi
    210 
    211 if test $# = 0; then
    212   if test -z "${symlink_files}${delete_files}${delete_symlinks}"; then
    213     echo "$0: No file to upload" 1>&2
    214     exit 1
    215   fi
    216 else
    217   # Make sure all files exist.  We don't want to ask
    218   # for the passphrase if the script will fail.
    219   for file
    220   do
    221     if test ! -f $file; then
    222       echo "$0: Cannot find '$file'" 1>&2
    223       exit 1
    224     elif test -n "$symlink_expr"; then
    225       linkname=`echo $file | sed "$symlink_expr"`
    226       if test -z "$linkname"; then
    227         echo "$0: symlink expression produces empty results" >&2
    228         exit 1
    229       elif test "$linkname" = $file; then
    230         echo "$0: symlink expression does not alter file name" >&2
    231         exit 1
    232       fi
    233     fi
    234   done
    235 fi
    236 
    237 # Make sure passphrase is not exported in the environment.
    238 unset passphrase
    239 
    240 # Reset PATH to be sure that echo is a built-in.  We will later use
    241 # 'echo $passphrase' to output the passphrase, so it is important that
    242 # it is a built-in (third-party programs tend to appear in 'ps'
    243 # listings with their arguments...).
    244 # Remember this script runs with 'set -e', so if echo is not built-in
    245 # it will exit now.
    246 PATH=/empty echo -n "Enter GPG passphrase: "
    247 stty -echo
    248 read -r passphrase
    249 stty echo
    250 echo
    251 
    252 if test $# -ne 0; then
    253   for file
    254   do
    255     echo "Signing $file ..."
    256     rm -f $file.sig
    257     echo "$passphrase" | $dbg $GPG --passphrase-fd 0 -ba -o $file.sig $file
    258   done
    259 fi
    260 
    261 
    262 # mkdirective DESTDIR BASE FILE STMT
    263 # Arguments: See upload, below
    264 mkdirective ()
    265 {
    266   stmt="$4"
    267   if test -n "$3"; then
    268     stmt="
    269 filename: $3$stmt"
    270   fi
    271 
    272   cat >${2}.directive<<EOF
    273 version: 1.1
    274 directory: $1
    275 comment: gnupload v. $scriptversion$stmt
    276 EOF
    277   if $dry_run; then
    278     echo "File ${2}.directive:"
    279     cat ${2}.directive
    280     echo "File ${2}.directive:" | sed 's/./-/g'
    281   fi
    282 }
    283 
    284 mksymlink ()
    285 {
    286   while test $# -ne 0
    287   do
    288     echo "symlink: $1 $2"
    289     shift
    290     shift
    291   done
    292 }
    293 
    294 # upload DEST DESTDIR BASE FILE STMT FILES
    295 # Arguments:
    296 #  DEST     Destination site;
    297 #  DESTDIR  Destination directory;
    298 #  BASE     Base name for the directive file;
    299 #  FILE     Name of the file to distribute (may be empty);
    300 #  STMT     Additional statements for the directive file;
    301 #  FILES    List of files to upload.
    302 upload ()
    303 {
    304   dest=$1
    305   destdir=$2
    306   base=$3
    307   file=$4
    308   stmt=$5
    309   files=$6
    310 
    311   rm -f $base.directive $base.directive.asc
    312   case $dest in
    313     alpha.gnu.org:*)
    314       mkdirective "$destdir" "$base" "$file" "$stmt"
    315       echo "$passphrase" | $dbg $GPG --passphrase-fd 0 --clearsign $base.directive
    316       $dbg ncftpput ftp-upload.gnu.org /incoming/alpha $files $base.directive.asc
    317       ;;
    318     ftp.gnu.org:*)
    319       mkdirective "$destdir" "$base" "$file" "$stmt"
    320       echo "$passphrase" | $dbg $GPG --passphrase-fd 0 --clearsign $base.directive
    321       $dbg ncftpput ftp-upload.gnu.org /incoming/ftp $files $base.directive.asc
    322       ;;
    323     savannah.gnu.org:*)
    324       if test -z "$files"; then
    325         echo "$0: warning: standalone directives not applicable for $dest" >&2
    326       fi
    327       $dbg ncftpput savannah.gnu.org /incoming/savannah/$destdir $files
    328       ;;
    329     savannah.nongnu.org:*)
    330       if test -z "$files"; then
    331         echo "$0: warning: standalone directives not applicable for $dest" >&2
    332       fi
    333       $dbg ncftpput savannah.nongnu.org /incoming/savannah/$destdir $files
    334       ;;
    335     download.gnu.org.ua:alpha/*|download.gnu.org.ua:ftp/*)
    336       destdir_p1=`echo "$destdir" | sed 's,^[^/]*/,,'`
    337       destdir_topdir=`echo "$destdir" | sed 's,/.*,,'`
    338       mkdirective "$destdir_p1" "$base" "$file" "$stmt"
    339       echo "$passphrase" | $dbg $GPG --passphrase-fd 0 --clearsign $base.directive
    340       for f in $files $base.directive.asc
    341       do
    342         echo put $f
    343       done | $dbg sftp -b - puszcza.gnu.org.ua:/incoming/$destdir_topdir
    344       ;;
    345     /*)
    346       dest_host=`echo "$dest" | sed 's,:.*,,'`
    347       mkdirective "$destdir" "$base" "$file" "$stmt"
    348       echo "$passphrase" | $dbg $GPG --passphrase-fd 0 --clearsign $base.directive
    349       $dbg cp $files $base.directive.asc $dest_host
    350       ;;
    351     *)
    352       if test -z "$files"; then
    353         echo "$0: warning: standalone directives not applicable for $dest" >&2
    354       fi
    355       $dbg scp $files $dest
    356       ;;
    357   esac
    358   rm -f $base.directive $base.directive.asc
    359 }
    360 
    361 #####
    362 # Process any standalone directives
    363 stmt=
    364 if test -n "$symlink_files"; then
    365   stmt="$stmt
    366 `mksymlink $symlink_files`"
    367 fi
    368 
    369 for file in $delete_files
    370 do
    371   stmt="$stmt
    372 archive: $file"
    373 done
    374 
    375 for file in $delete_symlinks
    376 do
    377   stmt="$stmt
    378 rmsymlink: $file"
    379 done
    380 
    381 if test -n "$stmt"; then
    382   for dest in $to
    383   do
    384     destdir=`echo $dest | sed 's/[^:]*://'`
    385     upload "$dest" "$destdir" "`hostname`-$$" "" "$stmt"
    386   done
    387 fi
    388 
    389 # Process actual uploads
    390 for dest in $to
    391 do
    392   for file
    393   do
    394     echo "Uploading $file to $dest ..."
    395     stmt=
    396     files="$file $file.sig"
    397     destdir=`echo $dest | sed 's/[^:]*://'`
    398     if test -n "$symlink_expr"; then
    399       linkname=`echo $file | sed "$symlink_expr"`
    400       stmt="$stmt
    401 symlink: $file $linkname
    402 symlink: $file.sig $linkname.sig"
    403     fi
    404     upload "$dest" "$destdir" "$file" "$file" "$stmt" "$files"
    405   done
    406 done
    407 
    408 exit 0
    409 
    410 # Local variables:
    411 # eval: (add-hook 'write-file-hooks 'time-stamp)
    412 # time-stamp-start: "scriptversion="
    413 # time-stamp-format: "%:y-%02m-%02d.%02H"
    414 # time-stamp-time-zone: "UTC"
    415 # time-stamp-end: "; # UTC"
    416 # End:
    417