1#!/bin/sh 2# Sign files and upload them. 3 4scriptversion=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@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 25set -e 26 27GPG='gpg --batch --no-tty' 28conffile=.gnuploadrc 29to= 30dry_run=false 31replace= 32symlink_files= 33delete_files= 34delete_symlinks= 35collect_var= 36dbg= 37nl=' 38' 39 40usage="Usage: $0 [OPTION]... [CMD] FILE... [[CMD] FILE...] 41 42Sign all FILES, and process them at selected destinations according to CMD. 43<http://www.gnu.org/prep/maintain/html_node/Automated-FTP-Uploads.html> 44explains further. 45 46Commands: 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 52Options: 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 63If --symlink-regex is given without EXPR, then the link target name 64is created by replacing the version information with '-latest', e.g.: 65 66 foo-1.3.4.tar.gz -> foo-latest.tar.gz 67 68Recognized 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 78Options and commands are applied in order. If the file $conffile exists 79in the current working directory, its contents are prepended to the 80actual command line options. Use this to keep your defaults. Comments 81(#) and empty lines in $conffile are allowed. 82 83Examples: 841. Upload foobar-1.0.tar.gz to ftp.gnu.org: 85 gnupload --to ftp.gnu.org:foobar foobar-1.0.tar.gz 86 872. 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 903. 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 954. 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 1005. 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 107gnupload uses the ncftpput program to do the transfers; if you don't 108happen to have an ncftp package installed, the ncftpput-ftp script in 109the build-aux/ directory of the gnulib package 110(http://savannah.gnu.org/projects/gnulib) may serve as a replacement. 111 112Send patches and bug reports to <bug-gnulib@gnu.org>." 113 114# Read local configuration file 115if 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 120fi 121 122while 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 193done 194 195dprint() 196{ 197 echo "Running $* ..." 198} 199 200if $dry_run; then 201 dbg=dprint 202fi 203 204if test -z "$to"; then 205 echo "$0: Missing destination sites" >&2 206 exit 1 207fi 208 209if 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 215fi 216 217if 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 222else 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 241fi 242 243# Make sure passphrase is not exported in the environment. 244unset passphrase 245unset passphrase_fd_0 246GNUPGHOME=${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. 254if $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" 261fi 262 263if 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 270fi 271 272 273# mkdirective DESTDIR BASE FILE STMT 274# Arguments: See upload, below 275mkdirective () 276{ 277 stmt="$4" 278 if test -n "$3"; then 279 stmt=" 280filename: $3$stmt" 281 fi 282 283 cat >${2}.directive<<EOF 284version: 1.2 285directory: $1 286comment: gnupload v. $scriptversion$stmt 287EOF 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 295mksymlink () 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. 313upload () 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 374stmt= 375if test -n "$symlink_files"; then 376 stmt="$stmt 377`mksymlink $symlink_files`" 378fi 379 380for file in $delete_files 381do 382 stmt="$stmt 383archive: $file" 384done 385 386for file in $delete_symlinks 387do 388 stmt="$stmt 389rmsymlink: $file" 390done 391 392if 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 398fi 399 400# Process actual uploads 401for dest in $to 402do 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 418symlink: $file $linkname 419symlink: $file.sig $linkname.sig" 420 fi 421 upload "$dest" "$destdir" "$file" "$file" "$stmt" "$files" 422 done 423done 424 425exit 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