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