source: trunk/SOURCE_TOOLS/release/release_tool.pl

Last change on this file was 18914, checked in by westram, 3 years ago
  • fix error handling for piped commands in perl
    • when forking piped commands
      • use error message ($!) instead of exitcode ($?).
      • use message 'failed to fork'.
    • when closing piped commands
      • show IPC errors and exitcode of command.
  • Property svn:executable set to *
File size: 10.7 KB
Line 
1#!/usr/bin/perl
2
3use strict;
4use warnings;
5
6my $ARBHOME = $ENV{ARBHOME};
7die "ARBHOME has to be defined" if not defined $ARBHOME;
8die "ARBHOME has specify a directory (ARBHOME='$ARBHOME')" if not -d $ARBHOME;
9
10my %svn_info = ();
11
12sub 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
42sub 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
80sub 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
95my %known_branches = ();
96my %known_tags = ();
97
98sub 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}
105sub 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
113sub trunkURL()   { return $svn_info{ROOT}.'/trunk'; }
114sub currentURL() { return $svn_info{ROOT}.'/'.$svn_info{SUB}; }
115sub branchURL($) { my ($branch) = @_; return $svn_info{ROOT}.'/branches/'.$branch; }
116sub tagURL($)    { my ($tag)    = @_; return $svn_info{ROOT}.'/tags/'.$tag; }
117
118sub URL2SUB($) {
119  my ($url) = @_;
120  my $sub = substr($url, length($svn_info{ROOT}.'/'));
121  return $sub;
122}
123
124sub getSUB() {
125  my $got = $svn_info{SUB};
126  defined $got || die "SUB undefined";
127  return $got;
128}
129
130sub 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
138sub 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
147sub expectTrunk()   { expectSUB('trunk'); }
148sub expectBranch($) { my ($branch) = @_; expectSUB('branches/'.$branch); }
149sub denyBranch($)   { my ($branch) = @_; denySUB  ('branches/'.$branch); }
150
151sub tag_remove_command($$) {
152  my ($tag,$action) = @_;
153  return "svn delete '".tagURL($tag)."' -m \"[$action] delete tag '$tag'\"";
154}
155sub branch_remove_command($$) {
156  my ($branch,$action) = @_;
157  return "svn delete '".branchURL($branch)."' -m \"[$action] delete branch '$branch'\"";
158}
159sub 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
165sub get_branches() { branch_exists('xxx'); return sort keys %known_branches; }
166sub get_tags() { tag_exists('xxx'); return sort keys %known_tags; }
167
168sub 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
177sub 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
273sub 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
280sub 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
304sub 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}
313main();
Note: See TracBrowser for help on using the repository browser.