1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
|
#!/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;
my $skip_ftp = 0;
GetOptions("version=s" => \$version,
"setver" => \$setver,
"upload" => \$upload,
"precheck" => \$precheck,
"postcheck" => \$postcheck,
"no-ftp" => \$skip_ftp)
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 && ./mkfiles.pl" or die;
0 == system "cd $builddir && ./mkauto.sh" or die;
0 == system "cd $builddir && ./configure" or die;
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/ });
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"],
["chiark", "ftp/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 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";
my $httpprefix = "https://the.earth.li/~sgtatham/putty/";
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.
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";
}
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";
}
|