diff options
-rwxr-xr-x | build-aux/announce-gen | 557 | ||||
-rwxr-xr-x | build-aux/do-release-commit-and-tag | 179 | ||||
-rwxr-xr-x | build-aux/gnu-web-doc-update | 210 | ||||
-rwxr-xr-x | build-aux/gnupload | 440 |
4 files changed, 1386 insertions, 0 deletions
diff --git a/build-aux/announce-gen b/build-aux/announce-gen new file mode 100755 index 0000000..eeb9071 --- /dev/null +++ b/build-aux/announce-gen @@ -0,0 +1,557 @@ +eval '(exit $?0)' && eval 'exec perl -wS "$0" "$@"' + & eval 'exec perl -wS "$0" $argv:q' + if 0; +# Generate a release announcement message. + +my $VERSION = '2018-03-07 03:46'; # UTC +# The definition above must lie within the first 8 lines in order +# for the Emacs time-stamp write hook (at end) to update it. +# If you change this file with Emacs, please let the write hook +# do its job. Otherwise, update this string manually. + +# Copyright (C) 2002-2018 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +# Written by Jim Meyering + +use strict; + +use Getopt::Long; +use POSIX qw(strftime); + +(my $ME = $0) =~ s|.*/||; + +my %valid_release_types = map {$_ => 1} qw (alpha beta stable); +my @archive_suffixes = ('tar.gz', 'tar.bz2', 'tar.lzma', 'tar.xz'); +my %digest_classes = + ( + 'md5' => (eval { require Digest::MD5; } and 'Digest::MD5'), + 'sha1' => ((eval { require Digest::SHA; } and 'Digest::SHA') + or (eval { require Digest::SHA1; } and 'Digest::SHA1')) + ); +my $srcdir = '.'; + +sub usage ($) +{ + my ($exit_code) = @_; + my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR); + if ($exit_code != 0) + { + print $STREAM "Try '$ME --help' for more information.\n"; + } + else + { + my @types = sort keys %valid_release_types; + print $STREAM <<EOF; +Usage: $ME [OPTIONS] +Generate an announcement message. Run this from builddir. + +OPTIONS: + +These options must be specified: + + --release-type=TYPE TYPE must be one of @types + --package-name=PACKAGE_NAME + --previous-version=VER + --current-version=VER + --gpg-key-id=ID The GnuPG ID of the key used to sign the tarballs + --url-directory=URL_DIR + +The following are optional: + + --news=NEWS_FILE include the NEWS section about this release + from this NEWS_FILE; accumulates. + --srcdir=DIR where to find the NEWS_FILEs (default: $srcdir) + --bootstrap-tools=TOOL_LIST a comma-separated list of tools, e.g., + autoconf,automake,bison,gnulib + --gnulib-version=VERSION report VERSION as the gnulib version, where + VERSION is the result of running git describe + in the gnulib source directory. + required if gnulib is in TOOL_LIST. + --no-print-checksums do not emit MD5 or SHA1 checksums + --archive-suffix=SUF add SUF to the list of archive suffixes + --mail-headers=HEADERS a space-separated list of mail headers, e.g., + To: x\@example.com Cc: y-announce\@example.com,... + + --help display this help and exit + --version output version information and exit + +EOF + } + exit $exit_code; +} + + +=item C<%size> = C<sizes (@file)> + +Compute the sizes of the C<@file> and return them as a hash. Return +C<undef> if one of the computation failed. + +=cut + +sub sizes (@) +{ + my (@file) = @_; + + my $fail = 0; + my %res; + foreach my $f (@file) + { + my $cmd = "du -h $f"; + my $t = `$cmd`; + # FIXME-someday: give a better diagnostic, a la $PROCESS_STATUS + $@ + and (warn "command failed: '$cmd'\n"), $fail = 1; + chomp $t; + $t =~ s/^\s*([\d.]+[MkK]).*/${1}B/; + $res{$f} = $t; + } + return $fail ? undef : %res; +} + +=item C<print_locations ($title, \@url, \%size, @file) + +Print a section C<$title> dedicated to the list of <@file>, which +sizes are stored in C<%size>, and which are available from the C<@url>. + +=cut + +sub print_locations ($\@\%@) +{ + my ($title, $url, $size, @file) = @_; + print "Here are the $title:\n"; + foreach my $url (@{$url}) + { + for my $file (@file) + { + print " $url/$file"; + print " (", $$size{$file}, ")" + if exists $$size{$file}; + print "\n"; + } + } + print "\n"; +} + +=item C<print_checksums (@file) + +Print the MD5 and SHA1 signature section for each C<@file>. + +=cut + +sub print_checksums (@) +{ + my (@file) = @_; + + print "Here are the MD5 and SHA1 checksums:\n"; + print "\n"; + + foreach my $meth (qw (md5 sha1)) + { + my $class = $digest_classes{$meth} or next; + foreach my $f (@file) + { + open IN, '<', $f + or die "$ME: $f: cannot open for reading: $!\n"; + binmode IN; + my $dig = $class->new->addfile(*IN)->hexdigest; + close IN; + print "$dig $f\n"; + } + } + print "\n"; +} + +=item C<print_news_deltas ($news_file, $prev_version, $curr_version) + +Print the section of the NEWS file C<$news_file> addressing changes +between versions C<$prev_version> and C<$curr_version>. + +=cut + +sub print_news_deltas ($$$) +{ + my ($news_file, $prev_version, $curr_version) = @_; + + my $news_name = $news_file; + $news_name =~ s|^\Q$srcdir\E/||; + + print "\n$news_name\n\n"; + + # Print all lines from $news_file, starting with the first one + # that mentions $curr_version up to but not including + # the first occurrence of $prev_version. + my $in_items; + + my $re_prefix = qr/(?:\* )?(?:Noteworthy c|Major c|C)(?i:hanges)/; + + my $found_news; + open NEWS, '<', $news_file + or die "$ME: $news_file: cannot open for reading: $!\n"; + while (defined (my $line = <NEWS>)) + { + if ( ! $in_items) + { + # Match lines like these: + # * Major changes in release 5.0.1: + # * Noteworthy changes in release 6.6 (2006-11-22) [stable] + $line =~ /^$re_prefix.*(?:[^\d.]|$)\Q$curr_version\E(?:[^\d.]|$)/o + or next; + $in_items = 1; + print $line; + } + else + { + # This regexp must not match version numbers in NEWS items. + # For example, they might well say "introduced in 4.5.5", + # and we don't want that to match. + $line =~ /^$re_prefix.*(?:[^\d.]|$)\Q$prev_version\E(?:[^\d.]|$)/o + and last; + print $line; + $line =~ /\S/ + and $found_news = 1; + } + } + close NEWS; + + $in_items + or die "$ME: $news_file: no matching lines for '$curr_version'\n"; + $found_news + or die "$ME: $news_file: no news item found for '$curr_version'\n"; +} + +sub print_changelog_deltas ($$) +{ + my ($package_name, $prev_version) = @_; + + # Print new ChangeLog entries. + + # First find all CVS-controlled ChangeLog files. + use File::Find; + my @changelog; + find ({wanted => sub {$_ eq 'ChangeLog' && -d 'CVS' + and push @changelog, $File::Find::name}}, + '.'); + + # If there are no ChangeLog files, we're done. + @changelog + or return; + my %changelog = map {$_ => 1} @changelog; + + # Reorder the list of files so that if there are ChangeLog + # files in the specified directories, they're listed first, + # in this order: + my @dir = qw ( . src lib m4 config doc ); + + # A typical @changelog array might look like this: + # ./ChangeLog + # ./po/ChangeLog + # ./m4/ChangeLog + # ./lib/ChangeLog + # ./doc/ChangeLog + # ./config/ChangeLog + my @reordered; + foreach my $d (@dir) + { + my $dot_slash = $d eq '.' ? $d : "./$d"; + my $target = "$dot_slash/ChangeLog"; + delete $changelog{$target} + and push @reordered, $target; + } + + # Append any remaining ChangeLog files. + push @reordered, sort keys %changelog; + + # Remove leading './'. + @reordered = map { s!^\./!!; $_ } @reordered; + + print "\nChangeLog entries:\n\n"; + # print join ("\n", @reordered), "\n"; + + $prev_version =~ s/\./_/g; + my $prev_cvs_tag = "\U$package_name\E-$prev_version"; + + my $cmd = "cvs -n diff -u -r$prev_cvs_tag -rHEAD @reordered"; + open DIFF, '-|', $cmd + or die "$ME: cannot run '$cmd': $!\n"; + # Print two types of lines, making minor changes: + # Lines starting with '+++ ', e.g., + # +++ ChangeLog 22 Feb 2003 16:52:51 -0000 1.247 + # and those starting with '+'. + # Don't print the others. + my $prev_printed_line_empty = 1; + while (defined (my $line = <DIFF>)) + { + if ($line =~ /^\+\+\+ /) + { + my $separator = "*"x70 ."\n"; + $line =~ s///; + $line =~ s/\s.*//; + $prev_printed_line_empty + or print "\n"; + print $separator, $line, $separator; + } + elsif ($line =~ /^\+/) + { + $line =~ s///; + print $line; + $prev_printed_line_empty = ($line =~ /^$/); + } + } + close DIFF; + + # The exit code should be 1. + # Allow in case there are no modified ChangeLog entries. + $? == 256 || $? == 128 + or warn "warning: '$cmd' had unexpected exit code or signal ($?)\n"; +} + +sub get_tool_versions ($$) +{ + my ($tool_list, $gnulib_version) = @_; + @$tool_list + or return (); + + my $fail; + my @tool_version_pair; + foreach my $t (@$tool_list) + { + if ($t eq 'gnulib') + { + push @tool_version_pair, ucfirst $t . ' ' . $gnulib_version; + next; + } + # Assume that the last "word" on the first line of + # 'tool --version' output is the version string. + my ($first_line, undef) = split ("\n", `$t --version`); + if ($first_line =~ /.* (\d[\w.-]+)$/) + { + $t = ucfirst $t; + push @tool_version_pair, "$t $1"; + } + else + { + defined $first_line + and $first_line = ''; + warn "$t: unexpected --version output\n:$first_line"; + $fail = 1; + } + } + + $fail + and exit 1; + + return @tool_version_pair; +} + +{ + # Neutralize the locale, so that, for instance, "du" does not + # issue "1,2" instead of "1.2", what confuses our regexps. + $ENV{LC_ALL} = "C"; + + my $mail_headers; + my $release_type; + my $package_name; + my $prev_version; + my $curr_version; + my $gpg_key_id; + my @url_dir_list; + my @news_file; + my $bootstrap_tools; + my $gnulib_version; + my $print_checksums_p = 1; + + # Reformat the warnings before displaying them. + local $SIG{__WARN__} = sub + { + my ($msg) = @_; + # Warnings from GetOptions. + $msg =~ s/Option (\w)/option --$1/; + warn "$ME: $msg"; + }; + + GetOptions + ( + 'mail-headers=s' => \$mail_headers, + 'release-type=s' => \$release_type, + 'package-name=s' => \$package_name, + 'previous-version=s' => \$prev_version, + 'current-version=s' => \$curr_version, + 'gpg-key-id=s' => \$gpg_key_id, + 'url-directory=s' => \@url_dir_list, + 'news=s' => \@news_file, + 'srcdir=s' => \$srcdir, + 'bootstrap-tools=s' => \$bootstrap_tools, + 'gnulib-version=s' => \$gnulib_version, + 'print-checksums!' => \$print_checksums_p, + 'archive-suffix=s' => \@archive_suffixes, + + help => sub { usage 0 }, + version => sub { print "$ME version $VERSION\n"; exit }, + ) or usage 1; + + my $fail = 0; + # Ensure that each required option is specified. + $release_type + or (warn "release type not specified\n"), $fail = 1; + $package_name + or (warn "package name not specified\n"), $fail = 1; + $prev_version + or (warn "previous version string not specified\n"), $fail = 1; + $curr_version + or (warn "current version string not specified\n"), $fail = 1; + $gpg_key_id + or (warn "GnuPG key ID not specified\n"), $fail = 1; + @url_dir_list + or (warn "URL directory name(s) not specified\n"), $fail = 1; + + my @tool_list = split ',', $bootstrap_tools + if $bootstrap_tools; + + grep (/^gnulib$/, @tool_list) ^ defined $gnulib_version + and (warn "when specifying gnulib as a tool, you must also specify\n" + . "--gnulib-version=V, where V is the result of running git describe\n" + . "in the gnulib source directory.\n"), $fail = 1; + + !$release_type || exists $valid_release_types{$release_type} + or (warn "'$release_type': invalid release type\n"), $fail = 1; + + @ARGV + and (warn "too many arguments:\n", join ("\n", @ARGV), "\n"), + $fail = 1; + $fail + and usage 1; + + my $my_distdir = "$package_name-$curr_version"; + + my $xd = "$package_name-$prev_version-$curr_version.xdelta"; + + my @candidates = map { "$my_distdir.$_" } @archive_suffixes; + my @tarballs = grep {-f $_} @candidates; + + @tarballs + or die "$ME: none of " . join(', ', @candidates) . " were found\n"; + my @sizable = @tarballs; + -f $xd + and push @sizable, $xd; + my %size = sizes (@sizable); + %size + or exit 1; + + my $headers = ''; + if (defined $mail_headers) + { + ($headers = $mail_headers) =~ s/\s+(\S+:)/\n$1/g; + $headers .= "\n"; + } + + # The markup is escaped as <\# so that when this script is sent by + # mail (or part of a diff), Gnus is not triggered. + print <<EOF; + +${headers}Subject: $my_distdir released [$release_type] + +<\#secure method=pgpmime mode=sign> + +FIXME: put comments here + +EOF + + if (@url_dir_list == 1 && @tarballs == 1) + { + # When there's only one tarball and one URL, use a more concise form. + my $m = "$url_dir_list[0]/$tarballs[0]"; + print "Here are the compressed sources and a GPG detached signature[*]:\n" + . " $m\n" + . " $m.sig\n\n"; + } + else + { + print_locations ("compressed sources", @url_dir_list, %size, @tarballs); + -f $xd + and print_locations ("xdelta diffs (useful? if so, " + . "please tell bug-gnulib\@gnu.org)", + @url_dir_list, %size, $xd); + my @sig_files = map { "$_.sig" } @tarballs; + print_locations ("GPG detached signatures[*]", @url_dir_list, %size, + @sig_files); + } + + if ($url_dir_list[0] =~ "gnu\.org") + { + print "Use a mirror for higher download bandwidth:\n"; + if (@tarballs == 1 && $url_dir_list[0] =~ m!https://ftp\.gnu\.org/gnu/!) + { + (my $m = "$url_dir_list[0]/$tarballs[0]") + =~ s!https://ftp\.gnu\.org/gnu/!https://ftpmirror\.gnu\.org/!; + print " $m\n" + . " $m.sig\n\n"; + + } + else + { + print " https://www.gnu.org/order/ftp.html\n\n"; + } + } + + $print_checksums_p + and print_checksums (@sizable); + + print <<EOF; +[*] Use a .sig file to verify that the corresponding file (without the +.sig suffix) is intact. First, be sure to download both the .sig file +and the corresponding tarball. Then, run a command like this: + + gpg --verify $tarballs[0].sig + +If that command fails because you don't have the required public key, +then run this command to import it: + + gpg --keyserver keys.gnupg.net --recv-keys $gpg_key_id + +and rerun the 'gpg --verify' command. +EOF + + my @tool_versions = get_tool_versions (\@tool_list, $gnulib_version); + @tool_versions + and print "\nThis release was bootstrapped with the following tools:", + join ('', map {"\n $_"} @tool_versions), "\n"; + + print_news_deltas ($_, $prev_version, $curr_version) + foreach @news_file; + + $release_type eq 'stable' + or print_changelog_deltas ($package_name, $prev_version); + + exit 0; +} + +### Setup "GNU" style for perl-mode and cperl-mode. +## Local Variables: +## mode: perl +## perl-indent-level: 2 +## perl-continued-statement-offset: 2 +## perl-continued-brace-offset: 0 +## perl-brace-offset: 0 +## perl-brace-imaginary-offset: 0 +## perl-label-offset: -2 +## perl-extra-newline-before-brace: t +## perl-merge-trailing-else: nil +## eval: (add-hook 'before-save-hook 'time-stamp) +## time-stamp-start: "my $VERSION = '" +## time-stamp-format: "%:y-%02m-%02d %02H:%02M" +## time-stamp-time-zone: "UTC0" +## time-stamp-end: "'; # UTC" +## End: diff --git a/build-aux/do-release-commit-and-tag b/build-aux/do-release-commit-and-tag new file mode 100755 index 0000000..4472ff5 --- /dev/null +++ b/build-aux/do-release-commit-and-tag @@ -0,0 +1,179 @@ +#!/bin/sh +# In a git/autoconf/automake-enabled project with a NEWS file and a version- +# controlled .prev-version file, automate the procedure by which we record +# the date, release-type and version string in the NEWS file. That commit +# will serve to identify the release, so apply a signed tag to it as well. +VERSION=2018-03-07.03 # UTC + +# Note: this is a bash script (could be zsh or dash) + +# Copyright (C) 2009-2018 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +# Written by Jim Meyering + +ME=$(basename "$0") +warn() { printf '%s: %s\n' "$ME" "$*" >&2; } +die() { warn "$*"; exit 1; } + +help() +{ + cat <<EOF +Usage: $ME [OPTION...] VERSION RELEASE_TYPE + +Run this script from top_srcdir to perform the final pre-release NEWS +update in which the date, release-type and version string are +recorded. Commit that result with a log entry marking the release, +and apply a signed tag. Run it from your project's top-level +directory. + +Requirements: +- you use git for version-control +- a version-controlled .prev-version file +- a NEWS file, with line 3 identical to this: +$noteworthy_stub + +Options: + --branch=BRANCH set release branch (default: $branch) + -C, --builddir=DIR location of (configured) Makefile (default: $builddir) + --help print this help, then exit + --version print version number, then exit + +EXAMPLE: +To update NEWS and tag the beta 8.1 release of coreutils, I would run this: + + $ME 8.1 beta + +Report bugs and patches to <bug-gnulib@gnu.org>. +EOF + exit +} + +version() +{ + year=$(echo "$VERSION" | sed 's/[^0-9].*//') + cat <<EOF +$ME $VERSION +Copyright (C) $year Free Software Foundation, Inc, +License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html> +This is free software: you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law. +EOF + exit +} + +## ------ ## +## Main. ## +## ------ ## + +# Constants. +noteworthy='* Noteworthy changes in release' +noteworthy_stub="$noteworthy ?.? (????-??-??) [?]" + +# Variables. +branch=$(git branch | sed -ne '/^\* /{s///;p;q;}') +builddir=. + +while test $# != 0 +do + # Handle --option=value by splitting apart and putting back on argv. + case $1 in + --*=*) + opt=$(echo "$1" | sed -e 's/=.*//') + val=$(echo "$1" | sed -e 's/[^=]*=//') + shift + set dummy "$opt" "$val" "$@"; shift + ;; + esac + + case $1 in + --help|--version) ${1#--};; + --branch) shift; branch=$1; shift ;; + -C|--builddir) shift; builddir=$1; shift ;; + --*) die "unrecognized option: $1";; + *) break;; + esac +done + +test $# = 2 \ + || die "Usage: $ME [OPTION...] VERSION TYPE" + +ver=$1 +type=$2 + + +## ---------------------- ## +## First, sanity checks. ## +## ---------------------- ## + +# Verify that $ver looks like a version number, and... +echo "$ver"|grep -E '^[0-9][0-9.]*[0-9]$' > /dev/null \ + || die "invalid version: $ver" +prev_ver=$(cat .prev-version) \ + || die 'failed to determine previous version number from .prev-version' + +# Verify that $ver is sensible (> .prev-version). +case $(printf "$prev_ver\n$ver\n"|sort -V -u|tr '\n' ':') in + "$prev_ver:$ver:") ;; + *) die "invalid version: $ver (<= $prev_ver)";; +esac + +case $type in + alpha|beta|stable) ;; + *) die "invalid release type: $type";; +esac + +# No local modifications allowed. +case $(git diff-index --name-only HEAD) in + '') ;; + *) die 'this tree is dirty; commit your changes first';; +esac + +# Ensure the current branch name is correct: +curr_br=$(git rev-parse --symbolic-full-name HEAD) +test "$curr_br" = "refs/heads/$branch" || die not on branch $branch + +# Extract package name from Makefile. +Makefile=$builddir/Makefile +pkg=$(sed -n 's/^PACKAGE = \(.*\)/\1/p' "$Makefile") \ + || die "failed to determine package name from $Makefile" + +# Check that line 3 of NEWS is the stub line about to be replaced. +test "$(sed -n 3p NEWS)" = "$noteworthy_stub" \ + || die "line 3 of NEWS must be exactly '$noteworthy_stub'" + +## --------------- ## +## Then, changes. ## +## --------------- ## + +# Update NEWS to have today's date, plus desired version number and $type. +perl -MPOSIX -ni -e 'my $today = strftime "%F", localtime time;' \ + -e 'my ($type, $ver) = qw('"$type $ver"');' \ + -e 'my $pfx = "'"$noteworthy"'";' \ + -e 'print $.==3 ? "$pfx $ver ($today) [$type]\n" : $_' \ + NEWS || die 'failed to update NEWS' + +printf "version $ver\n\n* NEWS: Record release date.\n" \ + | git commit -F - -a || die 'git commit failed' +git tag -s -m "$pkg $ver" v$ver HEAD || die 'git tag failed' + +# Local variables: +# indent-tabs-mode: nil +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "VERSION=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: " # UTC" +# End: diff --git a/build-aux/gnu-web-doc-update b/build-aux/gnu-web-doc-update new file mode 100755 index 0000000..356c561 --- /dev/null +++ b/build-aux/gnu-web-doc-update @@ -0,0 +1,210 @@ +#!/bin/sh +# Run this after each non-alpha release, to update the web documentation at +# https://www.gnu.org/software/$pkg/manual/ + +VERSION=2018-03-07.03; # UTC + +# Copyright (C) 2009-2018 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +ME=$(basename "$0") +warn() { printf '%s: %s\n' "$ME" "$*" >&2; } +die() { warn "$*"; exit 1; } + +help() +{ + cat <<EOF +Usage: $ME + +Run this script from top_srcdir (no arguments) after each non-alpha +release, to update the web documentation at +https://www.gnu.org/software/\$pkg/manual/ + +This script assumes you're using git for revision control, and +requires a .prev-version file as well as a Makefile, from which it +extracts the version number and package name, respectively. Also, it +assumes all documentation is in the doc/ sub-directory. + +Options: + -C, --builddir=DIR location of (configured) Makefile (default: .) + -n, --dry-run don't actually commit anything + -m, --mirror remove out of date files from document server + --help print this help, then exit + --version print version number, then exit + +Report bugs and patches to <bug-gnulib@gnu.org>. +EOF + exit +} + +version() +{ + year=$(echo "$VERSION" | sed 's/[^0-9].*//') + cat <<EOF +$ME $VERSION +Copyright (C) $year Free Software Foundation, Inc, +License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html> +This is free software: you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law. +EOF + exit +} + +# find_tool ENVVAR NAMES... +# ------------------------- +# Search for a required program. Use the value of ENVVAR, if set, +# otherwise find the first of the NAMES that can be run (i.e., +# supports --version). If found, set ENVVAR to the program name, +# die otherwise. +# +# FIXME: code duplication, see also bootstrap. +find_tool () +{ + find_tool_envvar=$1 + shift + find_tool_names=$@ + eval "find_tool_res=\$$find_tool_envvar" + if test x"$find_tool_res" = x; then + for i + do + if ($i --version </dev/null) >/dev/null 2>&1; then + find_tool_res=$i + break + fi + done + else + find_tool_error_prefix="\$$find_tool_envvar: " + fi + test x"$find_tool_res" != x \ + || die "one of these is required: $find_tool_names" + ($find_tool_res --version </dev/null) >/dev/null 2>&1 \ + || die "${find_tool_error_prefix}cannot run $find_tool_res --version" + eval "$find_tool_envvar=\$find_tool_res" + eval "export $find_tool_envvar" +} + +## ------ ## +## Main. ## +## ------ ## + +# Requirements: everything required to bootstrap your package, plus +# these. +find_tool CVS cvs +find_tool GIT git +find_tool RSYNC rsync +find_tool XARGS gxargs xargs + +builddir=. +dryrun= +rm_stale='echo' +while test $# != 0 +do + # Handle --option=value by splitting apart and putting back on argv. + case $1 in + --*=*) + opt=$(echo "$1" | sed -e 's/=.*//') + val=$(echo "$1" | sed -e 's/[^=]*=//') + shift + set dummy "$opt" "$val" "$@"; shift + ;; + esac + + case $1 in + --help|--version) ${1#--};; + -C|--builddir) shift; builddir=$1; shift ;; + -n|--dry-run) dryrun=echo; shift;; + -m|--mirror) rm_stale=''; shift;; + --*) die "unrecognized option: $1";; + *) break;; + esac +done + +test $# = 0 \ + || die "too many arguments" + +prev=.prev-version +version=$(cat $prev) || die "no $prev file?" +pkg=$(sed -n 's/^PACKAGE = \(.*\)/\1/p' $builddir/Makefile) \ + || die "no Makefile?" +tmp_branch=web-doc-$version-$$ +current_branch=$($GIT branch | sed -ne '/^\* /{s///;p;q;}') + +cleanup() +{ + __st=$? + $dryrun rm -rf "$tmp" + $GIT checkout "$current_branch" + $GIT submodule update --recursive + $GIT branch -d $tmp_branch + exit $__st +} +trap cleanup 0 +trap 'exit $?' 1 2 13 15 + +# We must build using sources for which --version reports the +# just-released version number, not some string like 7.6.18-20761. +# That version string propagates into all documentation. +set -e +$GIT checkout -b $tmp_branch v$version +$GIT submodule update --recursive +./bootstrap +srcdir=$(pwd) +cd "$builddir" +builddir=$(pwd) + ./config.status --recheck + ./config.status + make + make web-manual +cd "$srcdir" +set +e + +tmp=$(mktemp -d web-doc-update.XXXXXX) || exit 1 +( cd $tmp \ + && $CVS -d $USER@cvs.sv.gnu.org:/webcvs/$pkg co $pkg ) +$RSYNC -avP "$builddir"/doc/manual/ $tmp/$pkg/manual + +( + cd $tmp/$pkg/manual + + # Add all the files. This is simpler than trying to add only the + # new ones because of new directories + # First add non empty dirs individually + find . -name CVS -prune -o -type d \! -empty -print \ + | $XARGS -n1 --no-run-if-empty -- $dryrun $CVS add -ko + # Now add all files + find . -name CVS -prune -o -type f -print \ + | $XARGS --no-run-if-empty -- $dryrun $CVS add -ko + + # Report/Remove stale files + # excluding doc server specific files like CVS/* and .symlinks + if test -n "$rm_stale"; then + echo 'Consider the --mirror option if all of the manual is generated,' >&2 + echo 'which will run `cvs remove` to remove stale files.' >&2 + fi + { find . \( -name CVS -o -type f -name '.*' \) -prune -o -type f -print + (cd "$builddir"/doc/manual/ && find . -type f -print | sed p) + } | sort | uniq -u \ + | $XARGS --no-run-if-empty -- ${rm_stale:-$dryrun} $CVS remove -f + + $dryrun $CVS ci -m $version +) + +# Local variables: +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "VERSION=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: "; # UTC" +# End: diff --git a/build-aux/gnupload b/build-aux/gnupload new file mode 100755 index 0000000..2a0bfa3 --- /dev/null +++ b/build-aux/gnupload @@ -0,0 +1,440 @@ +#!/bin/sh +# Sign files and upload them. + +scriptversion=2018-03-07.03; # UTC + +# Copyright (C) 2004-2018 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +# Originally written by Alexandre Duret-Lutz <adl@gnu.org>. +# The master copy of this file is maintained in the gnulib Git repository. +# Please send bug reports and feature requests to bug-gnulib@gnu.org. + +set -e + +GPG='gpg --batch --no-tty' +conffile=.gnuploadrc +to= +dry_run=false +replace= +symlink_files= +delete_files= +delete_symlinks= +collect_var= +dbg= +nl=' +' + +usage="Usage: $0 [OPTION]... [CMD] FILE... [[CMD] FILE...] + +Sign all FILES, and process them at the destinations specified with --to. +If CMD is not given, it defaults to uploading. See examples below. + +Commands: + --delete delete FILES from destination + --symlink create symbolic links + --rmsymlink remove symbolic links + -- treat the remaining arguments as files to upload + +Options: + --to DEST specify a destination DEST for FILES + (multiple --to options are allowed) + --user NAME sign with key NAME + --replace allow replacements of existing files + --symlink-regex[=EXPR] use sed script EXPR to compute symbolic link names + --dry-run do nothing, show what would have been done + (including the constructed directive file) + --version output version information and exit + --help print this help text and exit + +If --symlink-regex is given without EXPR, then the link target name +is created by replacing the version information with '-latest', e.g.: + foo-1.3.4.tar.gz -> foo-latest.tar.gz + +Recognized destinations are: + alpha.gnu.org:DIRECTORY + savannah.gnu.org:DIRECTORY + savannah.nongnu.org:DIRECTORY + ftp.gnu.org:DIRECTORY + build directive files and upload files by FTP + download.gnu.org.ua:{alpha|ftp}/DIRECTORY + build directive files and upload files by SFTP + [user@]host:DIRECTORY upload files with scp + +Options and commands are applied in order. If the file $conffile exists +in the current working directory, its contents are prepended to the +actual command line options. Use this to keep your defaults. Comments +(#) and empty lines in $conffile are allowed. + +<https://www.gnu.org/prep/maintain/html_node/Automated-FTP-Uploads.html> +gives some further background. + +Examples: +1. Upload foobar-1.0.tar.gz to ftp.gnu.org: + gnupload --to ftp.gnu.org:foobar foobar-1.0.tar.gz + +2. Upload foobar-1.0.tar.gz and foobar-1.0.tar.xz to ftp.gnu.org: + gnupload --to ftp.gnu.org:foobar foobar-1.0.tar.gz foobar-1.0.tar.xz + +3. Same as above, and also create symbolic links to foobar-latest.tar.*: + gnupload --to ftp.gnu.org:foobar \\ + --symlink-regex \\ + foobar-1.0.tar.gz foobar-1.0.tar.xz + +4. Upload foobar-0.9.90.tar.gz to two sites: + gnupload --to alpha.gnu.org:foobar \\ + --to sources.redhat.com:~ftp/pub/foobar \\ + foobar-0.9.90.tar.gz + +5. Delete oopsbar-0.9.91.tar.gz and upload foobar-0.9.91.tar.gz + (the -- terminates the list of files to delete): + gnupload --to alpha.gnu.org:foobar \\ + --to sources.redhat.com:~ftp/pub/foobar \\ + --delete oopsbar-0.9.91.tar.gz \\ + -- foobar-0.9.91.tar.gz + +gnupload executes a program ncftpput to do the transfers; if you don't +happen to have an ncftp package installed, the ncftpput-ftp script in +the build-aux/ directory of the gnulib package +(https://savannah.gnu.org/projects/gnulib) may serve as a replacement. + +Send patches and bug reports to <bug-gnulib@gnu.org>." + +# Read local configuration file +if test -r "$conffile"; then + echo "$0: Reading configuration file $conffile" + conf=`sed 's/#.*$//;/^$/d' "$conffile" | tr "\015$nl" ' '` + eval set x "$conf \"\$@\"" + shift +fi + +while test -n "$1"; do + case $1 in + -*) + collect_var= + case $1 in + --help) + echo "$usage" + exit $? + ;; + --to) + if test -z "$2"; then + echo "$0: Missing argument for --to" 1>&2 + exit 1 + elif echo "$2" | grep 'ftp-upload\.gnu\.org' >/dev/null; then + echo "$0: Use ftp.gnu.org:PKGNAME or alpha.gnu.org:PKGNAME" >&2 + echo "$0: for the destination, not ftp-upload.gnu.org (which" >&2 + echo "$0: is used for direct ftp uploads, not with gnupload)." >&2 + echo "$0: See --help and its examples if need be." >&2 + exit 1 + else + to="$to $2" + shift + fi + ;; + --user) + if test -z "$2"; then + echo "$0: Missing argument for --user" 1>&2 + exit 1 + else + GPG="$GPG --local-user $2" + shift + fi + ;; + --delete) + collect_var=delete_files + ;; + --replace) + replace="replace: true" + ;; + --rmsymlink) + collect_var=delete_symlinks + ;; + --symlink-regex=*) + symlink_expr=`expr "$1" : '[^=]*=\(.*\)'` + ;; + --symlink-regex) + symlink_expr='s|-[0-9][0-9\.]*\(-[0-9][0-9]*\)\{0,1\}\.|-latest.|' + ;; + --symlink) + collect_var=symlink_files + ;; + --dry-run|-n) + dry_run=: + ;; + --version) + echo "gnupload $scriptversion" + exit $? + ;; + --) + shift + break + ;; + -*) + echo "$0: Unknown option '$1', try '$0 --help'" 1>&2 + exit 1 + ;; + esac + ;; + *) + if test -z "$collect_var"; then + break + else + eval "$collect_var=\"\$$collect_var $1\"" + fi + ;; + esac + shift +done + +dprint() +{ + echo "Running $* ..." +} + +if $dry_run; then + dbg=dprint +fi + +if test -z "$to"; then + echo "$0: Missing destination sites" >&2 + exit 1 +fi + +if test -n "$symlink_files"; then + x=`echo "$symlink_files" | sed 's/[^ ]//g;s/ //g'` + if test -n "$x"; then + echo "$0: Odd number of symlink arguments" >&2 + exit 1 + fi +fi + +if test $# = 0; then + if test -z "${symlink_files}${delete_files}${delete_symlinks}"; then + echo "$0: No file to upload" 1>&2 + exit 1 + fi +else + # Make sure all files exist. We don't want to ask + # for the passphrase if the script will fail. + for file + do + if test ! -f $file; then + echo "$0: Cannot find '$file'" 1>&2 + exit 1 + elif test -n "$symlink_expr"; then + linkname=`echo $file | sed "$symlink_expr"` + if test -z "$linkname"; then + echo "$0: symlink expression produces empty results" >&2 + exit 1 + elif test "$linkname" = $file; then + echo "$0: symlink expression does not alter file name" >&2 + exit 1 + fi + fi + done +fi + +# Make sure passphrase is not exported in the environment. +unset passphrase +unset passphrase_fd_0 +GNUPGHOME=${GNUPGHOME:-$HOME/.gnupg} + +# Reset PATH to be sure that echo is a built-in. We will later use +# 'echo $passphrase' to output the passphrase, so it is important that +# it is a built-in (third-party programs tend to appear in 'ps' +# listings with their arguments...). +# Remember this script runs with 'set -e', so if echo is not built-in +# it will exit now. +if $dry_run || grep -q "^use-agent" $GNUPGHOME/gpg.conf; then :; else + PATH=/empty echo -n "Enter GPG passphrase: " + stty -echo + read -r passphrase + stty echo + echo + passphrase_fd_0="--passphrase-fd 0" +fi + +if test $# -ne 0; then + for file + do + echo "Signing $file ..." + rm -f $file.sig + echo "$passphrase" | $dbg $GPG $passphrase_fd_0 -ba -o $file.sig $file + done +fi + + +# mkdirective DESTDIR BASE FILE STMT +# Arguments: See upload, below +mkdirective () +{ + stmt="$4" + if test -n "$3"; then + stmt=" +filename: $3$stmt" + fi + + cat >${2}.directive<<EOF +version: 1.2 +directory: $1 +comment: gnupload v. $scriptversion$stmt +EOF + if $dry_run; then + echo "File ${2}.directive:" + cat ${2}.directive + echo "File ${2}.directive:" | sed 's/./-/g' + fi +} + +mksymlink () +{ + while test $# -ne 0 + do + echo "symlink: $1 $2" + shift + shift + done +} + +# upload DEST DESTDIR BASE FILE STMT FILES +# Arguments: +# DEST Destination site; +# DESTDIR Destination directory; +# BASE Base name for the directive file; +# FILE Name of the file to distribute (may be empty); +# STMT Additional statements for the directive file; +# FILES List of files to upload. +upload () +{ + dest=$1 + destdir=$2 + base=$3 + file=$4 + stmt=$5 + files=$6 + + rm -f $base.directive $base.directive.asc + case $dest in + alpha.gnu.org:*) + mkdirective "$destdir" "$base" "$file" "$stmt" + echo "$passphrase" | $dbg $GPG $passphrase_fd_0 --clearsign $base.directive + $dbg ncftpput ftp-upload.gnu.org /incoming/alpha $files $base.directive.asc + ;; + ftp.gnu.org:*) + mkdirective "$destdir" "$base" "$file" "$stmt" + echo "$passphrase" | $dbg $GPG $passphrase_fd_0 --clearsign $base.directive + $dbg ncftpput ftp-upload.gnu.org /incoming/ftp $files $base.directive.asc + ;; + savannah.gnu.org:*) + if test -z "$files"; then + echo "$0: warning: standalone directives not applicable for $dest" >&2 + fi + $dbg ncftpput savannah.gnu.org /incoming/savannah/$destdir $files + ;; + savannah.nongnu.org:*) + if test -z "$files"; then + echo "$0: warning: standalone directives not applicable for $dest" >&2 + fi + $dbg ncftpput savannah.nongnu.org /incoming/savannah/$destdir $files + ;; + download.gnu.org.ua:alpha/*|download.gnu.org.ua:ftp/*) + destdir_p1=`echo "$destdir" | sed 's,^[^/]*/,,'` + destdir_topdir=`echo "$destdir" | sed 's,/.*,,'` + mkdirective "$destdir_p1" "$base" "$file" "$stmt" + echo "$passphrase" | $dbg $GPG $passphrase_fd_0 --clearsign $base.directive + for f in $files $base.directive.asc + do + echo put $f + done | $dbg sftp -b - puszcza.gnu.org.ua:/incoming/$destdir_topdir + ;; + /*) + dest_host=`echo "$dest" | sed 's,:.*,,'` + mkdirective "$destdir" "$base" "$file" "$stmt" + echo "$passphrase" | $dbg $GPG $passphrase_fd_0 --clearsign $base.directive + $dbg cp $files $base.directive.asc $dest_host + ;; + *) + if test -z "$files"; then + echo "$0: warning: standalone directives not applicable for $dest" >&2 + fi + $dbg scp $files $dest + ;; + esac + rm -f $base.directive $base.directive.asc +} + +##### +# Process any standalone directives +stmt= +if test -n "$symlink_files"; then + stmt="$stmt +`mksymlink $symlink_files`" +fi + +for file in $delete_files +do + stmt="$stmt +archive: $file" +done + +for file in $delete_symlinks +do + stmt="$stmt +rmsymlink: $file" +done + +if test -n "$stmt"; then + for dest in $to + do + destdir=`echo $dest | sed 's/[^:]*://'` + upload "$dest" "$destdir" "`hostname`-$$" "" "$stmt" + done +fi + +# Process actual uploads +for dest in $to +do + for file + do + echo "Uploading $file to $dest ..." + stmt= + # + # allowing file replacement is all or nothing. + if test -n "$replace"; then stmt="$stmt +$replace" + fi + # + files="$file $file.sig" + destdir=`echo $dest | sed 's/[^:]*://'` + if test -n "$symlink_expr"; then + linkname=`echo $file | sed "$symlink_expr"` + stmt="$stmt +symlink: $file $linkname +symlink: $file.sig $linkname.sig" + fi + upload "$dest" "$destdir" "$file" "$file" "$stmt" "$files" + done +done + +exit 0 + +# Local variables: +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: "; # UTC" +# End: |