2015-11-07 18:59:00 +03:00
|
|
|
#!/usr/bin/perl
|
|
|
|
|
|
|
|
# Script to automate some easy-to-mess-up parts of the PuTTY release
|
|
|
|
# procedure.
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
use warnings;
|
|
|
|
use Getopt::Long;
|
2015-11-12 22:09:36 +03:00
|
|
|
use File::Find;
|
2015-11-07 18:59:00 +03:00
|
|
|
use File::Temp qw/tempdir/;
|
2015-11-12 22:09:36 +03:00
|
|
|
use LWP::UserAgent;
|
2015-11-07 18:59:00 +03:00
|
|
|
|
|
|
|
my $version = undef;
|
2015-11-12 22:09:36 +03:00
|
|
|
my $setver = 0;
|
|
|
|
my $upload = 0;
|
|
|
|
my $precheck = 0;
|
|
|
|
my $postcheck = 0;
|
2017-07-08 11:23:51 +03:00
|
|
|
my $skip_ftp = 0;
|
2015-11-12 22:09:36 +03:00
|
|
|
GetOptions("version=s" => \$version,
|
|
|
|
"setver" => \$setver,
|
|
|
|
"upload" => \$upload,
|
|
|
|
"precheck" => \$precheck,
|
2017-07-08 11:23:51 +03:00
|
|
|
"postcheck" => \$postcheck,
|
|
|
|
"no-ftp" => \$skip_ftp)
|
2015-11-07 18:59:00 +03:00
|
|
|
or &usage();
|
|
|
|
|
2015-11-12 22:09:36 +03:00
|
|
|
# --set-version: construct a local commit which updates the version
|
|
|
|
# number, and the command-line help transcripts in the docs.
|
|
|
|
if ($setver) {
|
|
|
|
defined $version or die "use --version";
|
2015-11-07 18:59:00 +03:00
|
|
|
0 == system "git", "diff-index", "--quiet", "--cached", "HEAD"
|
|
|
|
or die "index is dirty";
|
|
|
|
0 == system "git", "diff-files", "--quiet" or die "working tree is dirty";
|
|
|
|
my $builddir = tempdir(DIR => ".", CLEANUP => 1);
|
2017-07-03 09:19:07 +03:00
|
|
|
0 == system "git archive --format=tar HEAD | ( cd $builddir && tar xf - )"
|
|
|
|
or die;
|
|
|
|
0 == system "cd $builddir && ./mkfiles.pl" or die;
|
|
|
|
0 == system "cd $builddir && ./mkauto.sh" or die;
|
|
|
|
0 == system "cd $builddir && ./configure" or die;
|
2015-11-07 18:59:00 +03:00
|
|
|
0 == system "cd $builddir && make pscp plink RELEASE=${version}" or die;
|
|
|
|
our $pscp_transcript = `cd $builddir && ./pscp --help`;
|
|
|
|
$pscp_transcript =~ s/^Unidentified build/Release ${version}/m or die;
|
|
|
|
$pscp_transcript =~ s/^/\\c /mg;
|
|
|
|
our $plink_transcript = `cd $builddir && ./plink --help`;
|
|
|
|
$plink_transcript =~ s/^Unidentified build/Release ${version}/m or die;
|
|
|
|
$plink_transcript =~ s/^/\\c /mg;
|
|
|
|
&transform("LATEST.VER", sub { s/^\d+\.\d+$/$version/ });
|
|
|
|
&transform("windows/putty.iss", sub {
|
|
|
|
s/^(AppVerName=PuTTY version |VersionInfoTextVersion=Release |AppVersion=|VersionInfoVersion=)\d+\.\d+/$1$version/ });
|
|
|
|
our $transforming = 0;
|
|
|
|
&transform("doc/pscp.but", sub {
|
|
|
|
if (/^\\c.*>pscp$/) { $transforming = 1; $_ .= $pscp_transcript; }
|
|
|
|
elsif (!/^\\c/) { $transforming = 0; }
|
|
|
|
elsif ($transforming) { $_=""; }
|
|
|
|
});
|
|
|
|
$transforming = 0;
|
|
|
|
&transform("doc/plink.but", sub {
|
|
|
|
if (/^\\c.*>plink$/) { $transforming = 1; $_ .= $plink_transcript; }
|
|
|
|
elsif (!/^\\c/) { $transforming = 0; }
|
|
|
|
elsif ($transforming) { $_=""; }
|
|
|
|
});
|
|
|
|
&transform("Buildscr", sub {
|
|
|
|
s!^(set Epoch )\d+!$1 . sprintf "%d", time/86400 - 1000!e });
|
|
|
|
0 == system ("git", "commit", "-a", "-m",
|
|
|
|
"Update version number for ${version} release.") or die;
|
|
|
|
exit 0;
|
|
|
|
}
|
|
|
|
|
2015-11-12 22:09:36 +03:00
|
|
|
# --upload: upload the release to all the places it should live, and
|
|
|
|
# check all signatures and md5sums once it arrives there.
|
|
|
|
if ($upload) {
|
|
|
|
defined $version or die "use --version";
|
|
|
|
|
|
|
|
# Run this inside the build.out directory.
|
2017-01-21 18:17:03 +03:00
|
|
|
-d "maps" or die "no maps directory in cwd";
|
2015-11-12 22:09:36 +03:00
|
|
|
-d "putty" or die "no putty directory in cwd";
|
|
|
|
|
2017-01-21 18:17:03 +03:00
|
|
|
0 == system("rsync", "-av", "maps/",
|
2017-02-21 21:36:29 +03:00
|
|
|
"thyestes:src/putty-local/maps-$version")
|
2015-11-12 22:09:36 +03:00
|
|
|
or die "could not upload link maps";
|
|
|
|
|
2017-02-21 21:36:29 +03:00
|
|
|
for my $location (["thyestes", "www/putty/$version"],
|
|
|
|
["the", "www/putty/$version"],
|
|
|
|
["chiark", "ftp/putty-$version"]) {
|
2015-11-12 22:09:36 +03:00
|
|
|
my ($host, $path) = @$location;
|
|
|
|
0 == system("rsync", "-av", "putty/", "$host:$path")
|
|
|
|
or die "could not upload release to $host";
|
|
|
|
open my $pipe, "|-", "ssh", $host, "cd $path && sh";
|
|
|
|
print $pipe "set -e\n";
|
|
|
|
print $pipe "pwd\n";
|
|
|
|
find({ wanted => sub
|
|
|
|
{
|
|
|
|
if (m!^putty/(.*).gpg!) {
|
|
|
|
my $file = $1;
|
|
|
|
print $pipe "echo verifying $file\n";
|
|
|
|
if ($file =~ /sums$/) {
|
|
|
|
print $pipe "gpg --verify $file.gpg\n";
|
|
|
|
} else {
|
|
|
|
print $pipe "gpg --verify $file.gpg $file\n";
|
|
|
|
}
|
|
|
|
} elsif (m!^putty/(.*sum)s!) {
|
|
|
|
print $pipe "echo checking ${1}s\n";
|
|
|
|
print $pipe "$1 -c ${1}s\n";
|
|
|
|
}
|
|
|
|
}, no_chdir => 1}, "putty");
|
|
|
|
print $pipe "echo all verified ok\n";
|
|
|
|
close $pipe;
|
|
|
|
die "VERIFICATION FAILED on $host" if $? != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
print "Uploaded $version OK!\n";
|
|
|
|
exit 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
# --precheck and --postcheck: attempt to download the release from its
|
|
|
|
# various web and FTP locations.
|
|
|
|
if ($precheck || $postcheck) {
|
|
|
|
defined $version or die "use --version";
|
|
|
|
|
|
|
|
# Run this inside the build.out directory, so we can check the
|
|
|
|
# downloaded files against the exact contents they should have.
|
|
|
|
-d "putty" or die "no putty directory in cwd";
|
|
|
|
|
2016-11-13 14:56:09 +03:00
|
|
|
my $httpprefix = "https://the.earth.li/~sgtatham/putty/";
|
2015-11-12 22:09:36 +03:00
|
|
|
my $ftpprefix = "ftp://ftp.chiark.greenend.org.uk/users/sgtatham/putty-";
|
|
|
|
|
|
|
|
# Go through all the files in build.out.
|
|
|
|
find({ wanted => sub
|
|
|
|
{
|
|
|
|
if (-f $_) {
|
|
|
|
die unless (m!^putty/(.*)$!);
|
|
|
|
my $path = $1;
|
|
|
|
|
|
|
|
# Don't try to check .htaccess - web servers will
|
|
|
|
# treat it weirdly.
|
|
|
|
return if $path =~ m!^(.*/)?.htaccess$!;
|
|
|
|
|
|
|
|
print "Checking $path\n";
|
|
|
|
|
|
|
|
my $real_content = "";
|
|
|
|
open my $fh, "<", $_ or die "$_: open local file: $!";
|
|
|
|
$real_content .= $_ while <$fh>;
|
|
|
|
close $fh;
|
|
|
|
|
|
|
|
my $http_numbered = "${httpprefix}$version/$path";
|
|
|
|
my $http_latest = "${httpprefix}latest/$path";
|
|
|
|
my $ftp_numbered = "${ftpprefix}$version/$path";
|
|
|
|
my $ftp_latest = "${ftpprefix}latest/$path";
|
|
|
|
|
|
|
|
my ($http_uri, $ftp_uri);
|
|
|
|
|
|
|
|
if ($precheck) {
|
|
|
|
# Before the 'latest' links/redirects update,
|
|
|
|
# we just download from explicitly version-
|
|
|
|
# numbered URLs.
|
|
|
|
$http_uri = $http_numbered;
|
|
|
|
$ftp_uri = $ftp_numbered;
|
|
|
|
}
|
|
|
|
if ($postcheck) {
|
|
|
|
# After 'latest' is updated, we're testing that
|
|
|
|
# the redirects work, so we download from the
|
|
|
|
# URLs with 'latest' in them.
|
|
|
|
$http_uri = $http_latest;
|
|
|
|
$ftp_uri = $ftp_latest;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Now test-download the files themselves.
|
2017-07-08 11:23:51 +03:00
|
|
|
unless ($skip_ftp) {
|
|
|
|
my $ftpdata = `curl -s $ftp_uri`;
|
|
|
|
printf " got %d bytes via FTP", length $ftpdata;
|
|
|
|
die "FTP download for $ftp_uri did not match"
|
|
|
|
if $ftpdata ne $real_content;
|
|
|
|
print ", ok\n";
|
|
|
|
}
|
2015-11-12 22:09:36 +03:00
|
|
|
|
|
|
|
my $ua = LWP::UserAgent->new;
|
|
|
|
my $httpresponse = $ua->get($http_uri);
|
|
|
|
my $httpdata = $httpresponse->{_content};
|
|
|
|
printf " got %d bytes via HTTP", length $httpdata;
|
|
|
|
die "HTTP download for $http_uri did not match"
|
|
|
|
if $httpdata ne $real_content;
|
|
|
|
print ", ok\n";
|
|
|
|
|
|
|
|
# Check content types on any files likely to go
|
|
|
|
# wrong.
|
|
|
|
my $ct = $httpresponse->{_headers}->{"content-type"};
|
|
|
|
if (defined $ct) {
|
|
|
|
printf " got content-type %s", $ct;
|
|
|
|
} else {
|
|
|
|
printf " got no content-type";
|
|
|
|
}
|
|
|
|
my $right_ct = undef;
|
|
|
|
if ($path =~ m/\.(hlp|cnt|chm)$/) {
|
|
|
|
$right_ct = "application/octet-stream";
|
|
|
|
} elsif ($path =~ /\.gpg$/) {
|
|
|
|
$right_ct = "application/pgp-signature";
|
|
|
|
}
|
|
|
|
if (defined $right_ct) {
|
|
|
|
if ($ct ne $right_ct) {
|
|
|
|
die "content-type $ct should be $right_ct";
|
|
|
|
} else {
|
|
|
|
print ", ok\n";
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
print "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($postcheck) {
|
|
|
|
# Finally, if we're testing the 'latest' URL,
|
|
|
|
# also check that the HTTP redirect header was
|
|
|
|
# present and correct.
|
|
|
|
my $redirected = $httpresponse->{_request}->{_uri};
|
|
|
|
printf " redirect -> %s\n", $redirected;
|
|
|
|
die "redirect header wrong for $http_uri"
|
|
|
|
if $redirected ne $http_numbered;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, no_chdir => 1}, "putty");
|
|
|
|
|
|
|
|
print "Check OK\n";
|
|
|
|
exit 0;
|
|
|
|
}
|
|
|
|
|
2015-11-07 18:59:00 +03:00
|
|
|
&usage();
|
|
|
|
|
|
|
|
sub transform {
|
|
|
|
my ($filename, $proc) = @_;
|
|
|
|
my $file;
|
|
|
|
open $file, "<", $filename or die "$file: open for read: $!\n";
|
|
|
|
my $data = "";
|
|
|
|
while (<$file>) {
|
|
|
|
$proc->();
|
|
|
|
$data .= $_;
|
|
|
|
}
|
|
|
|
close $file;
|
|
|
|
open $file, ">", $filename or die "$file: open for write: $!\n";
|
|
|
|
print $file $data;
|
|
|
|
close $file or die "$file: close after write: $!\n";;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub usage {
|
|
|
|
die "usage: release.pl --set-version=X.YZ\n";
|
|
|
|
}
|