| 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(); |
|---|