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