| 1 | # Movable Type (r) Open Source (C) 2001-2008 Six Apart, Ltd. |
|---|
| 2 | # This program is distributed under the terms of the |
|---|
| 3 | # GNU General Public License, version 2. |
|---|
| 4 | # |
|---|
| 5 | # $Id$ |
|---|
| 6 | |
|---|
| 7 | package Build; |
|---|
| 8 | our $VERSION = '0.08'; |
|---|
| 9 | |
|---|
| 10 | =head1 NAME |
|---|
| 11 | |
|---|
| 12 | Build - Movable Type build functionality |
|---|
| 13 | |
|---|
| 14 | =head1 SYNOPSIS |
|---|
| 15 | |
|---|
| 16 | cd $MT_DIR |
|---|
| 17 | svn update |
|---|
| 18 | perl build/exportmt.pl --help |
|---|
| 19 | perl build/exportmt.pl --debug |
|---|
| 20 | # --alpha=1 |
|---|
| 21 | # --beta=42 |
|---|
| 22 | # --local |
|---|
| 23 | # --make |
|---|
| 24 | # --plugin=Foo --plugin=Bar |
|---|
| 25 | # --prod |
|---|
| 26 | # --qa |
|---|
| 27 | # --stage |
|---|
| 28 | |
|---|
| 29 | =head1 DESCRIPTION |
|---|
| 30 | |
|---|
| 31 | A C<Build> object contains the internal routines needed to build |
|---|
| 32 | Movable Type distributions in multiple languages. |
|---|
| 33 | |
|---|
| 34 | =cut |
|---|
| 35 | |
|---|
| 36 | use strict; |
|---|
| 37 | use warnings; |
|---|
| 38 | use Archive::Tar; |
|---|
| 39 | use Cwd; |
|---|
| 40 | use File::Basename; |
|---|
| 41 | use File::Copy; |
|---|
| 42 | use File::Path; |
|---|
| 43 | use File::Spec; |
|---|
| 44 | use Getopt::Long; |
|---|
| 45 | use IO::File; |
|---|
| 46 | use LWP::UserAgent; |
|---|
| 47 | use Net::SMTP; |
|---|
| 48 | use Sys::Hostname; |
|---|
| 49 | |
|---|
| 50 | sub new { |
|---|
| 51 | my $class = shift; |
|---|
| 52 | my $self = {}; |
|---|
| 53 | bless $self, $class; |
|---|
| 54 | return $self; |
|---|
| 55 | } |
|---|
| 56 | |
|---|
| 57 | sub get_options { |
|---|
| 58 | my $self = shift; |
|---|
| 59 | my %o = ( |
|---|
| 60 | 'agent=s' => '', # Constructed at run-time. |
|---|
| 61 | 'alpha=s' => 0, # Alpha build number. |
|---|
| 62 | 'arch=s@' => undef, # Constructed below. |
|---|
| 63 | 'beta=s' => 0, # Beta build number. |
|---|
| 64 | 'rc=s' => 0, # Release candidate build number. |
|---|
| 65 | 'cleanup!' => 1, # Remove the exported directory after deployment. |
|---|
| 66 | 'date!' => 1, # Toggle date stamping. |
|---|
| 67 | 'debug' => 0, # Turn on/off the actual system calls. |
|---|
| 68 | 'deploy:s' => '', |
|---|
| 69 | 'deploy-uri=s' => '', |
|---|
| 70 | 'build!' => 1, # Build distribution files? |
|---|
| 71 | 'email-bcc:s' => undef, |
|---|
| 72 | 'email-body=s' => '', # Constructed at run-time. |
|---|
| 73 | 'email-cc:s' => undef, |
|---|
| 74 | 'email-from=s' => ( $ENV{USER} || $ENV{USERNAME} ), |
|---|
| 75 | 'email-host=s' => 'localhost', |
|---|
| 76 | 'email-subject=s' => '', # Constructed at run-time. |
|---|
| 77 | 'export!' => 1, # To export or not to export. That is the question. |
|---|
| 78 | 'export-dir=s' => '', # Constructed at run-time. |
|---|
| 79 | 'footer=s' => "<br/><b>SOFTWARE IS PROVIDED FOR TESTING ONLY - NOT FOR PRODUCTION USE.</b>\n", |
|---|
| 80 | 'footer-tmpl=s' => 'tmpl/cms/include/copyright.tmpl', |
|---|
| 81 | 'help|h' => 0, # Show the program usage. |
|---|
| 82 | 'license=s' => undef, |
|---|
| 83 | 'http-user=s' => undef, |
|---|
| 84 | 'http-pass=s' => undef, |
|---|
| 85 | 'ldap' => 0, # Use LDAP (and don't initialize the database). |
|---|
| 86 | 'lang=s' => $ENV{BUILD_LANGUAGE} || 'en_US', # de,es,en_US,fr,ja,nl |
|---|
| 87 | 'language=s@' => undef, # Constructed below. |
|---|
| 88 | 'local' => 0, # Command-line --option alias |
|---|
| 89 | 'make' => 0, # Command-line --option alias for simple legacy `make` |
|---|
| 90 | 'notify:s' => undef, # Send email notification on completion. |
|---|
| 91 | 'pack=s' => undef, # Constructed at run-time. |
|---|
| 92 | 'plugin=s@' => undef, # Plugin list |
|---|
| 93 | 'plugin-uri=s' => 'http://code.sixapart.com/svn/mtplugins/trunk', |
|---|
| 94 | 'prod' => 0, # Command-line --option alias |
|---|
| 95 | 'prod-dir=s' => 'Production_Builds', |
|---|
| 96 | 'qa' => 0, # Command-line --option alias |
|---|
| 97 | 'repo=s' => 'trunk', # Reset at runtime depending on branch,tag. |
|---|
| 98 | 'repo-uri=s' => '', |
|---|
| 99 | 'rev!' => 1, # Toggle revision stamping. |
|---|
| 100 | 'revision=s' => undef, # Constructed at run-time. |
|---|
| 101 | 'stage' => 0, # Command-line --option alias |
|---|
| 102 | 'stage-dir=s' => '', |
|---|
| 103 | 'stage-uri=s' => '', |
|---|
| 104 | 'short-lang=s' => '', # Constructed at run-time. |
|---|
| 105 | 'stamp=s' => $ENV{BUILD_VERSION_ID}, |
|---|
| 106 | 'symlink!' => 1, # Make build symlinks when staging. |
|---|
| 107 | 'verbose!' => 1, # Express (the default) or suppress run output. |
|---|
| 108 | @_, |
|---|
| 109 | ); |
|---|
| 110 | |
|---|
| 111 | # Map all literal string values to scalar references because |
|---|
| 112 | # Getopt::Long wants it that way. |
|---|
| 113 | while( my( $key, $val ) = each %o ) { |
|---|
| 114 | $o{$key} = \$val unless ref $val; |
|---|
| 115 | } |
|---|
| 116 | |
|---|
| 117 | GetOptions( %o ); # Get the command-line options. |
|---|
| 118 | |
|---|
| 119 | # "Un-map" the references so we don't have to say, ${$self->{'foo'}}. |
|---|
| 120 | while( my( $key, $val ) = each %o ) { |
|---|
| 121 | $self->{$key} = $$val |
|---|
| 122 | if ref($val) eq 'SCALAR' || ref($val) eq 'REF'; |
|---|
| 123 | } |
|---|
| 124 | |
|---|
| 125 | # Make sure we have an archive file type list. |
|---|
| 126 | $self->{'arch=s@'} ||= [qw( .tar.gz .zip )]; |
|---|
| 127 | # Make the plugins an empty list unless defined. |
|---|
| 128 | $self->{'plugin=s@'} ||= []; |
|---|
| 129 | # Construct the list of languages to build. |
|---|
| 130 | $self->{'lang=s'} = 'de,en_US,es,fr,ja,nl' |
|---|
| 131 | if lc( $self->{'lang=s'} ) eq 'all'; |
|---|
| 132 | push @{ $self->{'language=s@'} }, split /,/, $self->{'lang=s'}; |
|---|
| 133 | } |
|---|
| 134 | |
|---|
| 135 | sub setup { |
|---|
| 136 | my $self = shift; |
|---|
| 137 | my %args = @_; |
|---|
| 138 | |
|---|
| 139 | my $prereq = 'ExtUtils::Install 1.37_02'; |
|---|
| 140 | eval "use $prereq"; |
|---|
| 141 | die( "ERROR: Can't handle @{ $self->{'plugin=s@'} } plugin installation: $prereq needed." ) |
|---|
| 142 | if $@ && @{ $self->{'plugin=s@'} }; |
|---|
| 143 | |
|---|
| 144 | # Do we have SSL support? |
|---|
| 145 | $prereq = 'Crypt::SSLeay'; |
|---|
| 146 | eval { require $prereq }; |
|---|
| 147 | warn( "WARNING: $prereq not found. Can't use SSL.\n" ) if $@; |
|---|
| 148 | |
|---|
| 149 | # Replace the current language if given one as an argument. |
|---|
| 150 | $self->{'lang=s'} = $args{language} if $args{language}; |
|---|
| 151 | # Strip the dialect portion of the language code (ab_CD into ab). |
|---|
| 152 | ($self->{'short-lang=s'} = $self->{'lang=s'}) =~ s/([a-z]{2})_[A-Z]{2}$/$1/o; |
|---|
| 153 | |
|---|
| 154 | $self->{'pack=s'} ||= 'MT'; |
|---|
| 155 | $ENV{BUILD_PACKAGE} = $self->{'pack=s'}; |
|---|
| 156 | $ENV{BUILD_LANGUAGE} = $self->{'lang=s'}; |
|---|
| 157 | |
|---|
| 158 | # Handle option aliases. |
|---|
| 159 | if( $self->{'prod'} or $self->{'alpha=s'} or $self->{'beta=s'} or $self->{'rc=s'} ) { |
|---|
| 160 | $self->{'symlink!'} = 0; |
|---|
| 161 | } |
|---|
| 162 | if( $self->{'make'} ) { |
|---|
| 163 | $self->{'build!'} = 0; |
|---|
| 164 | $self->{'export!'} = 0; |
|---|
| 165 | } |
|---|
| 166 | if( $self->{'local'} ) { |
|---|
| 167 | $self->{'export!'} = 0; |
|---|
| 168 | } |
|---|
| 169 | if( $self->{'stage'} ) { |
|---|
| 170 | $self->{'deploy:s'} = $self->{'stage-dir=s'}; |
|---|
| 171 | } |
|---|
| 172 | |
|---|
| 173 | # Grab our repository revision. |
|---|
| 174 | $self->{'revision=s'} = repo_rev(); |
|---|
| 175 | # Figure out what repository to use. |
|---|
| 176 | $self->set_repo(); |
|---|
| 177 | |
|---|
| 178 | # Create the build-stamp if one is not already defined. |
|---|
| 179 | if( !$self->{'stamp=s'} || $args{language} ) { |
|---|
| 180 | # Read-in the configuration variables for substitution. |
|---|
| 181 | my $config = $self->read_conf( "build/mt-dists/default.mk", "build/mt-dists/$self->{'pack=s'}.mk" ); |
|---|
| 182 | $self->{'license=s'} ||= $config->{LICENSE}; |
|---|
| 183 | my @stamp = (); |
|---|
| 184 | if ($self->{'stamp=s'}) { |
|---|
| 185 | push @stamp, $self->{'stamp=s'}; |
|---|
| 186 | } else { |
|---|
| 187 | push @stamp, $config->{PRODUCT_VERSION} . ( |
|---|
| 188 | $self->{'alpha=s'} ? "a$self->{'alpha=s'}" |
|---|
| 189 | : $self->{'beta=s'} ? "b$self->{'beta=s'}" |
|---|
| 190 | : $self->{'rc=s'} ? "rc$self->{'rc=s'}" |
|---|
| 191 | : '' ); |
|---|
| 192 | } |
|---|
| 193 | # Add repo, date and ldap to the stamp if we are not production. |
|---|
| 194 | unless( $self->{'prod'} ) { |
|---|
| 195 | push @stamp, $self->{'short-lang=s'}; |
|---|
| 196 | if( $self->{'rev!'} ) { |
|---|
| 197 | push @stamp, lc( fileparse $self->{'repo=s'} ); |
|---|
| 198 | push @stamp, $self->{'revision=s'}; |
|---|
| 199 | } |
|---|
| 200 | push @stamp, sprintf( |
|---|
| 201 | '%04d%02d%02d', (localtime)[5]+1900, (localtime)[4]+1, (localtime)[3] |
|---|
| 202 | ) if $self->{'date!'}; |
|---|
| 203 | # Add -ldap to distingush them from Melody Nelson builds. |
|---|
| 204 | push @stamp, 'ldap' if $self->{'ldap'}; |
|---|
| 205 | } |
|---|
| 206 | $self->{'stamp=s'} = join '-', @stamp; |
|---|
| 207 | die( "ERROR: No stamp created. Cannot proceed.\n" ) |
|---|
| 208 | unless $self->{'stamp=s'}; |
|---|
| 209 | } |
|---|
| 210 | |
|---|
| 211 | # Set the BUILD_VERSION_ID, which has not been defined until now. |
|---|
| 212 | $ENV{BUILD_VERSION_ID} ||= $self->{'stamp=s'}; |
|---|
| 213 | |
|---|
| 214 | # Set the full name to use for the distribution (e.g. MT-3.3b1-fr-r12345-20061225). |
|---|
| 215 | $self->{'export-dir=s'} = "$self->{'pack=s'}-$self->{'stamp=s'}"; |
|---|
| 216 | # Name the exported directory (and archive) with the language. |
|---|
| 217 | $self->{'export-dir=s'} .= "-$self->{'short-lang=s'}" if $self->{'prod'}; |
|---|
| 218 | } |
|---|
| 219 | |
|---|
| 220 | sub make { |
|---|
| 221 | my $self = shift; |
|---|
| 222 | $self->verbose( 'Entered make()' ); |
|---|
| 223 | return if $self->{'debug'}; |
|---|
| 224 | |
|---|
| 225 | if( !$self->{'debug'} && $self->{'export!'} ) { |
|---|
| 226 | chdir( $self->{'export-dir=s'} ) or |
|---|
| 227 | die( "ERROR: Can't cd to $self->{'export-dir=s'}: $!" ); |
|---|
| 228 | $self->verbose( "Change to the $self->{'export-dir=s'} directory" ); |
|---|
| 229 | } |
|---|
| 230 | |
|---|
| 231 | if( $self->{'build!'} ) { |
|---|
| 232 | $self->verbose_command( sprintf( |
|---|
| 233 | '%s build/mt-dists/make-dists --package=%s --language=%s --stamp=%s %s --license=%s', |
|---|
| 234 | $^X, |
|---|
| 235 | $self->{'pack=s'}, |
|---|
| 236 | $self->{'lang=s'}, |
|---|
| 237 | $self->{'export-dir=s'}, |
|---|
| 238 | ($self->{'verbose!'} ? '--silent' : ''), |
|---|
| 239 | $self->{'license=s'} || '', |
|---|
| 240 | )); |
|---|
| 241 | } |
|---|
| 242 | else { |
|---|
| 243 | $self->verbose_command( 'make' ); |
|---|
| 244 | } |
|---|
| 245 | |
|---|
| 246 | if( !$self->{'debug'} && $self->{'export!'} ) { |
|---|
| 247 | chdir( '..' ) or die( "ERROR: Can't cd ..: $!" ); |
|---|
| 248 | $self->verbose( 'Change back to the parent directory' ); |
|---|
| 249 | } |
|---|
| 250 | } |
|---|
| 251 | |
|---|
| 252 | sub cleanup { |
|---|
| 253 | my $self = shift; |
|---|
| 254 | $self->verbose( 'Entered cleanup()' ); |
|---|
| 255 | return unless $self->{'cleanup!'}; |
|---|
| 256 | |
|---|
| 257 | my $build = $self->{'export-dir=s'}; # Less ugly. |
|---|
| 258 | if( !$self->{'debug'} && $self->{'export!'} ) { |
|---|
| 259 | # Move the build archives out of the soon-to-be-removed build directory. |
|---|
| 260 | for my $arch ( @{ $self->{'arch=s@'} } ) { |
|---|
| 261 | move( "$build/$build$arch", "$build$arch" ) |
|---|
| 262 | or die( "ERROR: Can't move $build/$build$arch: $!" ); |
|---|
| 263 | } |
|---|
| 264 | |
|---|
| 265 | rmtree( $build ) or die( "ERROR: Can't rmtree clean-up $build: $!" ); |
|---|
| 266 | } |
|---|
| 267 | $self->verbose( "Cleanup: Remove $build" ); |
|---|
| 268 | } |
|---|
| 269 | |
|---|
| 270 | sub create_distro_list { |
|---|
| 271 | my $self = shift; |
|---|
| 272 | |
|---|
| 273 | my $distros = { path => [], url => [] }; |
|---|
| 274 | |
|---|
| 275 | my %seen = (); |
|---|
| 276 | |
|---|
| 277 | for my $lang ( split( /\s*,\s*/, $self->{'lang=s'} ) ) { |
|---|
| 278 | for my $arch ( @{ $self->{'arch=s@'} } ) { |
|---|
| 279 | # The filename is the distribution name plus the archive extension. |
|---|
| 280 | my $filename = $self->{'export-dir=s'} . $arch; |
|---|
| 281 | |
|---|
| 282 | # The distribution is the full export path and filename. |
|---|
| 283 | my $dist = File::Spec->catdir( $self->{'export-dir=s'}, $filename ); |
|---|
| 284 | |
|---|
| 285 | # Create lists of the distribution paths. |
|---|
| 286 | push @{ $distros->{path} }, $dist; |
|---|
| 287 | |
|---|
| 288 | # Add to the URL list depending on where we are deploying. |
|---|
| 289 | if( $self->{'stage'} ) { |
|---|
| 290 | # Magically use the internal production folder if it exists. |
|---|
| 291 | my $loc = $self->{'prod'} && $self->{'prod-dir=s'} && $dist =~ /$self->{'prod-dir=s'}/ |
|---|
| 292 | ? sprintf( "%s/%s/%s/mt.cgi", |
|---|
| 293 | $self->{'stage-uri=s'}, $self->{'prod-dir=s'}, $self->{'export-dir=s'} ) |
|---|
| 294 | : sprintf( "%s/%s/mt.cgi", $self->{'stage-uri=s'}, $self->{'export-dir=s'} ); |
|---|
| 295 | push @{ $distros->{url} }, $loc unless $seen{$loc}++; |
|---|
| 296 | } |
|---|
| 297 | elsif( $self->{'deploy:s'} =~ /:/ ) { |
|---|
| 298 | my $loc = sprintf '%s/%s', $self->{'deploy-uri=s'}, $filename; |
|---|
| 299 | push @{ $distros->{url} }, $loc unless $seen{$loc}++; |
|---|
| 300 | } |
|---|
| 301 | } |
|---|
| 302 | } |
|---|
| 303 | |
|---|
| 304 | return $distros; |
|---|
| 305 | } |
|---|
| 306 | |
|---|
| 307 | sub deploy_distros { |
|---|
| 308 | my $self = shift; |
|---|
| 309 | my $distros = shift; |
|---|
| 310 | |
|---|
| 311 | return unless $self->{'deploy:s'}; |
|---|
| 312 | |
|---|
| 313 | # If a colon is in the deployment string, use scp. |
|---|
| 314 | if( $self->{'deploy:s'} =~ /:/ ) { |
|---|
| 315 | $self->verbose_command( sprintf( '%s %s %s', |
|---|
| 316 | 'scp', |
|---|
| 317 | join(' ', @{ $distros->{path} }), |
|---|
| 318 | $self->{'deploy:s'} |
|---|
| 319 | ) ); |
|---|
| 320 | } |
|---|
| 321 | # Otherwise, copy the distribution file(s) to the destination. |
|---|
| 322 | else { |
|---|
| 323 | for my $dist ( @{ $distros->{path} } ) { |
|---|
| 324 | my $dest = ''; |
|---|
| 325 | |
|---|
| 326 | # Magically use the internal production folder if it exists. |
|---|
| 327 | if( $self->{'prod'} && $self->{'stage'} && $self->{'prod-dir=s'} && |
|---|
| 328 | -e File::Spec->catdir( $self->{'stage-dir=s'}, $self->{'prod-dir=s'} ) |
|---|
| 329 | ) { |
|---|
| 330 | $dest = File::Spec->catdir( |
|---|
| 331 | $self->{'deploy:s'}, |
|---|
| 332 | $self->{'prod-dir=s'}, |
|---|
| 333 | scalar fileparse( $dist ), |
|---|
| 334 | ); |
|---|
| 335 | } |
|---|
| 336 | else { |
|---|
| 337 | $dest = File::Spec->catdir( |
|---|
| 338 | $self->{'deploy:s'}, |
|---|
| 339 | scalar fileparse( $dist ), |
|---|
| 340 | ); |
|---|
| 341 | } |
|---|
| 342 | |
|---|
| 343 | copy( $dist, $dest ) or die( "ERROR: Can't copy $dist to $dest: $!" ) |
|---|
| 344 | unless $self->{'debug'}; |
|---|
| 345 | $self->verbose( "Copy $dist to $dest" ); |
|---|
| 346 | |
|---|
| 347 | # Install the build if we are staging. |
|---|
| 348 | $self->stage_distro( $dest ) if $self->{'stage'}; |
|---|
| 349 | |
|---|
| 350 | # Update the build summary page. |
|---|
| 351 | $self->update_html( $dest ); |
|---|
| 352 | |
|---|
| 353 | } |
|---|
| 354 | } |
|---|
| 355 | |
|---|
| 356 | # Make sure the deployed distros actually made it. |
|---|
| 357 | unless( $self->{'debug'} ) { |
|---|
| 358 | for( @{ $distros->{url} } ) { |
|---|
| 359 | die( "ERROR: $_ can't be resolved." ) |
|---|
| 360 | unless $self->{'agent=s'}->head( $_ ); |
|---|
| 361 | } |
|---|
| 362 | } |
|---|
| 363 | } |
|---|
| 364 | |
|---|
| 365 | sub stage_distro { |
|---|
| 366 | my $self = shift; |
|---|
| 367 | my $dest = shift; |
|---|
| 368 | |
|---|
| 369 | # We only stage tar.gz's. |
|---|
| 370 | return if $dest !~ /\.gz$/o; |
|---|
| 371 | |
|---|
| 372 | die( "ERROR: Cannot stage '$dest': No such file or directory" ) |
|---|
| 373 | unless $self->{'debug'} || -e $dest; |
|---|
| 374 | |
|---|
| 375 | my $cwd = cwd(); |
|---|
| 376 | |
|---|
| 377 | my $prod = $self->{'prod'} && $self->{'stage'} && $self->{'prod-dir=s'} && -e File::Spec->catdir( $self->{'stage-dir=s'}, $self->{'prod-dir=s'} ); |
|---|
| 378 | |
|---|
| 379 | # Add the prod-dir to the staged directory if appropriate. |
|---|
| 380 | my $stage_root = $self->{'stage-dir=s'}; |
|---|
| 381 | $stage_root = File::Spec->catdir( $self->{'stage-dir=s'}, $self->{'prod-dir=s'} ) |
|---|
| 382 | if $prod; |
|---|
| 383 | |
|---|
| 384 | chdir $stage_root or |
|---|
| 385 | die( "ERROR: Can't chdir to $stage_root $!" ); |
|---|
| 386 | $self->verbose( "Change to staging root $stage_root" ); |
|---|
| 387 | |
|---|
| 388 | # Do we have a current symlink? |
|---|
| 389 | my $link = lc( fileparse $self->{'repo=s'} ); |
|---|
| 390 | $link .= "-$self->{'short-lang=s'}"; |
|---|
| 391 | $link .= '-ldap' if $self->{'ldap'}; |
|---|
| 392 | my $current = ''; |
|---|
| 393 | $current = readlink( $link ) if $self->{'symlink!'} and -e $link; |
|---|
| 394 | # Remove any trailing slash. |
|---|
| 395 | $current =~ s/\/$//; |
|---|
| 396 | # Database named the same as the distribution (but with _'s). |
|---|
| 397 | (my $current_db = $current) =~ s/[.-]/_/g; |
|---|
| 398 | $current_db = 'stage_' . $current_db; |
|---|
| 399 | |
|---|
| 400 | # Set the stage_dir to the literal build directory name. |
|---|
| 401 | my $stage_dir = fileparse( $dest, @{ $self->{'arch=s@'} } ); |
|---|
| 402 | # Reset the staging root directory. |
|---|
| 403 | $stage_root = File::Spec->catdir( $stage_root, $stage_dir ); |
|---|
| 404 | |
|---|
| 405 | # Remove any existing distro, with the same path name. |
|---|
| 406 | if( -d $stage_root ) { |
|---|
| 407 | rmtree( $stage_root ) or |
|---|
| 408 | die( "ERROR: Can't rmtree the old $stage_root $!" ) |
|---|
| 409 | unless $self->{'debug'}; |
|---|
| 410 | $self->verbose( "Remove: $stage_root" ); |
|---|
| 411 | } |
|---|
| 412 | |
|---|
| 413 | # Drop previous. |
|---|
| 414 | if( -d $current ) { |
|---|
| 415 | rmtree( $current ) or |
|---|
| 416 | warn( "WARNING: Can't rmtree previous '$current': $!" ) |
|---|
| 417 | unless $self->{'debug'}; |
|---|
| 418 | for my $arch ( @{ $self->{'arch=s@'} } ) { |
|---|
| 419 | unlink( "$current$arch" ) or |
|---|
| 420 | warn( "WARNING: Can't unlink '$current$arch': $!\n" ) |
|---|
| 421 | unless $self->{'debug'} or ("$current$arch" eq $dest); |
|---|
| 422 | } |
|---|
| 423 | } |
|---|
| 424 | |
|---|
| 425 | # Un-tar the distribution. |
|---|
| 426 | my $tar; |
|---|
| 427 | unless( $self->{'debug'} ) { |
|---|
| 428 | $self->verbose( "Extract: $dest..." ); |
|---|
| 429 | # Temporarily switching to using tar utility for this |
|---|
| 430 | # since Archive::Tar is croaking on one of our files. |
|---|
| 431 | `tar xf $dest`; |
|---|
| 432 | # $tar = Archive::Tar->new( $dest ); |
|---|
| 433 | # $tar->extract(); |
|---|
| 434 | } |
|---|
| 435 | $self->verbose( "Extract: $dest" ); |
|---|
| 436 | |
|---|
| 437 | # Change to the distribution directory. |
|---|
| 438 | chdir( $stage_root ) or die( "ERROR: Can't chdir $stage_root $!" ) |
|---|
| 439 | unless $self->{'debug'}; |
|---|
| 440 | $self->verbose( "Change to $stage_root" ); |
|---|
| 441 | |
|---|
| 442 | # Make sure there is a user-style so we don't barf unneccessarily into the error_log. |
|---|
| 443 | open STYLE, "> user_styles.css" or |
|---|
| 444 | die( "ERROR: Can't touch user_styles.css $@" ); |
|---|
| 445 | close STYLE; |
|---|
| 446 | |
|---|
| 447 | # Our database is named the same as the distribution (but with _'s) except for LDAP. |
|---|
| 448 | (my $db = $stage_dir) =~ s/[.-]/_/g; |
|---|
| 449 | # Reset the db to have the same name, if we are LDAP. |
|---|
| 450 | $db = 'ldap' if $self->{'ldap'}; |
|---|
| 451 | # Append the handy staging build flag. |
|---|
| 452 | $db = 'stage_' . $db; |
|---|
| 453 | |
|---|
| 454 | # Set the staging URL to a real location now. |
|---|
| 455 | my $url = sprintf '%s/%s/', $self->{'stage-uri=s'}, |
|---|
| 456 | ($prod |
|---|
| 457 | ? File::Spec->catdir( $self->{'prod-dir=s'}, $stage_dir ) |
|---|
| 458 | : $self->{'symlink!'} |
|---|
| 459 | ? $link |
|---|
| 460 | : $stage_dir |
|---|
| 461 | ); |
|---|
| 462 | |
|---|
| 463 | # Give unto us a shiny, new config file. |
|---|
| 464 | my $config = 'mt-config.cgi'; |
|---|
| 465 | unless( $self->{'debug'} ) { |
|---|
| 466 | my $fh = IO::File->new( ">$config" ); |
|---|
| 467 | print $fh <<CONFIG; |
|---|
| 468 | CGIPath $url |
|---|
| 469 | # DefaultSiteURL http://example.com/blogs/ |
|---|
| 470 | # DefaultSiteRoot /var/www/html/blogs/ |
|---|
| 471 | Database $db |
|---|
| 472 | ObjectDriver DBI::mysql |
|---|
| 473 | DBUser root |
|---|
| 474 | DebugMode 2 |
|---|
| 475 | CONFIG |
|---|
| 476 | if( $self->{'ldap'} ) { |
|---|
| 477 | print $fh <<CONFIG; |
|---|
| 478 | AuthenticationModule LDAP |
|---|
| 479 | # AuthLDAPURL ldap://ldap.example.com/dc=example,dc=com |
|---|
| 480 | CONFIG |
|---|
| 481 | } |
|---|
| 482 | |
|---|
| 483 | $fh->close(); |
|---|
| 484 | } |
|---|
| 485 | $self->verbose( "Write configuration to $config" ); |
|---|
| 486 | |
|---|
| 487 | # Create and initialize a new database. |
|---|
| 488 | unless( $self->{'ldap'} ) { |
|---|
| 489 | # Set up the database for this distribution. |
|---|
| 490 | $self->verbose( 'Initialize database.' ); |
|---|
| 491 | # XXX Use DBI ASAP. |
|---|
| 492 | # Drop the previous database. |
|---|
| 493 | $self->verbose_command( "mysqladmin -f -u root drop $current_db" ) |
|---|
| 494 | if $current; |
|---|
| 495 | # Drop a database of same name. |
|---|
| 496 | if( $db ) { |
|---|
| 497 | $self->verbose_command( "mysqladmin -f -u root drop $db" ); |
|---|
| 498 | $self->verbose_command( "mysqladmin -u root create $db" ); |
|---|
| 499 | # Run the upgrade tool. |
|---|
| 500 | $self->verbose_command( "$^X ./tools/upgrade --name Melody" ); |
|---|
| 501 | } |
|---|
| 502 | else { |
|---|
| 503 | die "ERROR: No database to stage - very odd."; |
|---|
| 504 | } |
|---|
| 505 | } |
|---|
| 506 | |
|---|
| 507 | # Change to the parent of the new stage directory. |
|---|
| 508 | chdir( '..' ) or die( "ERROR: Can't chdir to .." ) |
|---|
| 509 | unless $self->{'debug'}; |
|---|
| 510 | $self->verbose( 'Change back to staging root' ); |
|---|
| 511 | |
|---|
| 512 | # Now we re-link the stamped directory. |
|---|
| 513 | if( $self->{'symlink!'} ) { |
|---|
| 514 | unless( $self->{'debug'} ) { |
|---|
| 515 | print "Unlink $link\n"; |
|---|
| 516 | # Drop current symlink. |
|---|
| 517 | unlink( $link ) or warn( "WARNING: Can't unlink '$link': $!" ); |
|---|
| 518 | # Relink the staged directory. |
|---|
| 519 | symlink( "$stage_dir/", $link ) or |
|---|
| 520 | warn( "WARNING: Can't symlink $stage_dir/ to $link: $!" ); |
|---|
| 521 | } |
|---|
| 522 | $self->verbose( "Symlink: $stage_dir/ to $link" ); |
|---|
| 523 | } |
|---|
| 524 | |
|---|
| 525 | unless( $self->{'debug'} or $self->{'symlink!'} ) { |
|---|
| 526 | # Make sure we can get to our symlink. |
|---|
| 527 | $url = sprintf "%s/%s/mt.cgi", |
|---|
| 528 | $self->{'stage-uri=s'}, $link; |
|---|
| 529 | die( "ERROR: Staging $url can't be resolved." ) |
|---|
| 530 | unless $self->{'agent=s'}->head( $url ); |
|---|
| 531 | # Make sure we can get to our archive file symlinks. |
|---|
| 532 | for my $arch ( @{ $self->{'arch=s@'} } ) { |
|---|
| 533 | $url = sprintf '%s/%s%s', |
|---|
| 534 | $self->{'stage-uri=s'}, $stage_dir, $arch; |
|---|
| 535 | die( "ERROR: Staging $url can't be resolved." ) |
|---|
| 536 | unless $self->{'agent=s'}->head( $url ); |
|---|
| 537 | } |
|---|
| 538 | } |
|---|
| 539 | |
|---|
| 540 | chdir( $cwd ) or die( "ERROR: Can't chdir back to $cwd: $!" ); |
|---|
| 541 | } |
|---|
| 542 | |
|---|
| 543 | sub update_html { |
|---|
| 544 | my $self = shift; |
|---|
| 545 | $self->verbose( 'Entered update_html()' ); |
|---|
| 546 | my $dest = shift; |
|---|
| 547 | |
|---|
| 548 | if( $self->{'symlink!'} && |
|---|
| 549 | !$self->{'prod'} && |
|---|
| 550 | !$self->{'ldap'} && |
|---|
| 551 | ($self->{'stage'} || ($self->{'deploy:s'} eq $self->{'stage-dir=s'})) |
|---|
| 552 | ) { |
|---|
| 553 | my( $stage_dir, $suffix ); |
|---|
| 554 | ($stage_dir, undef, $suffix) = fileparse( $dest, @{ $self->{'arch=s@'} } ); |
|---|
| 555 | my $lang = $self->{'short-lang=s'}; |
|---|
| 556 | my $branch = $self->{'repo=s'}; |
|---|
| 557 | $branch =~ s!^(branches|tags)/!!; |
|---|
| 558 | my $revision = $self->{'revision=s'}; |
|---|
| 559 | $revision =~ s!^r!!; |
|---|
| 560 | |
|---|
| 561 | my $old_html = File::Spec->catdir( $self->{'stage-dir=s'}, 'build.html' ); |
|---|
| 562 | |
|---|
| 563 | unless( -e $old_html ) { |
|---|
| 564 | warn "WARNING: Staging HTML file, $old_html, does not exist.\n"; |
|---|
| 565 | return; |
|---|
| 566 | } |
|---|
| 567 | unless( -e $stage_dir ) { |
|---|
| 568 | warn "WARNING: Distribution file, $dest, does not exist.\n"; |
|---|
| 569 | return; |
|---|
| 570 | } |
|---|
| 571 | |
|---|
| 572 | my $id = lc( fileparse $self->{'repo=s'} ) . "-$lang$suffix"; |
|---|
| 573 | $self->verbose( "Update: $old_html with $id for $dest" ); |
|---|
| 574 | |
|---|
| 575 | my %set; |
|---|
| 576 | unless( $self->{'debug'} ) { |
|---|
| 577 | warn "WARNING: $old_html does not exist" unless -e $old_html; |
|---|
| 578 | my $new_html = "$old_html.new"; |
|---|
| 579 | my $old_fh = IO::File->new( '< ' . $old_html ); |
|---|
| 580 | my $new_fh = IO::File->new( '> ' . $new_html ); |
|---|
| 581 | |
|---|
| 582 | while( my $line = <$old_fh> ) { |
|---|
| 583 | # build replacement |
|---|
| 584 | if( $line =~ m!^(\s*).*?/\*\s*(build|branch):\s*(\S+?)\s*\*/! ) { |
|---|
| 585 | my $spacer = $1; |
|---|
| 586 | my $type = $2; |
|---|
| 587 | my $val = $3; |
|---|
| 588 | if ($type eq 'build') { |
|---|
| 589 | if ($val eq $id) { |
|---|
| 590 | $self->verbose( "Matched $type: id=$id" ); |
|---|
| 591 | $set{$type} = 1; |
|---|
| 592 | $line = sprintf qq|$spacer'$id': '%s/%s%s', /* build: $id */\n|, |
|---|
| 593 | $self->{'stage-uri=s'}, |
|---|
| 594 | $stage_dir, $suffix; |
|---|
| 595 | } |
|---|
| 596 | } elsif ($branch && ($type eq 'branch')) { |
|---|
| 597 | if ($val eq $branch) { |
|---|
| 598 | $self->verbose( "Matched $type: branch=$branch" ); |
|---|
| 599 | $set{$type} = 1; |
|---|
| 600 | $line = sprintf qq|$spacer'$branch': '$revision', /* branch: $branch */\n|; |
|---|
| 601 | } |
|---|
| 602 | } |
|---|
| 603 | } |
|---|
| 604 | if ($line =~ m!^(\s*)/\*\s*new-(build|branch)\s*\*/!) { |
|---|
| 605 | # create a new release |
|---|
| 606 | my $spacer = $1; |
|---|
| 607 | my $type = $2; |
|---|
| 608 | unless ($set{$type}) { |
|---|
| 609 | if ($type eq 'build') { |
|---|
| 610 | $set{$type} = 1; |
|---|
| 611 | $self->verbose( "Writing new build: id=$id" ); |
|---|
| 612 | my $new_line = sprintf qq|$spacer'$id': '%s/%s%s', /* build: $id */\n|, |
|---|
| 613 | $self->{'stage-uri=s'}, |
|---|
| 614 | $stage_dir, $suffix; |
|---|
| 615 | $line = $new_line . $line; |
|---|
| 616 | } elsif ($branch && ($type eq 'branch')) { |
|---|
| 617 | $set{$type} = 1; |
|---|
| 618 | $self->verbose( "Writing new branch: branch=$branch" ); |
|---|
| 619 | my $new_line = sprintf qq|$spacer'$branch': '$revision', /* branch: $branch */\n|; |
|---|
| 620 | $line = $new_line . $line; |
|---|
| 621 | } |
|---|
| 622 | } |
|---|
| 623 | } |
|---|
| 624 | |
|---|
| 625 | print $new_fh $line; |
|---|
| 626 | } |
|---|
| 627 | |
|---|
| 628 | $old_fh->close; |
|---|
| 629 | $new_fh->close; |
|---|
| 630 | move( $new_html, $old_html ) or |
|---|
| 631 | die( "ERROR: Can't move $new_html, $old_html: $!" ); |
|---|
| 632 | $self->verbose( "Move: $new_html to $old_html" ); |
|---|
| 633 | } |
|---|
| 634 | } |
|---|
| 635 | } |
|---|
| 636 | |
|---|
| 637 | sub remove_copy { |
|---|
| 638 | my $self = shift; |
|---|
| 639 | if( -d $self->{'export-dir=s'} ) { |
|---|
| 640 | $self->verbose( "Remove existing export: $self->{'export-dir=s'}" ); |
|---|
| 641 | rmtree( $self->{'export-dir=s'} ) or |
|---|
| 642 | die( "ERROR: Can't rmtree existing export $self->{'export-dir=s'}: $!" ) |
|---|
| 643 | unless $self->{'debug'}; |
|---|
| 644 | } |
|---|
| 645 | } |
|---|
| 646 | |
|---|
| 647 | sub repo_rev { |
|---|
| 648 | my $revision = qx{ svn info | grep 'Last Changed Rev' }; |
|---|
| 649 | chomp $revision; |
|---|
| 650 | $revision =~ s/^Last Changed Rev: (\d+)$/r$1/o; |
|---|
| 651 | die( "ERROR: $revision" ) if $revision =~ /is not a working copy/; |
|---|
| 652 | return $revision; |
|---|
| 653 | } |
|---|
| 654 | |
|---|
| 655 | sub set_repo { |
|---|
| 656 | my $self = shift; |
|---|
| 657 | |
|---|
| 658 | # Grab our repository from the environment. |
|---|
| 659 | $self->{'repo-uri=s'} = qx{ svn info | grep URL }; |
|---|
| 660 | chomp $self->{'repo-uri=s'}; |
|---|
| 661 | $self->{'repo-uri=s'} =~ s/^URL: (.+)$/$1/o; |
|---|
| 662 | |
|---|
| 663 | if( $self->{'repo-uri=s'} =~ /http.+?(branches|tags)\/([0-9A-Za-z_.-]+)/ ) { |
|---|
| 664 | # The repo is embedded in the repo uri. |
|---|
| 665 | my( $key, $val ) = ( $1, $2 ); |
|---|
| 666 | $self->{'repo=s'} = join '/', $key, $val; |
|---|
| 667 | } |
|---|
| 668 | |
|---|
| 669 | # Make sure that the repository actually exists. |
|---|
| 670 | if( !$self->{'debug'} && $self->{'export!'} ) { |
|---|
| 671 | $self->{'agent=s'} = LWP::UserAgent->new; |
|---|
| 672 | my $request = HTTP::Request->new( HEAD => $self->{'repo-uri=s'} ); |
|---|
| 673 | $request->authorization_basic( $self->{'http-user=s'}, $self->{'http-pass=s'} ) |
|---|
| 674 | if $self->{'http-user=s'} && $self->{'http-pass=s'}; |
|---|
| 675 | my $response = $self->{'agent=s'}->request( $request ); |
|---|
| 676 | die( "ERROR: The repoository '$self->{'repo-uri=s'}' can't be resolved." ) |
|---|
| 677 | unless $response->is_success; |
|---|
| 678 | } |
|---|
| 679 | } |
|---|
| 680 | |
|---|
| 681 | sub export { |
|---|
| 682 | my $self = shift; |
|---|
| 683 | return unless $self->{'export!'}; |
|---|
| 684 | # NOTE Subversion auto-creates the export directory. |
|---|
| 685 | $self->verbose_command( sprintf( 'svn export --quiet %s %s', |
|---|
| 686 | $self->{'repo-uri=s'}, $self->{'export-dir=s'} |
|---|
| 687 | )); |
|---|
| 688 | } |
|---|
| 689 | |
|---|
| 690 | sub plugin_export { |
|---|
| 691 | my $self = shift; |
|---|
| 692 | return unless $self->{'plugin=s@'}; |
|---|
| 693 | |
|---|
| 694 | # Change to the export directory, if we are exporting. |
|---|
| 695 | chdir( $self->{'export-dir=s'} ) or |
|---|
| 696 | die( "ERROR: Can't cd to $self->{'export-dir=s'}: $!" ) |
|---|
| 697 | if !$self->{debug} && $self->{'export!'}; |
|---|
| 698 | |
|---|
| 699 | # Export the plugins. |
|---|
| 700 | for my $plugin ( @{ $self->{'plugin=s@'} } ) { |
|---|
| 701 | my $uri = "$self->{'plugin-uri=s'}/$plugin"; |
|---|
| 702 | my $path = "plugins/$plugin"; |
|---|
| 703 | $self->verbose_command( |
|---|
| 704 | sprintf( 'svn export --quiet %s %s', $uri, $path ) |
|---|
| 705 | ); |
|---|
| 706 | die "ERROR: Plugin not exported: $uri" |
|---|
| 707 | unless $self->{debug} || -d $path; |
|---|
| 708 | |
|---|
| 709 | # Handle the plugin subdirectory. |
|---|
| 710 | $path = 'plugins'; |
|---|
| 711 | my $subdir = "plugins/$plugin/$path"; |
|---|
| 712 | if( -d $subdir && !$self->{debug} ) { |
|---|
| 713 | $self->dirmove( $subdir, $path ) or |
|---|
| 714 | die( "Can't move $subdir to $path: $!" ); |
|---|
| 715 | $self->verbose( "Moved $subdir to $path" ); |
|---|
| 716 | } |
|---|
| 717 | # Handle the mt-static subdirectory. |
|---|
| 718 | $path = "mt-static/plugins"; |
|---|
| 719 | $subdir = "plugins/$plugin/$path"; |
|---|
| 720 | if( -d $subdir && !$self->{debug} ) { |
|---|
| 721 | $self->dirmove( $subdir, $path ) or |
|---|
| 722 | die( "Can't move directory $subdir to $path $!" ); |
|---|
| 723 | $self->verbose( "Moved $subdir to $path" ); |
|---|
| 724 | } |
|---|
| 725 | |
|---|
| 726 | $path = "plugins/$plugin"; |
|---|
| 727 | rmtree( $path ) or die( "Can't rmtree() $path: $!" ) |
|---|
| 728 | unless $self->{debug}; |
|---|
| 729 | $self->verbose( "Removed $path" ); |
|---|
| 730 | } |
|---|
| 731 | |
|---|
| 732 | chdir( '..' ) or die( "ERROR: Can't cd ..: $!" ) |
|---|
| 733 | if !$self->{debug} && $self->{'export!'}; |
|---|
| 734 | } |
|---|
| 735 | |
|---|
| 736 | sub dirmove { |
|---|
| 737 | my $self = shift; |
|---|
| 738 | my @paths = @_; |
|---|
| 739 | my $dest = pop @paths; |
|---|
| 740 | for my $path ( @paths ) { |
|---|
| 741 | # $self->verbose( "Moving $path to $dest..." ); |
|---|
| 742 | eval{ install({ $path => $dest }) }; |
|---|
| 743 | } |
|---|
| 744 | return 1; |
|---|
| 745 | } |
|---|
| 746 | |
|---|
| 747 | sub verbose_command { |
|---|
| 748 | my $self = shift; |
|---|
| 749 | my $command = shift; |
|---|
| 750 | $self->verbose( "Execute: $command" ); |
|---|
| 751 | system $command unless $self->{'debug'}; |
|---|
| 752 | |
|---|
| 753 | if( $? == -1 ) { |
|---|
| 754 | die( "ERROR: Failed to execute: $!" ); |
|---|
| 755 | } |
|---|
| 756 | elsif( $? & 127 ) { |
|---|
| 757 | die sprintf( "ERROR: Child died with signal %d, with%s coredump\n", |
|---|
| 758 | ( $? & 127 ), ( $? & 128 ? '' : 'out' ) |
|---|
| 759 | ); |
|---|
| 760 | } |
|---|
| 761 | else { |
|---|
| 762 | # printf "Child exited with value %d\n", $? >> 8 if $self->{'verbose!'}; |
|---|
| 763 | } |
|---|
| 764 | |
|---|
| 765 | return $command; |
|---|
| 766 | } |
|---|
| 767 | |
|---|
| 768 | sub notify { |
|---|
| 769 | my $self = shift; |
|---|
| 770 | my $distros = shift; |
|---|
| 771 | |
|---|
| 772 | return unless $self->{'notify:s'}; |
|---|
| 773 | $self->verbose( 'Entered notify()' ); |
|---|
| 774 | return if $self->{'debug'}; |
|---|
| 775 | |
|---|
| 776 | $self->{'email-subject=s'} = sprintf '%s build: %s', |
|---|
| 777 | $self->{'pack=s'}, $self->{'stamp=s'}; |
|---|
| 778 | $self->{'email-subject=s'} .= |
|---|
| 779 | $self->{'alpha=i'} ? ' - Alpha ' . $self->{'alpha=i'} : |
|---|
| 780 | $self->{'beta=i'} ? ' - Beta ' . $self->{'beta=i'} : |
|---|
| 781 | $self->{'prod'} ? ' - Production' : |
|---|
| 782 | $self->{'stage'} ? ' - Staging' : |
|---|
| 783 | $self->{'qa'} ? ' - QA' : ''; |
|---|
| 784 | # If an email-cc exists, add a comma in front of the QA address. |
|---|
| 785 | # $self->{'email-cc:s'} .= ($self->{'email-cc:s'} ? ',' : '') |
|---|
| 786 | # if $self->{'qa'}; |
|---|
| 787 | # Show the deployed URL's. |
|---|
| 788 | $self->{'email-body=s'} = sprintf "File URL(s):\n%s\n\n", |
|---|
| 789 | join( "\n", @{ $distros->{url} } ) |
|---|
| 790 | if $self->{'deploy:s'}; |
|---|
| 791 | $self->{'email-body=s'} .= sprintf "Build file(s) located on %s\n%s", |
|---|
| 792 | hostname(), join( "\n", @{ $distros->{path} } ) |
|---|
| 793 | if $self->{'qa'} or !$self->{'cleanup!'}; |
|---|
| 794 | |
|---|
| 795 | my $smtp = Net::SMTP->new( |
|---|
| 796 | $self->{'email-host=s'}, |
|---|
| 797 | Debug => $self->{'debug'}, |
|---|
| 798 | ); |
|---|
| 799 | |
|---|
| 800 | $smtp->mail( $self->{'email-from=s'} ); |
|---|
| 801 | $smtp->to( $self->{'notify:s'} ); |
|---|
| 802 | $smtp->cc( $self->{'email-cc:s'} ) if $self->{'email-cc:s'}; |
|---|
| 803 | $smtp->bcc( $self->{'email-bcc:s'} ) if $self->{'email-bcc:s'}; |
|---|
| 804 | |
|---|
| 805 | $smtp->data(); |
|---|
| 806 | $smtp->datasend( "To: $self->{'notify:s'}\n" ); |
|---|
| 807 | $smtp->datasend( "Cc: $self->{'email-cc:s'}\n" ) if $self->{'email-cc:s'}; |
|---|
| 808 | $smtp->datasend( "Subject: $self->{'email-subject=s'}\n" ); |
|---|
| 809 | $smtp->datasend( "\n" ); |
|---|
| 810 | $smtp->datasend( "$self->{'email-body=s'}\n" ); |
|---|
| 811 | $smtp->dataend(); |
|---|
| 812 | |
|---|
| 813 | $smtp->quit; |
|---|
| 814 | |
|---|
| 815 | $self->verbose( "Email sent to $self->{'notify:s'}" ); |
|---|
| 816 | } |
|---|
| 817 | |
|---|
| 818 | sub read_conf { |
|---|
| 819 | my $self = shift; |
|---|
| 820 | my @files = @_; |
|---|
| 821 | my $config = {}; |
|---|
| 822 | |
|---|
| 823 | for my $file ( @files ) { |
|---|
| 824 | next unless -e $file; |
|---|
| 825 | print "Parse: config $file file...\n"; |
|---|
| 826 | my $fh = IO::File->new( '< ' . $file ); |
|---|
| 827 | |
|---|
| 828 | while( <$fh> ) { |
|---|
| 829 | # Skip comment lines. |
|---|
| 830 | next if /^\s*#/; |
|---|
| 831 | # Skip blank lines. |
|---|
| 832 | next if /^\s*$/; |
|---|
| 833 | # Capture a configuration pair. |
|---|
| 834 | /^\s*(.*?)\s*=\s*(.*)\s*$/ or next; |
|---|
| 835 | my( $k, $v ) = ( $1, $2 ); |
|---|
| 836 | $config->{$k} = $v; |
|---|
| 837 | } |
|---|
| 838 | |
|---|
| 839 | $fh->close; |
|---|
| 840 | } |
|---|
| 841 | |
|---|
| 842 | return $config; |
|---|
| 843 | } |
|---|
| 844 | |
|---|
| 845 | sub inject_footer { |
|---|
| 846 | my $self = shift; |
|---|
| 847 | |
|---|
| 848 | # Do not inject the non-production footer if we are running in |
|---|
| 849 | # debug mode, doing a (local) make or are building an alpha/beta |
|---|
| 850 | # version. |
|---|
| 851 | return if $self->{'debug'} || $self->{'make'} || |
|---|
| 852 | ($self->{'prod'} && !($self->{'beta=s'} || $self->{'alpha=s'} || $self->{'rc=s'})); |
|---|
| 853 | $self->verbose( 'Entered inject_footer()' ); |
|---|
| 854 | return if $self->{'prod'} || $self->{'debug'} || $self->{'make'}; |
|---|
| 855 | |
|---|
| 856 | my $file = $self->{'export!'} |
|---|
| 857 | ? File::Spec->catdir( $self->{'export-dir=s'}, $self->{'footer-tmpl=s'} ) |
|---|
| 858 | : $self->{'footer-tmpl=s'}; |
|---|
| 859 | die( "ERROR: File $file does not exist: $!" ) unless -e $file; |
|---|
| 860 | |
|---|
| 861 | # Slurp-in the contents of the file. |
|---|
| 862 | local $/; |
|---|
| 863 | my $fh = IO::File->new( $file ); |
|---|
| 864 | my $contents = <$fh>; |
|---|
| 865 | $fh->close(); |
|---|
| 866 | |
|---|
| 867 | return if $contents =~ m/\Q$self->{'footer=s'}\E/; |
|---|
| 868 | |
|---|
| 869 | $contents =~ s/Reserved.\n/Reserved.\n$self->{'footer=s'}/; |
|---|
| 870 | |
|---|
| 871 | # Rewrite the file with the injected footer. |
|---|
| 872 | $fh = IO::File->new( "> $file" ); |
|---|
| 873 | print $fh $contents; |
|---|
| 874 | $fh->close(); |
|---|
| 875 | } |
|---|
| 876 | |
|---|
| 877 | sub verbose { |
|---|
| 878 | my $self = shift; |
|---|
| 879 | return unless $self->{'verbose!'}; |
|---|
| 880 | print join( "\n", @_ ), "\n\n"; |
|---|
| 881 | } |
|---|
| 882 | |
|---|
| 883 | sub languages { return @{ shift->{'language=s@'} } } |
|---|
| 884 | |
|---|
| 885 | sub debug { return shift->{'debug'} } |
|---|
| 886 | |
|---|
| 887 | sub help { return shift->{'help|h'} } |
|---|
| 888 | |
|---|
| 889 | sub usage { |
|---|
| 890 | my $self = shift; |
|---|
| 891 | print <<'USAGE'; |
|---|
| 892 | |
|---|
| 893 | MT export build deployment notification automation. |
|---|
| 894 | Examples: |
|---|
| 895 | |
|---|
| 896 | cd $MT_DIR |
|---|
| 897 | svn update |
|---|
| 898 | perl build/exportmt.pl --help |
|---|
| 899 | perl build/exportmt.pl --debug |
|---|
| 900 | # --alpha=1 |
|---|
| 901 | # --beta=42 |
|---|
| 902 | # --local |
|---|
| 903 | # --make |
|---|
| 904 | # --plugin=Foo --plugin=Foo |
|---|
| 905 | # --prod |
|---|
| 906 | # --qa |
|---|
| 907 | # --stage |
|---|
| 908 | |
|---|
| 909 | USAGE |
|---|
| 910 | exit; |
|---|
| 911 | } |
|---|
| 912 | |
|---|
| 913 | 1; |
|---|
| 914 | __END__ |
|---|