123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- #!/usr/bin/perl
- # Script to automate some easy-to-mess-up parts of the PuTTY release
- # procedure.
- use strict;
- use warnings;
- use Getopt::Long;
- use File::Find;
- use File::Temp qw/tempdir/;
- use LWP::UserAgent;
- my $version = undef;
- my $setver = 0;
- my $upload = 0;
- my $precheck = 0;
- my $postcheck = 0;
- GetOptions("version=s" => \$version,
- "setver" => \$setver,
- "upload" => \$upload,
- "precheck" => \$precheck,
- "postcheck" => \$postcheck)
- or &usage();
- # --setver: 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";
- 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);
- 0 == system "git archive --format=tar HEAD | ( cd $builddir && tar xf - )"
- or die;
- 0 == system "cd $builddir && cmake . -DCMAKE_C_FLAGS=-DRELEASE=${version}" or die;
- 0 == system "cd $builddir && cmake --build . -t pscp -t plink -j" 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/ });
- 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;
- }
- # --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.
- -d "maps" or die "no maps directory in cwd";
- -d "putty" or die "no putty directory in cwd";
- 0 == system("rsync", "-av", "maps/",
- "thyestes:src/putty-local/maps-$version")
- or die "could not upload link maps";
- for my $location (["thyestes", "www/putty/$version"],
- ["the", "www/putty/$version"]) {
- 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 "grep -vF ' (installer version)' ${1}s | grep . | $1 -c\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 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";
- my $httpprefix = "https://the.earth.li/~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 $http_uri;
- if ($precheck) {
- # Before the 'latest' links/redirects update,
- # we just download from explicitly version-
- # numbered URLs.
- $http_uri = $http_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;
- }
- # Now test-download the files themselves.
- 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;
- }
- &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";
- }
|