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