gnupload 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. #!/bin/sh
  2. # Sign files and upload them.
  3. scriptversion=2022-01-27.18; # UTC
  4. # Copyright (C) 2004-2023 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 2, 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 2 but gpg is still version 1.
  26. # FIXME-2020: remove, once all major distros ship gpg version 2 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. copyright_year=`echo "$scriptversion" | sed -e 's/[^0-9].*//'`
  126. copyright="Copyright (C) ${copyright_year} Free Software Foundation, Inc.
  127. License GPLv2+: GNU GPL version 2 or later <https://gnu.org/licenses/gpl.html>.
  128. This is free software: you are free to change and redistribute it.
  129. There is NO WARRANTY, to the extent permitted by law."
  130. # Read local configuration file
  131. if test -r "$conffile"; then
  132. echo "$0: Reading configuration file $conffile"
  133. conf=`sed 's/#.*$//;/^$/d' "$conffile" | tr "\015$nl" ' '`
  134. eval set x "$conf \"\$@\""
  135. shift
  136. fi
  137. while test -n "$1"; do
  138. case $1 in
  139. -*)
  140. collect_var=
  141. case $1 in
  142. -h | --help)
  143. echo "$usage"
  144. exit $?
  145. ;;
  146. --to)
  147. if test -z "$2"; then
  148. echo "$0: Missing argument for --to" 1>&2
  149. exit 1
  150. elif echo "$2" | grep 'ftp-upload\.gnu\.org' >/dev/null; then
  151. echo "$0: Use ftp.gnu.org:PKGNAME or alpha.gnu.org:PKGNAME" >&2
  152. echo "$0: for the destination, not ftp-upload.gnu.org (which" >&2
  153. echo "$0: is used for direct ftp uploads, not with gnupload)." >&2
  154. echo "$0: See --help and its examples if need be." >&2
  155. exit 1
  156. else
  157. to="$to $2"
  158. shift
  159. fi
  160. ;;
  161. --user)
  162. if test -z "$2"; then
  163. echo "$0: Missing argument for --user" 1>&2
  164. exit 1
  165. else
  166. GPG="$GPG --local-user $2"
  167. shift
  168. fi
  169. ;;
  170. --delete)
  171. collect_var=delete_files
  172. ;;
  173. --replace)
  174. replace="replace: true"
  175. ;;
  176. --rmsymlink)
  177. collect_var=delete_symlinks
  178. ;;
  179. --symlink-regex=*)
  180. symlink_expr=`expr "$1" : '[^=]*=\(.*\)'`
  181. ;;
  182. --symlink-regex)
  183. symlink_expr='s|-[0-9][0-9\.]*\(-[0-9][0-9]*\)\{0,1\}\.|-latest.|'
  184. ;;
  185. --symlink)
  186. collect_var=symlink_files
  187. ;;
  188. -n | --dry-run)
  189. dry_run=:
  190. ;;
  191. --version)
  192. echo "gnupload $scriptversion"
  193. echo "$copyright"
  194. exit 0
  195. ;;
  196. --)
  197. shift
  198. break
  199. ;;
  200. -*)
  201. echo "$0: Unknown option '$1', try '$0 --help'" 1>&2
  202. exit 1
  203. ;;
  204. esac
  205. ;;
  206. *)
  207. if test -z "$collect_var"; then
  208. break
  209. else
  210. eval "$collect_var=\"\$$collect_var $1\""
  211. fi
  212. ;;
  213. esac
  214. shift
  215. done
  216. dprint()
  217. {
  218. echo "Running $* ..."
  219. }
  220. if $dry_run; then
  221. dbg=dprint
  222. fi
  223. if test -z "$to"; then
  224. echo "$0: Missing destination sites" >&2
  225. exit 1
  226. fi
  227. if test -n "$symlink_files"; then
  228. x=`echo "$symlink_files" | sed 's/[^ ]//g;s/ //g'`
  229. if test -n "$x"; then
  230. echo "$0: Odd number of symlink arguments" >&2
  231. exit 1
  232. fi
  233. fi
  234. if test $# = 0; then
  235. if test -z "${symlink_files}${delete_files}${delete_symlinks}"; then
  236. echo "$0: No file to upload" 1>&2
  237. exit 1
  238. fi
  239. else
  240. # Make sure all files exist. We don't want to ask
  241. # for the passphrase if the script will fail.
  242. for file
  243. do
  244. if test ! -f $file; then
  245. echo "$0: Cannot find '$file'" 1>&2
  246. exit 1
  247. elif test -n "$symlink_expr"; then
  248. linkname=`echo $file | sed "$symlink_expr"`
  249. if test -z "$linkname"; then
  250. echo "$0: symlink expression produces empty results" >&2
  251. exit 1
  252. elif test "$linkname" = $file; then
  253. echo "$0: symlink expression does not alter file name" >&2
  254. exit 1
  255. fi
  256. fi
  257. done
  258. fi
  259. # Make sure passphrase is not exported in the environment.
  260. unset passphrase
  261. unset passphrase_fd_0
  262. GNUPGHOME=${GNUPGHOME:-$HOME/.gnupg}
  263. # Reset PATH to be sure that echo is a built-in. We will later use
  264. # 'echo $passphrase' to output the passphrase, so it is important that
  265. # it is a built-in (third-party programs tend to appear in 'ps'
  266. # listings with their arguments...).
  267. # Remember this script runs with 'set -e', so if echo is not built-in
  268. # it will exit now.
  269. if $dry_run || grep -q "^use-agent" $GNUPGHOME/gpg.conf; then :; else
  270. PATH=/empty echo -n "Enter GPG passphrase: "
  271. stty -echo
  272. read -r passphrase
  273. stty echo
  274. echo
  275. passphrase_fd_0="--passphrase-fd 0"
  276. fi
  277. if test $# -ne 0; then
  278. for file
  279. do
  280. echo "Signing $file ..."
  281. rm -f $file.sig
  282. echo "$passphrase" | $dbg $GPG $passphrase_fd_0 -ba -o $file.sig $file
  283. done
  284. fi
  285. # mkdirective DESTDIR BASE FILE STMT
  286. # Arguments: See upload, below
  287. mkdirective ()
  288. {
  289. stmt="$4"
  290. if test -n "$3"; then
  291. stmt="
  292. filename: $3$stmt"
  293. fi
  294. cat >${2}.directive<<EOF
  295. version: 1.2
  296. directory: $1
  297. comment: gnupload v. $scriptversion$stmt
  298. EOF
  299. if $dry_run; then
  300. echo "File ${2}.directive:"
  301. cat ${2}.directive
  302. echo "File ${2}.directive:" | sed 's/./-/g'
  303. fi
  304. }
  305. mksymlink ()
  306. {
  307. while test $# -ne 0
  308. do
  309. echo "symlink: $1 $2"
  310. shift
  311. shift
  312. done
  313. }
  314. # upload DEST DESTDIR BASE FILE STMT FILES
  315. # Arguments:
  316. # DEST Destination site;
  317. # DESTDIR Destination directory;
  318. # BASE Base name for the directive file;
  319. # FILE Name of the file to distribute (may be empty);
  320. # STMT Additional statements for the directive file;
  321. # FILES List of files to upload.
  322. upload ()
  323. {
  324. dest=$1
  325. destdir=$2
  326. base=$3
  327. file=$4
  328. stmt=$5
  329. files=$6
  330. rm -f $base.directive $base.directive.asc
  331. case $dest in
  332. alpha.gnu.org:*)
  333. mkdirective "$destdir" "$base" "$file" "$stmt"
  334. echo "$passphrase" | $dbg $GPG $passphrase_fd_0 --clearsign $base.directive
  335. $dbg ncftpput ftp-upload.gnu.org /incoming/alpha $files $base.directive.asc
  336. ;;
  337. ftp.gnu.org:*)
  338. mkdirective "$destdir" "$base" "$file" "$stmt"
  339. echo "$passphrase" | $dbg $GPG $passphrase_fd_0 --clearsign $base.directive
  340. $dbg ncftpput ftp-upload.gnu.org /incoming/ftp $files $base.directive.asc
  341. ;;
  342. savannah.gnu.org:*)
  343. if test -z "$files"; then
  344. echo "$0: warning: standalone directives not applicable for $dest" >&2
  345. fi
  346. $dbg ncftpput savannah.gnu.org /incoming/savannah/$destdir $files
  347. ;;
  348. savannah.nongnu.org:*)
  349. if test -z "$files"; then
  350. echo "$0: warning: standalone directives not applicable for $dest" >&2
  351. fi
  352. $dbg ncftpput savannah.nongnu.org /incoming/savannah/$destdir $files
  353. ;;
  354. download.gnu.org.ua:alpha/*|download.gnu.org.ua:ftp/*)
  355. destdir_p1=`echo "$destdir" | sed 's,^[^/]*/,,'`
  356. destdir_topdir=`echo "$destdir" | sed 's,/.*,,'`
  357. mkdirective "$destdir_p1" "$base" "$file" "$stmt"
  358. echo "$passphrase" | $dbg $GPG $passphrase_fd_0 --clearsign $base.directive
  359. for f in $files $base.directive.asc
  360. do
  361. echo put $f
  362. done | $dbg sftp -b - puszcza.gnu.org.ua:/incoming/$destdir_topdir
  363. ;;
  364. /*)
  365. dest_host=`echo "$dest" | sed 's,:.*,,'`
  366. mkdirective "$destdir" "$base" "$file" "$stmt"
  367. echo "$passphrase" | $dbg $GPG $passphrase_fd_0 --clearsign $base.directive
  368. $dbg cp $files $base.directive.asc $dest_host
  369. ;;
  370. *)
  371. if test -z "$files"; then
  372. echo "$0: warning: standalone directives not applicable for $dest" >&2
  373. fi
  374. $dbg scp $files $dest
  375. ;;
  376. esac
  377. rm -f $base.directive $base.directive.asc
  378. }
  379. #####
  380. # Process any standalone directives
  381. stmt=
  382. if test -n "$symlink_files"; then
  383. stmt="$stmt
  384. `mksymlink $symlink_files`"
  385. fi
  386. for file in $delete_files
  387. do
  388. stmt="$stmt
  389. archive: $file"
  390. done
  391. for file in $delete_symlinks
  392. do
  393. stmt="$stmt
  394. rmsymlink: $file"
  395. done
  396. if test -n "$stmt"; then
  397. for dest in $to
  398. do
  399. destdir=`echo $dest | sed 's/[^:]*://'`
  400. upload "$dest" "$destdir" "`hostname`-$$" "" "$stmt"
  401. done
  402. fi
  403. # Process actual uploads
  404. for dest in $to
  405. do
  406. for file
  407. do
  408. echo "Uploading $file to $dest ..."
  409. stmt=
  410. #
  411. # allowing file replacement is all or nothing.
  412. if test -n "$replace"; then stmt="$stmt
  413. $replace"
  414. fi
  415. #
  416. files="$file $file.sig"
  417. destdir=`echo $dest | sed 's/[^:]*://'`
  418. if test -n "$symlink_expr"; then
  419. linkname=`echo $file | sed "$symlink_expr"`
  420. stmt="$stmt
  421. symlink: $file $linkname
  422. symlink: $file.sig $linkname.sig"
  423. fi
  424. upload "$dest" "$destdir" "$file" "$file" "$stmt" "$files"
  425. done
  426. done
  427. exit 0
  428. # Local variables:
  429. # eval: (add-hook 'before-save-hook 'time-stamp)
  430. # time-stamp-start: "scriptversion="
  431. # time-stamp-format: "%:y-%02m-%02d.%02H"
  432. # time-stamp-time-zone: "UTC0"
  433. # time-stamp-end: "; # UTC"
  434. # End: