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