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