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