gnupload 13 KB

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