1 | #!/usr/bin/perl |
---|
2 | |
---|
3 | use strict; |
---|
4 | use warnings; |
---|
5 | |
---|
6 | my $ARBHOME = $ENV{ARBHOME}; |
---|
7 | die "ARBHOME has to be defined" if not defined $ARBHOME; |
---|
8 | die "ARBHOME has specify a directory (ARBHOME='$ARBHOME')" if not -d $ARBHOME; |
---|
9 | |
---|
10 | my %svn_info = (); |
---|
11 | |
---|
12 | sub retrieve_svn_info() { |
---|
13 | %svn_info = (); |
---|
14 | my $cmd = "(cd '$ARBHOME'; svn info)"; |
---|
15 | open(INFO,$cmd.'|') || die "failed to fork '$cmd' (Reason: $!)"; |
---|
16 | foreach (<INFO>) { |
---|
17 | chomp; |
---|
18 | if (/^Repository\sRoot:\s+/o) { $svn_info{ROOT} = $'; } |
---|
19 | elsif (/^Revision:\s+/o) { $svn_info{REVISION} = $'; } |
---|
20 | elsif (/^URL:\s+/o) { $svn_info{URL} = $'; } |
---|
21 | # else { print "info='$_'\n"; } |
---|
22 | } |
---|
23 | close(INFO) || die "failed to execute '$cmd' (Reason: $! exitcode=$?)"; |
---|
24 | |
---|
25 | if (not defined $svn_info{ROOT}) { die "Failed to detect SVN root"; } |
---|
26 | |
---|
27 | { |
---|
28 | my $rootlen = length($svn_info{ROOT}); |
---|
29 | my $prefix = substr($svn_info{URL},0,$rootlen); |
---|
30 | my $suffix = substr($svn_info{URL},$rootlen+1); |
---|
31 | if ($prefix ne $svn_info{ROOT}) { |
---|
32 | die "prefix!=ROOT ('$prefix' != '".$svn_info{ROOT}."')"; |
---|
33 | } |
---|
34 | $svn_info{SUB} = $suffix; |
---|
35 | } |
---|
36 | |
---|
37 | print "-------------------- [WC info]\n"; |
---|
38 | foreach (sort keys %svn_info) { print "$_='".$svn_info{$_}."'\n"; } |
---|
39 | print "--------------------\n"; |
---|
40 | } |
---|
41 | |
---|
42 | sub getArbVersion() { |
---|
43 | my ($tag,$version) = (undef,undef); |
---|
44 | eval { |
---|
45 | my $arb_build = $ARBHOME.'/TEMPLATES/arb_build.h'; |
---|
46 | my $version_info = $ARBHOME.'/SOURCE_TOOLS/version_info'; |
---|
47 | |
---|
48 | die "missing expected file '$arb_build'" if not -f $arb_build; |
---|
49 | die "missing expected file '$version_info'" if not -f $version_info; |
---|
50 | |
---|
51 | open(BUILD,'<'.$arb_build) || die "can't read '$arb_build' (Reason: $!)"; |
---|
52 | foreach (<BUILD>) { |
---|
53 | if (/define\s+ARB_VERSION\s+"(.*)"/o) { $tag = $1; } |
---|
54 | } |
---|
55 | close(BUILD); |
---|
56 | |
---|
57 | { |
---|
58 | my ($minor,$major) = (undef,undef); |
---|
59 | open(VERSION,'<'.$version_info) || die "can't read '$version_info' (Reason: $!)"; |
---|
60 | foreach (<VERSION>) { |
---|
61 | if (/^MINOR=([0-9]+)$/o) { $minor = $1; } |
---|
62 | if (/^MAJOR=([0-9]+)$/o) { $major = $1; } |
---|
63 | } |
---|
64 | close(VERSION); |
---|
65 | if (not defined $minor) { die "Failed to retrieve MINOR from $version_info"; } |
---|
66 | if (not defined $major) { die "Failed to retrieve MAJOR from $version_info"; } |
---|
67 | $version = "$major.$minor"; |
---|
68 | } |
---|
69 | |
---|
70 | if (not defined $tag) { die "Failed to retrieve ARB_VERSION from $arb_build"; } |
---|
71 | defined $version || die; |
---|
72 | }; |
---|
73 | if ($@) { |
---|
74 | die "Note: maybe you forgot to 'make all'?\n". |
---|
75 | "Error while retrieving ARB version: $@"; |
---|
76 | } |
---|
77 | return ($tag,$version); |
---|
78 | } |
---|
79 | |
---|
80 | sub getExisting($) { |
---|
81 | my ($baseUrl) = @_; |
---|
82 | |
---|
83 | my @existing = (); |
---|
84 | my $cmd = "svn list '$baseUrl'"; |
---|
85 | open(LIST,$cmd.'|') || die "failed to fork '$cmd' (Reason: $!)"; |
---|
86 | foreach (<LIST>) { |
---|
87 | chomp; |
---|
88 | if (/\/$/o) { push @existing, $`; } |
---|
89 | else { die "Unexpected content '$_' (received from '$cmd')"; } |
---|
90 | } |
---|
91 | close(LIST) || die "failed to execute '$cmd' (Reason: $! exitcode=$?)"; |
---|
92 | return @existing; |
---|
93 | } |
---|
94 | |
---|
95 | my %known_branches = (); |
---|
96 | my %known_tags = (); |
---|
97 | |
---|
98 | sub branch_exists($) { |
---|
99 | my ($branch) = @_; |
---|
100 | if (not %known_branches) { |
---|
101 | %known_branches = map { $_ => 1; } getExisting($svn_info{ROOT}.'/branches'); |
---|
102 | } |
---|
103 | return exists $known_branches{$branch}; |
---|
104 | } |
---|
105 | sub tag_exists($) { |
---|
106 | my ($tag) = @_; |
---|
107 | if (not %known_tags) { |
---|
108 | %known_tags = map { $_ => 1; } getExisting($svn_info{ROOT}.'/tags'); |
---|
109 | } |
---|
110 | return exists $known_tags{$tag}; |
---|
111 | } |
---|
112 | |
---|
113 | sub trunkURL() { return $svn_info{ROOT}.'/trunk'; } |
---|
114 | sub currentURL() { return $svn_info{ROOT}.'/'.$svn_info{SUB}; } |
---|
115 | sub branchURL($) { my ($branch) = @_; return $svn_info{ROOT}.'/branches/'.$branch; } |
---|
116 | sub tagURL($) { my ($tag) = @_; return $svn_info{ROOT}.'/tags/'.$tag; } |
---|
117 | |
---|
118 | sub URL2SUB($) { |
---|
119 | my ($url) = @_; |
---|
120 | my $sub = substr($url, length($svn_info{ROOT}.'/')); |
---|
121 | return $sub; |
---|
122 | } |
---|
123 | |
---|
124 | sub getSUB() { |
---|
125 | my $got = $svn_info{SUB}; |
---|
126 | defined $got || die "SUB undefined"; |
---|
127 | return $got; |
---|
128 | } |
---|
129 | |
---|
130 | sub expectSUB($) { |
---|
131 | my ($expected) = @_; |
---|
132 | my $got = getSUB(); |
---|
133 | if ($got ne $expected) { |
---|
134 | die "Error: this is only possible in '$expected' (you are in '$got')"; |
---|
135 | } |
---|
136 | } |
---|
137 | |
---|
138 | sub denySUB($) { |
---|
139 | my ($expected) = @_; |
---|
140 | my $got = getSUB(); |
---|
141 | if ($got eq $expected) { |
---|
142 | die "Error: this is NOT possible in '$expected'"; |
---|
143 | } |
---|
144 | } |
---|
145 | |
---|
146 | |
---|
147 | sub expectTrunk() { expectSUB('trunk'); } |
---|
148 | sub expectBranch($) { my ($branch) = @_; expectSUB('branches/'.$branch); } |
---|
149 | sub denyBranch($) { my ($branch) = @_; denySUB ('branches/'.$branch); } |
---|
150 | |
---|
151 | sub tag_remove_command($$) { |
---|
152 | my ($tag,$action) = @_; |
---|
153 | return "svn delete '".tagURL($tag)."' -m \"[$action] delete tag '$tag'\""; |
---|
154 | } |
---|
155 | sub branch_remove_command($$) { |
---|
156 | my ($branch,$action) = @_; |
---|
157 | return "svn delete '".branchURL($branch)."' -m \"[$action] delete branch '$branch'\""; |
---|
158 | } |
---|
159 | sub die_due_to_tag($$) { |
---|
160 | my ($tag,$desc) = @_; |
---|
161 | my $remove_cmd = tag_remove_command($tag,$desc); |
---|
162 | die "tag '$tag' already exists.\nTo remove that tag use\n$remove_cmd\n "; |
---|
163 | } |
---|
164 | |
---|
165 | sub get_branches() { branch_exists('xxx'); return sort keys %known_branches; } |
---|
166 | sub get_tags() { tag_exists('xxx'); return sort keys %known_tags; } |
---|
167 | |
---|
168 | sub build_command($) { |
---|
169 | my ($branch_url) = @_; |
---|
170 | my ($root_rel_branch) = URL2SUB($branch_url); |
---|
171 | if ((not defined $root_rel_branch) or ($root_rel_branch eq '')) { |
---|
172 | die "invalid argument '$branch_url'"; |
---|
173 | } |
---|
174 | return "# in separate shell: run_arb_builds.sh $root_rel_branch"; |
---|
175 | } |
---|
176 | |
---|
177 | sub perform($$) { |
---|
178 | my ($action,$arg) = @_; |
---|
179 | retrieve_svn_info(); |
---|
180 | |
---|
181 | my @commands = (); |
---|
182 | |
---|
183 | my ($tag,$version) = getArbVersion(); |
---|
184 | |
---|
185 | if ($action eq 'branch_rc1') { |
---|
186 | expectTrunk(); |
---|
187 | push @commands, "# check version and changelog in trunk are set correctly; see SOURCE_TOOLS/release/HOWTO.release"; |
---|
188 | if (branch_exists('rc')) { push @commands, branch_remove_command('rc', $action); } |
---|
189 | push @commands, "svn copy '".trunkURL().'@'.$svn_info{REVISION}."' '".branchURL('rc')."' -m \"[$action] create rc1 for arb $version\""; |
---|
190 | push @commands, "# increment version in trunk; see SOURCE_TOOLS/release/HOWTO.release"; |
---|
191 | push @commands, build_command(branchURL('rc')); |
---|
192 | push @commands, "svn switch '".branchURL('rc')."'"; |
---|
193 | push @commands, "make show_version"; |
---|
194 | push @commands, "SOURCE_TOOLS/release/release_tool.pl tag_rc"; |
---|
195 | } |
---|
196 | elsif ($action eq 'branch_stable') { |
---|
197 | my $got = getSUB(); |
---|
198 | if ($got eq 'rc') { |
---|
199 | ; |
---|
200 | } |
---|
201 | elsif ($got eq 'trunk') { |
---|
202 | push @commands, "# check version and changelog in trunk are set correctly; see SOURCE_TOOLS/release/HOWTO.release"; |
---|
203 | if (branch_exists('rc')) { push @commands, branch_remove_command('rc', $action); } |
---|
204 | } |
---|
205 | else { |
---|
206 | die "Error: this is only possible in 'rc' or 'trunk' (you are in '$got')"; |
---|
207 | } |
---|
208 | if (branch_exists('stable')) { push @commands, branch_remove_command('stable', $action); } |
---|
209 | push @commands, "svn copy '".currentURL().'@'.$svn_info{REVISION}."' '".branchURL('stable')."' -m \"[$action from $got] arb $version\""; |
---|
210 | if ($got eq 'trunk') { push @commands, "# increment version in trunk; see SOURCE_TOOLS/release/HOWTO.release"; } |
---|
211 | push @commands, build_command(branchURL('stable')); |
---|
212 | push @commands, "svn switch '".branchURL('stable')."'"; |
---|
213 | push @commands, "make show_version"; |
---|
214 | push @commands, "SOURCE_TOOLS/release/release_tool.pl tag_stable"; |
---|
215 | } |
---|
216 | elsif ($action eq 'tag_rc') { |
---|
217 | expectBranch('rc'); |
---|
218 | if (($tag =~ /devel/oi) or ($tag =~ /rev/oi) or (not $tag =~ /^arb-/o)) { die "Invalid tag '$tag'"; } |
---|
219 | if (tag_exists($tag)) { die_due_to_tag($tag, 'invalid rc'); } |
---|
220 | push @commands, "svn copy '".branchURL('rc').'@'.$svn_info{REVISION}."' '".tagURL($tag)."' -m \"[$action] '$tag'\""; |
---|
221 | } |
---|
222 | elsif ($action eq 'tag_stable') { |
---|
223 | expectBranch('stable'); |
---|
224 | if (($tag =~ /devel/oi) or ($tag =~ /rev/oi) or (not $tag =~ /^arb-/o)) { die "Invalid tag '$tag'"; } |
---|
225 | if (tag_exists($tag)) { die_due_to_tag($tag, 'invalid release'); } |
---|
226 | push @commands, "svn copy '".branchURL('stable').'@'.$svn_info{REVISION}."' '".tagURL($tag)."' -m \"[$action] release '$tag'\""; |
---|
227 | } |
---|
228 | elsif ($action eq 'tag_custom') { |
---|
229 | if (not defined $arg) { |
---|
230 | die "Expected additional argument 'tag'"; |
---|
231 | } |
---|
232 | |
---|
233 | denyBranch('rc'); |
---|
234 | denyBranch('stable'); |
---|
235 | $tag = $arg; # use given arg as tagname |
---|
236 | |
---|
237 | if (($tag =~ /dev/oi) or ($tag =~ /rev/oi)) { die "Invalid tag '$tag'"; } |
---|
238 | if (tag_exists($tag)) { |
---|
239 | my $remove_cmd = "svn delete '".tagURL($tag)."' -m \"[$action] delete invalid tag '$tag'\""; |
---|
240 | die "tag '$tag' already exists.\nTo remove that tag use\n$remove_cmd\n "; |
---|
241 | } |
---|
242 | push @commands, "svn copy '".currentURL().'@'.$svn_info{REVISION}."' '".tagURL($tag)."' -m \"[$action] '$tag'\""; |
---|
243 | } |
---|
244 | elsif ($action eq 'rm') { |
---|
245 | if (not defined $arg) { |
---|
246 | die "Expected additional argument 'action'"; |
---|
247 | } |
---|
248 | my $rm_action = $arg; |
---|
249 | print "To remove branches:\n"; foreach (get_branches()) { print branch_remove_command($_,$rm_action)."\n"; } |
---|
250 | print "To remove tags:\n"; foreach (get_tags()) { print tag_remove_command($_,$rm_action)."\n"; } |
---|
251 | } |
---|
252 | else { |
---|
253 | die "Unknown action '$action'"; |
---|
254 | } |
---|
255 | |
---|
256 | if ($action =~ /tag/) { |
---|
257 | push @commands, build_command(tagURL($tag)); |
---|
258 | } |
---|
259 | |
---|
260 | if (scalar(@commands)) { |
---|
261 | print "-------------------- [Commands to execute for '$action']\n"; |
---|
262 | foreach (@commands) { |
---|
263 | if ($_ =~ /^#\s/o) { $_ = $&.'[MANUALLY] '.$'; } |
---|
264 | print $_."\n"; |
---|
265 | } |
---|
266 | print "--------------------\n"; |
---|
267 | } |
---|
268 | |
---|
269 | print "Note: Please check the above commands for errors,\n"; |
---|
270 | print " then copy & paste to execute them.\n"; |
---|
271 | } |
---|
272 | |
---|
273 | sub warnya() { |
---|
274 | print "--------------------------------------------------------------------------------\n"; |
---|
275 | print "IMPORTANT: This script is for ARB adminstrators only.\n"; |
---|
276 | print " Please do not misuse this script!\n"; |
---|
277 | print "--------------------------------------------------------------------------------\n"; |
---|
278 | } |
---|
279 | |
---|
280 | sub show_usage($) { |
---|
281 | my ($err) = @_; |
---|
282 | warnya(); |
---|
283 | print "\n"; |
---|
284 | print "Usage: release_tool.pl [action [arg]]\n"; |
---|
285 | print "known 'action's:\n"; |
---|
286 | print "\n"; |
---|
287 | print " branch_rc1 branch a new release candidate from 'trunk' (uses WC-revision!)\n"; |
---|
288 | print " branch_stable branch a new release from 'branches/rc' (uses WC-revision!)\n"; |
---|
289 | print " OR from 'trunk' (uses WC-revision!)\n"; |
---|
290 | print "\n"; |
---|
291 | print " tag_rc tag rc in 'branches/rc' (uses WC-revision!)\n"; |
---|
292 | print " tag_stable tag release in 'branches/stable' (uses WC-revision!)\n"; |
---|
293 | print " tag_custom tag tag custom version anywhere (uses WC-revision!)\n"; |
---|
294 | print "\n"; |
---|
295 | print " rm action helper to get rid of unwanted branches/tags\n"; |
---|
296 | print "\n"; |
---|
297 | print "Note: currently does not execute commands (only shows them onto console)\n"; |
---|
298 | print "\n"; |
---|
299 | warnya(); |
---|
300 | if (defined $err) { print "\nError: $err\n"; } |
---|
301 | exit 1; |
---|
302 | } |
---|
303 | |
---|
304 | sub main() { |
---|
305 | my $args = scalar(@ARGV); |
---|
306 | if ($args < 1 or $args > 2) { |
---|
307 | show_usage(undef); |
---|
308 | } |
---|
309 | my $action = $ARGV[0]; |
---|
310 | my $arg = $ARGV[1]; |
---|
311 | perform($action,$arg); |
---|
312 | } |
---|
313 | main(); |
---|