nixenvironment 0.0.59 → 0.0.60

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/README.md +14 -14
  4. data/bin/Config +2 -2
  5. data/bin/nixenvironment +420 -842
  6. data/legacy/CleanWorkingCopy.sh +69 -0
  7. data/legacy/Deploy.sh +44 -0
  8. data/legacy/DeployAPK.py +58 -0
  9. data/legacy/DeployIPA.sh +125 -0
  10. data/legacy/DetectSCM.sh +11 -0
  11. data/legacy/GenerateCodeCoverageForXCTests.sh +134 -0
  12. data/legacy/GenerateCodeDuplicationReport.sh +24 -0
  13. data/legacy/IncrementBuildNumber.py +129 -0
  14. data/legacy/LoadBuildEnvVars.sh +116 -0
  15. data/legacy/MakeTag.sh +94 -0
  16. data/legacy/RemoveTemporaryFiles.sh +9 -0
  17. data/legacy/SaveRevision.sh +122 -0
  18. data/legacy/UnityBuildAndroid.py +84 -0
  19. data/legacy/UnityBuildAutomationScripts/CommandLineReader.cs +130 -0
  20. data/legacy/UnityBuildAutomationScripts/NIXBuilder.cs +105 -0
  21. data/legacy/UnityBuildEnvVars.py +41 -0
  22. data/legacy/VerifyBinarySigning.py +80 -0
  23. data/legacy/svn-clean.pl +246 -0
  24. data/legacy/svncopy.pl +1134 -0
  25. data/lib/nixenvironment.rb +5 -0
  26. data/lib/nixenvironment/archiver.rb +690 -0
  27. data/lib/nixenvironment/build_env_vars_loader.rb +24 -0
  28. data/lib/nixenvironment/cmd_executor.rb +33 -0
  29. data/lib/nixenvironment/config.rb +127 -14
  30. data/lib/nixenvironment/git.rb +107 -0
  31. data/lib/nixenvironment/plist.rb +52 -0
  32. data/lib/nixenvironment/version.rb +1 -1
  33. data/lib/nixenvironment/xcodebuild.rb +167 -0
  34. data/nixenvironment.gemspec +6 -0
  35. data/utils/XcodeIconTagger/IconTagger +0 -0
  36. data/utils/XcodeIconTagger/masks/OneLineMask.png +0 -0
  37. data/utils/XcodeIconTagger/masks/TwoLineMask.png +0 -0
  38. data/utils/aapt +0 -0
  39. data/utils/gcovr +986 -0
  40. data/utils/identitieslist +0 -0
  41. data/utils/simian-2.3.33.jar +0 -0
  42. metadata +118 -2
data/legacy/svncopy.pl ADDED
@@ -0,0 +1,1134 @@
1
+ #! /usr/bin/perl
2
+ #
3
+ # svncopy.pl -- Utility script for copying with branching/tagging.
4
+ #
5
+ # This program is free software; you can redistribute it and/or modify it
6
+ # under the terms of the GNU General Public License as published by the
7
+ # Free Software Foundation; either version 2 of the License, or (at your
8
+ # option) any later version.
9
+ #
10
+ # THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
11
+ # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
12
+ # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
13
+ # NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
14
+ # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
15
+ # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
16
+ # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
17
+ # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
18
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
19
+ # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20
+ #
21
+ # You should have received a copy of the GNU General Public License along
22
+ # with this program; if not, write to the Free Software Foundation, Inc.,
23
+ # 59 Temple Place - Suite 330, Boston MA 02111-1307 USA.
24
+ #
25
+ # This product makes use of software developed by
26
+ # CollabNet (http://www.Collab.Net/), see http://subversion.tigris.org/.
27
+ #
28
+ # This software consists of voluntary contributions made by many
29
+ # individuals. For exact contribution history, see the revision
30
+ # history and logs, available at http://subversion.tigris.org/.
31
+ #------------------------------------------------------------------------------
32
+
33
+ #------------------------------------------------------------------------------
34
+ #
35
+ # This script copies one Subversion location to another, in the same way as
36
+ # svn copy. Using the script allows proper branching and tagging of URLs
37
+ # containing svn:externals definitions.
38
+ #
39
+ # For more information see the pod documentation at the foot of the file,
40
+ # or run svncopy.pl -?.
41
+ #
42
+ #------------------------------------------------------------------------------
43
+
44
+ #
45
+ # Include files
46
+ #
47
+ use Cwd;
48
+ use File::Temp 0.12 qw(tempdir tempfile);
49
+ use Getopt::Long 2.25;
50
+ use Pod::Usage;
51
+ use URI 1.17;
52
+
53
+ #
54
+ # Global definitions
55
+ #
56
+
57
+ # Specify the location of the svn command.
58
+ my $svn = 'svn';
59
+
60
+ # Input parameters
61
+ my $testscript = 0;
62
+ my $verbose = 0;
63
+ my $pin_externals = 0;
64
+ my $update_externals = 0;
65
+ my @sources;
66
+ my $destination;
67
+ my $message;
68
+ my @svn_options = ();
69
+
70
+ # Internal information
71
+ my %externals_hash;
72
+ my $temp_dir;
73
+
74
+ # Error handling
75
+ my @errors = ();
76
+ my @warnings = ();
77
+
78
+ # Testing-specific variables
79
+ my $hideerrors = 0;
80
+
81
+
82
+ #------------------------------------------------------------------------------
83
+ # Main execution block
84
+ #
85
+
86
+ #
87
+ # Process arguments
88
+ #
89
+ GetOptions( "pin-externals|tag|t" => \$pin_externals,
90
+ "update-externals|branch|b" => \$update_externals,
91
+ "message|m=s" => \$message,
92
+ "revision|r=s" => \$revision,
93
+ "verbose!" => \$verbose,
94
+ "quiet|q" => sub { $verbose = 0; push( @svn_options, "--quiet" ) },
95
+ "file|F=s" => sub { push( @svn_options, "--file", $_[1] ) },
96
+ "username=s" => sub { push( @svn_options, "--username", $_[1] ) },
97
+ "password=s" => sub { push( @svn_options, "--password", $_[1] ) },
98
+ "no_auth_cache" => sub { push( @svn_options, "--no-auth-cache" ) },
99
+ "force-log" => sub { push( @svn_options, "--force-log" ) },
100
+ "encoding=s" => sub { push( @svn_options, "--encoding", $_[1] ) },
101
+ "config-dir=s" => sub { push( @svn_options, "--config-dir", $_[1] ) },
102
+ "help|?" => sub{ Usage() },
103
+ ) or Usage();
104
+
105
+ # Put in a signal handler to clean up any temporary directories.
106
+ sub catch_signal {
107
+ my $signal = shift;
108
+ warn "$0: caught signal $signal. Quitting now.\n";
109
+ exit 1;
110
+ }
111
+
112
+ $SIG{HUP} = \&catch_signal;
113
+ $SIG{INT} = \&catch_signal;
114
+ $SIG{TERM} = \&catch_signal;
115
+ $SIG{PIPE} = \&catch_signal;
116
+
117
+ #
118
+ # Check our parameters
119
+ #
120
+ if ( @ARGV < 2 )
121
+ {
122
+ Usage( "Please specify source and destination" );
123
+ exit 1;
124
+ }
125
+
126
+ #
127
+ # Get source(s) and destination.
128
+ #
129
+ push ( @sources, shift( @ARGV ) );
130
+ $destination = shift( @ARGV );
131
+ while ( scalar( @ARGV ) )
132
+ {
133
+ push( @sources, $destination );
134
+ $destination = shift( @ARGV );
135
+ }
136
+
137
+ #
138
+ # Any validation errors? If so, bomb out.
139
+ #
140
+ if ( scalar( @errors ) > 0 )
141
+ {
142
+ print "\n", @errors;
143
+ Usage();
144
+ exit scalar( @errors );
145
+ }
146
+
147
+ #
148
+ # Now do the main processing.
149
+ # This will update @errors if anything goes wrong.
150
+ #
151
+ if ( !DoCopy( \@sources, $destination, $message ) )
152
+ {
153
+ print "\n*****************************************************************\n";
154
+ print "Errors:\n";
155
+ print @errors;
156
+ }
157
+
158
+ exit scalar( @errors );
159
+
160
+
161
+ #------------------------------------------------------------------------------
162
+ # Function: DoCopy
163
+ #
164
+ # Does the work of the copy.
165
+ #
166
+ # Parameters:
167
+ # sources Reference to array containing source URLs
168
+ # destination Destination URL
169
+ # message Commit message to use
170
+ #
171
+ # Returns: 0 on error
172
+ #
173
+ # Updates @errors.
174
+ #------------------------------------------------------------------------------
175
+ sub DoCopy
176
+ {
177
+ my ( $sourceref, $destination, $message ) = @_;
178
+ my @sources = @$sourceref;
179
+ my $revstr = "";
180
+ my $src;
181
+ my $startdir = cwd;
182
+ my $starterrors = scalar( @errors );
183
+
184
+ print "\n=================================================================\n";
185
+ $revstr = "\@$revision" if $revision;
186
+ print "=== Copying from:\n";
187
+ foreach $src ( @sources ) { print "=== $src$revstr\n"; }
188
+ print "===\n";
189
+ print "=== Copying to:\n";
190
+ print "=== $destination\n";
191
+ print "===\n";
192
+ print "=== - branching (updating fully-contained svn:externals definitions)\n" if $update_externals;
193
+ if ( $pin_externals )
194
+ {
195
+ my $revtext = $revision ? "revision $revision" : "current revision";
196
+ print "=== - tagging (pinning all svn:externals definitions to $revtext)\n";
197
+ }
198
+ print "===\n" if ( $update_externals or $pin_externals );
199
+
200
+ # Convert destination to URI
201
+ $destination =~ s|/*$||;
202
+ my $destination_uri = URI->new($destination);
203
+
204
+ #
205
+ # Generate a message if we don't have one.
206
+ #
207
+ unless ( $message )
208
+ {
209
+ $message = "svncopy.pl: Copied to '$destination'\n";
210
+ foreach $src ( @sources )
211
+ {
212
+ $message .= " Copied from '$src'\n";
213
+ }
214
+ }
215
+
216
+ #
217
+ # Create a temporary directory to work in.
218
+ #
219
+ my ( $auto_temp_dir, $dest_dir ) =
220
+ PrepareDirectory( $destination_uri, "svncopy.pl to '$destination'\n - creating intermediate directory" );
221
+ $temp_dir = $auto_temp_dir->temp_dir();
222
+ chdir( $temp_dir );
223
+
224
+ foreach $src ( @sources )
225
+ {
226
+ # Convert source to URI
227
+ $src =~ s|/*$||;
228
+ my $source_uri = URI->new($src);
229
+
230
+ #
231
+ # Do the initial copy into our temporary. Note this will create a
232
+ # subdirectory with the same name as the last directory in $source.
233
+ #
234
+ if ( !CopyToWorkDir( $src, $dest_dir ) )
235
+ {
236
+ error( "Copy failed" );
237
+ return 0;
238
+ }
239
+ }
240
+
241
+ #
242
+ # Do any processing.
243
+ #
244
+ if ( $pin_externals or $update_externals )
245
+ {
246
+ if ( !UpdateExternals( $sourceref, $destination, $dest_dir, \$message ) )
247
+ {
248
+ error( "Couldn't update svn:externals" );
249
+ return 0;
250
+ }
251
+ }
252
+
253
+ #
254
+ # And check in the new.
255
+ #
256
+ DoCommit( $dest_dir, $message ) or die "Couldn't commit\n";
257
+
258
+ # Make sure we finish in the directory we started
259
+ chdir( $startdir );
260
+
261
+ print "=== ... copy complete\n";
262
+ print "=================================================================\n";
263
+
264
+ # Return whether there was an error.
265
+ return ( scalar( @errors ) == $starterrors );
266
+ }
267
+
268
+
269
+ #------------------------------------------------------------------------------
270
+ # Function: PrepareDirectory
271
+ #
272
+ # Prepares a temporary directory to work in.
273
+ #
274
+ # Parameters:
275
+ # destination Destination URI
276
+ # message Commit message
277
+ #
278
+ # Returns: temporary directory and subdirectory to work in
279
+ #------------------------------------------------------------------------------
280
+ sub PrepareDirectory
281
+ {
282
+ my ( $destination, $message ) = @_;
283
+
284
+ my $auto_temp_dir = Temp::Delete->new();
285
+ $temp_dir = $auto_temp_dir->temp_dir();
286
+ info( "Using temporary directory $temp_dir\n" );
287
+
288
+ #
289
+ # Our working destination directory has the same name as the last directory
290
+ # in the destination URI.
291
+ #
292
+ my @path_segments = grep { length($_) } $destination->path_segments;
293
+ my $new_dir = pop( @path_segments );
294
+ my $dest_dir = "$temp_dir/$new_dir";
295
+
296
+ # Make sure the destination directory exists in Subversion.
297
+ info( "Creating intermediate directories (if necessary)\n" );
298
+ if ( !CreateSVNDirectories( $destination, $message ) )
299
+ {
300
+ error( "Couldn't create parent directories for '$destination'" );
301
+ return;
302
+ }
303
+
304
+ # Check out the destination.
305
+ info( "Checking out destination directory '$destination'\n" );
306
+ if ( 0 != SVNCall( 'co', $destination, $dest_dir ) )
307
+ {
308
+ error( "Couldn't check out '$destination' into work directory." );
309
+ return;
310
+ }
311
+
312
+ return ( $auto_temp_dir, $dest_dir );
313
+ }
314
+
315
+
316
+ #------------------------------------------------------------------------------
317
+ # Function: CopyToWorkDir
318
+ #
319
+ # Does the svn copy into the temporary directory.
320
+ #
321
+ # Parameters:
322
+ # source The URI to copy from
323
+ # work_dir The working directory
324
+ #
325
+ # Returns: 1 on success
326
+ #------------------------------------------------------------------------------
327
+ sub CopyToWorkDir
328
+ {
329
+ my ( $source, $work_dir ) = @_;
330
+ my $dest_dir = DestinationSubdir( $source, $work_dir );
331
+ my @commandline = ();
332
+
333
+ push( @commandline, "--revision", $revision ) if ( $revision );
334
+
335
+ push( @commandline, $source, $work_dir );
336
+
337
+ my $exit = SVNCall( "copy", @commandline );
338
+
339
+ error( "$0: svn copy failed" ) if ( 0 != $exit );
340
+
341
+ return ( 0 == $exit );
342
+ }
343
+
344
+
345
+ #------------------------------------------------------------------------------
346
+ # Function: DestinationSubdir
347
+ #
348
+ # Returns the destination directory for a given source and a destination root
349
+ # directory.
350
+ #
351
+ # Parameters:
352
+ # source The URL to copy from
353
+ # destination The working directory
354
+ #
355
+ # Returns: The relevant directory
356
+ #------------------------------------------------------------------------------
357
+ sub DestinationSubdir
358
+ {
359
+ my ( $source, $destination ) = @_;
360
+ my $subdir;
361
+
362
+ # Make sure source and destination are consistent about separator.
363
+ # Note every function we call can handle Unix path format, so we
364
+ # default to that.
365
+ $source =~ s|\\|/|g;
366
+ $destination =~ s|\\|/|g;
367
+
368
+ # Find the last directory - that's the subdir we'll use in $destination
369
+ if ( $source =~ m"/([^/]+)/*$" )
370
+ {
371
+ $subdir = $1;
372
+ }
373
+ else
374
+ {
375
+ $subdir = $source;
376
+ }
377
+ return "$destination/$subdir";
378
+ }
379
+
380
+
381
+ #------------------------------------------------------------------------------
382
+ # Function: UpdateExternals
383
+ #
384
+ # Updates the svn:externals in the tree according to the --pin-externals or
385
+ # --update_externals options.
386
+ #
387
+ # Parameters:
388
+ # sourceref Ref to the URLs to copy from
389
+ # destination The URL being copied to
390
+ # work_dir The working directory
391
+ # msgref Ref to message string to update with changes
392
+ #
393
+ # Returns: 1 on success
394
+ #------------------------------------------------------------------------------
395
+ sub UpdateExternals
396
+ {
397
+ my ( $sourceref, $destination, $work_dir, $msgref ) = @_;
398
+ my @commandline = ();
399
+ my $msg;
400
+ my @dirfiles;
401
+ my %extlist;
402
+
403
+ # Check the externals on this directory and subdirectories
404
+ info( "Checking '$work_dir'\n" );
405
+ %extlist = GetRecursiveExternals( $work_dir );
406
+
407
+ # And do the update
408
+ while ( my ( $subdir, $exts ) = each ( %extlist ) )
409
+ {
410
+ my @externals = @$exts;
411
+ if ( scalar( @externals ) )
412
+ {
413
+ UpdateExternalsOnDir( $sourceref, $destination, $subdir, $msgref, \@externals );
414
+ }
415
+ }
416
+
417
+ return 1;
418
+ }
419
+
420
+
421
+ #------------------------------------------------------------------------------
422
+ # Function: UpdateExternalsOnDir
423
+ #
424
+ # Updates the svn:externals in the tree according to the --pin-externals or
425
+ # --update_externals options.
426
+ #
427
+ # Parameters:
428
+ # sourceref Ref to the URLs to copy from
429
+ # destination The URL being copied to
430
+ # work_dir The working directory
431
+ # externals Ref to the externals on the directory
432
+ # msgref Ref to message string to update with changes
433
+ #
434
+ # Returns: 1 on success
435
+ #------------------------------------------------------------------------------
436
+ sub UpdateExternalsOnDir
437
+ {
438
+ my ( $sourceref, $destination, $work_dir, $msgref, $externalsref ) = @_;
439
+ my @sources = @$sourceref;
440
+ my @externals = @$externalsref;
441
+ my @new_externals;
442
+ my %changed;
443
+
444
+ # Do any updating required
445
+ foreach my $external ( @externals )
446
+ {
447
+ chomp( $external );
448
+ next unless ( $external =~ m"^(\S+)(\s+)(?:-r\s*(\d+)\s+)?(.*)" );
449
+ my ( $ext_dir, $spacing, $ext_rev, $ext_val ) = ( $1, $2, $3, $4 );
450
+
451
+ info( " - Found $ext_dir => '$ext_val'" );
452
+ info( " ($ext_rev)" ) if $ext_rev;
453
+ info( "\n" );
454
+
455
+ $externals_hash{ "$ext_val" } = $ext_rev;
456
+
457
+ # Only update if it's not pinned to a version
458
+ if ( !$ext_rev )
459
+ {
460
+ if ( $update_externals )
461
+ {
462
+ my $old_external = $external;
463
+ foreach my $source ( @sources )
464
+ {
465
+ my $dest_dir = DestinationSubdir( $source, $destination );
466
+ #info( "Checking against '$source'\n" );
467
+ if ( $ext_val =~ s|^$source|$dest_dir| )
468
+ {
469
+ $external = "$ext_dir$spacing$ext_val";
470
+ info( " - updated '$old_external' to '$external'\n" );
471
+ $changed{$old_external} = $external;
472
+ }
473
+ }
474
+ }
475
+ elsif ( $pin_externals )
476
+ {
477
+ # Find the last revision of the destination and pin to that.
478
+ my $old_external = $external;
479
+ my $rev = LatestRevision( $ext_val, $revision );
480
+ #info( "Pinning '$ext_val' to '$rev'\n" );
481
+ $external = "$ext_dir -r $rev$spacing$ext_val";
482
+ info( " - updated '$old_external' to '$external'\n" );
483
+ $changed{$old_external} = $external;
484
+ }
485
+ }
486
+ push( @new_externals, $external );
487
+ }
488
+
489
+ # And write the changes back
490
+ if ( scalar( %changed ) )
491
+ {
492
+ # Update the commit log message
493
+ my %info = SVNInfo( $work_dir );
494
+ $$msgref .= "\n * $info{URL}: update svn:externals\n";
495
+ while ( my ( $old, $new ) = each( %changed ) )
496
+ {
497
+ $$msgref .= " from '$old' to '$new'\n";
498
+ info( " '$old' => '$new'\n" );
499
+ }
500
+
501
+ # And set the new externals
502
+ my ($handle, $tmpfile) = tempfile( DIR => $temp_dir );
503
+ print $handle join( "\n", @new_externals );
504
+ close($handle);
505
+ SVNCall( "propset", "--file", $tmpfile, "svn:externals", $work_dir );
506
+ }
507
+ }
508
+
509
+
510
+ #------------------------------------------------------------------------------
511
+ # Function: GetRecursiveExternals
512
+ #
513
+ # This function retrieves the svn:externals value from the
514
+ # specified URL or location and subdirectories.
515
+ #
516
+ # Parameters:
517
+ # location location of SVN object - file/dir or URL.
518
+ #
519
+ # Returns: hash
520
+ #------------------------------------------------------------------------------
521
+ sub GetRecursiveExternals
522
+ {
523
+ my ( $location ) = @_;
524
+ my %retval;
525
+ my $externals;
526
+ my $subdir = ".";
527
+
528
+ my ( $status, @externals ) = SVNCall( "propget", "-R", "svn:externals", $location );
529
+
530
+ foreach my $external ( @externals )
531
+ {
532
+ chomp( $external );
533
+
534
+ if ( $external =~ m"(.*) - (.*\s.*)" )
535
+ {
536
+ $subdir = $1;
537
+ $external = $2;
538
+ }
539
+
540
+ push( @{$retval{$subdir}}, $external ) unless $external =~ m"^\s*$";
541
+ }
542
+
543
+ return %retval;
544
+ }
545
+
546
+
547
+ #------------------------------------------------------------------------------
548
+ # Function: SVNInfo
549
+ #
550
+ # Gets the info about the given file.
551
+ #
552
+ # Parameters:
553
+ # file The SVN object to query
554
+ #
555
+ # Returns: hash with the info
556
+ #------------------------------------------------------------------------------
557
+ sub SVNInfo
558
+ {
559
+ my $file = shift;
560
+ my $old_verbose = $verbose;
561
+ $verbose = 0;
562
+ my ( $retval, @output ) = SVNCall( "info", $file );
563
+ $verbose = $old_verbose;
564
+ my %info;
565
+
566
+ return if ( 0 != $retval );
567
+
568
+ foreach my $line ( @output )
569
+ {
570
+ if ( $line =~ "^(.*): (.*)" )
571
+ {
572
+ $info{ $1 } = $2;
573
+ }
574
+ }
575
+
576
+ return %info;
577
+ }
578
+
579
+
580
+ #------------------------------------------------------------------------------
581
+ # Function: LatestRevision
582
+ #
583
+ # Returns the repository revision of the last change to the given object not
584
+ # later than the given revision (i.e. it may return revision, but won't
585
+ # return revision+1).
586
+ #
587
+ # Parameters:
588
+ # source The URL to check
589
+ # revision The revision of the URL to check from (if not supplied
590
+ # defaults to last revision).
591
+ #
592
+ # Returns: The relevant revision number
593
+ #------------------------------------------------------------------------------
594
+ sub LatestRevision
595
+ {
596
+ my ( $source, $revision ) = @_;
597
+ my $revtext = "";
598
+
599
+ if ( $revision )
600
+ {
601
+ $revtext = "--revision $revision:0";
602
+ }
603
+
604
+ my $old_verbose = $verbose;
605
+ $verbose = 0;
606
+ my ( $retval, @output ) = SVNCall( "log -q", $revtext, $source );
607
+ $verbose = $old_verbose;
608
+
609
+ if ( 0 != $retval )
610
+ {
611
+ error( "LatestRevision: log -q on '$source' failed" );
612
+ return -1;
613
+ }
614
+
615
+ #
616
+ # The second line should give us the info we need: e.g.
617
+ #
618
+ # >svn log -q http://subversion/svn/scratch/ianb/svncopy-update/source/dirA
619
+ # ------------------------------------------------------------------------
620
+ # r1429 | ib | 2004-06-14 17:39:36 +0100 (Mon, 14 Jun 2004)
621
+ # ------------------------------------------------------------------------
622
+ # r1423 | ib | 2004-06-14 17:39:26 +0100 (Mon, 14 Jun 2004)
623
+ # ------------------------------------------------------------------------
624
+ # r1422 | ib | 2004-06-14 17:39:23 +0100 (Mon, 14 Jun 2004)
625
+ # ------------------------------------------------------------------------
626
+ # r1421 | ib | 2004-06-14 17:39:22 +0100 (Mon, 14 Jun 2004)
627
+ # ------------------------------------------------------------------------
628
+ #
629
+ # The second line starts with the latest revision number.
630
+ #
631
+ if ( $output[1] =~ m"^r(\d+) \|" )
632
+ {
633
+ return $1;
634
+ }
635
+
636
+ error( "LatestRevision: log output not formatted as expected\n" );
637
+
638
+ return -1;
639
+ }
640
+
641
+
642
+ #------------------------------------------------------------------------------
643
+ # Function: DoCommit
644
+ #
645
+ # svn commits the temporary directory.
646
+ #
647
+ # Parameters:
648
+ # work_dir The working directory
649
+ # message Commit message
650
+ #
651
+ # Returns: non-zero on success
652
+ #------------------------------------------------------------------------------
653
+ sub DoCommit
654
+ {
655
+ my ( $work_dir, $message ) = @_;
656
+ my @commandline = ();
657
+
658
+ # Prepare a file containing the message
659
+ my ($handle, $messagefile) = tempfile( DIR => $temp_dir );
660
+ print $handle $message;
661
+ close($handle);
662
+ push( @commandline, "--file", $messagefile );
663
+
664
+ push( @commandline, $work_dir );
665
+
666
+ my ( $exit ) = SVNCall( "commit", @commandline );
667
+
668
+ error( "$0: svn commit failed" ) if ( 0 != $exit );
669
+
670
+ return ( 0 == $exit );
671
+ }
672
+
673
+
674
+ #------------------------------------------------------------------------------
675
+ # Function: SVNCall
676
+ #
677
+ # Makes a call to subversion.
678
+ #
679
+ # Parameters:
680
+ # command Subversion command
681
+ # options Other options to pass to Subversion
682
+ #
683
+ # Returns: exit status, output from command
684
+ #------------------------------------------------------------------------------
685
+ sub SVNCall
686
+ {
687
+ my ( $command, @options ) = @_;
688
+
689
+ # this block was added for the script to work fine with temporary
690
+ # directory pathes which include space characters
691
+ my $i=0; for(@options)
692
+ {
693
+ if(($options[$i] !~ /^".*"$/) && ($options[$i] =~ /\/.*/i))
694
+ {
695
+ $options[$i] = '"'.$_.'"';
696
+ }
697
+ $i++;
698
+ }
699
+
700
+ my @commandline = ( $svn, $command, @svn_options, @options );
701
+
702
+ info( " > ", join( " ", @commandline ), "\n" );
703
+
704
+ my @output = qx( @commandline 2>&1 );
705
+
706
+ my $result = $?;
707
+ my $exit = $result >> 8;
708
+ my $signal = $result & 127;
709
+ my $cd = $result & 128 ? "with core dump" : "";
710
+ if ($signal or $cd)
711
+ {
712
+ error( "$0: 'svn $command' failed $cd: exit=$exit signal=$signal\n" );
713
+ }
714
+
715
+ if ( $exit > 0 )
716
+ {
717
+ info( join( "\n", @output ) );
718
+ }
719
+ if ( wantarray )
720
+ {
721
+ return ( $exit, @output );
722
+ }
723
+
724
+ return $exit;
725
+ }
726
+
727
+
728
+ #------------------------------------------------------------------------------
729
+ # Function: FindRepositoryRoot
730
+ #
731
+ # Returns the root of the repository for a given URL. Do
732
+ # this with the svn log command. Take the svn_url hostname and port
733
+ # as the initial url and append to it successive portions of the final
734
+ # path until svn log succeeds.
735
+ #
736
+ # Parameters:
737
+ # URI URI within repository
738
+ #
739
+ # Returns: A URI for the root, or undefined on error
740
+ #------------------------------------------------------------------------------
741
+ sub FindRepositoryRoot
742
+ {
743
+ my $URI = shift;
744
+ my $repos_root_uri;
745
+ my $repos_root_uri_path;
746
+ my $old_verbose = $verbose;
747
+ $verbose = 0;
748
+
749
+ info( "Finding the root URL of '$URI'.\n" );
750
+
751
+ my $r = $URI->clone;
752
+ my @path_segments = grep { length($_) } $r->path_segments;
753
+ unshift(@path_segments, '');
754
+ $r->path('');
755
+ my @r_path_segments;
756
+
757
+ while (@path_segments)
758
+ {
759
+ $repos_root_uri_path = shift @path_segments;
760
+ push(@r_path_segments, $repos_root_uri_path);
761
+ $r->path_segments(@r_path_segments);
762
+ if ( SVNCall( 'log', '-r', 'HEAD', $r ) == 0 )
763
+ {
764
+ $repos_root_uri = $r;
765
+ last;
766
+ }
767
+ }
768
+
769
+ $verbose = $old_verbose;
770
+
771
+ if ($repos_root_uri)
772
+ {
773
+ info( "Determined that the svn root URL is $repos_root_uri.\n\n" );
774
+ return $repos_root_uri;
775
+ }
776
+ else
777
+ {
778
+ error( "$0: cannot determine root svn URL for '$URI'.\n" );
779
+ return;
780
+ }
781
+ }
782
+
783
+
784
+ #------------------------------------------------------------------------------
785
+ # Function: CreateSVNDirectories
786
+ #
787
+ # Creates a directory in Subversion, including all intermediate directories.
788
+ #
789
+ # Parameters:
790
+ # URI directory path to create.
791
+ # message commit message (optional).
792
+ #
793
+ # Returns: 1 on success, 0 on error
794
+ #------------------------------------------------------------------------------
795
+ sub CreateSVNDirectories
796
+ {
797
+ my ( $URI, $message ) = @_;
798
+ my $r = $URI->clone;
799
+ my @path_segments = grep { length($_) } $r->path_segments;
800
+ my @r_path_segments;
801
+ unshift(@path_segments, '');
802
+ $r->path('');
803
+
804
+ my $found_root = 0;
805
+ my $found_tail = 0;
806
+
807
+ # Prepare a file containing the message
808
+ my ($handle, $messagefile) = tempfile( DIR => $temp_dir );
809
+ print $handle $message;
810
+ close($handle);
811
+ my @msgcmd = ( "--file", $messagefile );
812
+
813
+ # We're going to get errors while we do this. Don't show the user.
814
+ my $old_verbose = $verbose;
815
+ $verbose = 0;
816
+ # Find the repository root
817
+ while (@path_segments)
818
+ {
819
+ my $segment = shift @path_segments;
820
+ push( @r_path_segments, $segment );
821
+ $r->path_segments( @r_path_segments );
822
+ if ( !$found_root )
823
+ {
824
+ if ( SVNCall( 'log', '-r', 'HEAD', $r ) == 0 )
825
+ {
826
+ # We've found the root of the repository.
827
+ $found_root = 1;
828
+ }
829
+ }
830
+ elsif ( !$found_tail )
831
+ {
832
+ if ( SVNCall( 'log', '-r', 'HEAD', $r ) != 0 )
833
+ {
834
+ # We've found the first directory which doesn't exist.
835
+ $found_tail = 1;
836
+ }
837
+ }
838
+
839
+ if ( $found_tail )
840
+ {
841
+ # We're creating directories
842
+ $verbose = $old_verbose;
843
+ if ( 0 != SVNCall( 'mkdir', @msgcmd, $r ) )
844
+ {
845
+ error( "Couldn't create directory '$r'" );
846
+ return 0;
847
+ }
848
+ }
849
+ }
850
+ $verbose = $old_verbose;
851
+
852
+ return 1;
853
+ }
854
+
855
+
856
+ #------------------------------------------------------------------------------
857
+ # Function: info
858
+ #
859
+ # Prints out an informational message in verbose mode
860
+ #
861
+ # Parameters:
862
+ # @_ The message(s) to print
863
+ #
864
+ # Returns: none
865
+ #------------------------------------------------------------------------------
866
+ sub info
867
+ {
868
+ if ( $verbose )
869
+ {
870
+ print @_;
871
+ }
872
+ }
873
+
874
+
875
+ #------------------------------------------------------------------------------
876
+ # Function: error
877
+ #
878
+ # Prints out and logs an error message
879
+ #
880
+ # Parameters:
881
+ # @_ The error messages
882
+ #
883
+ # Returns: none
884
+ #------------------------------------------------------------------------------
885
+ sub error
886
+ {
887
+ my $error;
888
+
889
+ # This is used during testing
890
+ if ( $hideerrors )
891
+ {
892
+ return;
893
+ }
894
+
895
+ # Now print out each error message and add it to the list.
896
+ foreach $error ( @_ )
897
+ {
898
+ my $text = "svncopy.pl: $error\n";
899
+ push( @errors, $text );
900
+ if ( $verbose )
901
+ {
902
+ print $text;
903
+ }
904
+ }
905
+ }
906
+
907
+
908
+ #------------------------------------------------------------------------------
909
+ # Function: Usage
910
+ #
911
+ # Prints out usage information.
912
+ #
913
+ # Parameters:
914
+ # optional error message
915
+ #
916
+ # Returns: none
917
+ #------------------------------------------------------------------------------
918
+ sub Usage
919
+ {
920
+ my $msg;
921
+ $msg = "\n*** $_[0] ***\n" if $_[0];
922
+
923
+ pod2usage( { -message => $msg,
924
+ -verbose => 0 } );
925
+ }
926
+
927
+
928
+ #------------------------------------------------------------------------------
929
+ # This package exists just to delete the temporary directory.
930
+ #------------------------------------------------------------------------------
931
+ package Temp::Delete;
932
+
933
+ use File::Temp 0.12 qw(tempdir);
934
+
935
+ sub new
936
+ {
937
+ my $this = shift;
938
+ my $class = ref($this) || $this;
939
+ my $self = {};
940
+ bless $self, $class;
941
+
942
+ my $temp_dir = tempdir("svncopy_XXXXXXXXXX", TMPDIR => 1);
943
+
944
+ $self->{tempdir} = $temp_dir;
945
+
946
+ return $self;
947
+ }
948
+
949
+ sub temp_dir
950
+ {
951
+ my $self = shift;
952
+ return $self->{tempdir};
953
+ }
954
+
955
+ sub DESTROY
956
+ {
957
+ my $self = shift;
958
+ my $temp_dir = $self->{tempdir};
959
+ if ( scalar( @errors ) )
960
+ {
961
+ print "Leaving $temp_dir for inspection\n";
962
+ }
963
+ else
964
+ {
965
+ info( "Cleaning up $temp_dir\n" );
966
+ File::Path::rmtree([$temp_dir], 0, 0);
967
+ }
968
+ }
969
+
970
+
971
+ #------------------------------------------------------------------------------
972
+ # Documentation follows, in pod format.
973
+ #------------------------------------------------------------------------------
974
+ __END__
975
+
976
+ =head1 NAME
977
+
978
+ B<svncopy> - extended form of B<svn copy>
979
+
980
+ =head1 SYNOPSIS
981
+
982
+ B<svncopy.pl> [option ...] source [source ...] destination
983
+
984
+ This script copies one Subversion location or set of locations to another,
985
+ in the same way as B<svn copy>. Using the script allows more advanced operations,
986
+ in particular allowing svn:externals to be dealt with properly for branching
987
+ or tagging.
988
+
989
+ Parameters:
990
+ source Subversion item to copy from.
991
+ Multiple sources can be given.
992
+ destination Destination to copy to.
993
+
994
+ Options:
995
+ -t [--tag] : set svn:externals to current version
996
+ [--pin-externals ]
997
+ -b [--branch] : update fully contained svn:externals
998
+ [--update-externals]
999
+ -m [--message] arg : specify commit message ARG
1000
+ -F [--file] arg : read data from file ARG
1001
+ -r [--revision] arg : ARG (some commands also take ARG1:ARG2 range)
1002
+ A revision argument can be one of:
1003
+ NUMBER revision number
1004
+ "{" DATE "}" revision at start of the date
1005
+ "HEAD" latest in repository
1006
+ "BASE" base rev of item's working copy
1007
+ "COMMITTED" last commit at or before BASE
1008
+ "PREV" revision just before COMMITTED
1009
+ -q [--quiet] : print as little as possible
1010
+ --username arg : specify a username ARG
1011
+ --password arg : specify a password ARG
1012
+ --no-auth-cache : do not cache authentication tokens
1013
+ --force-log : force validity of log message source
1014
+ --encoding arg : treat value as being in charset encoding ARG
1015
+ --config-dir arg : read user config files from directory ARG
1016
+ --[no]verbose : sets the script to give lots of output
1017
+
1018
+ =head1 PARAMETERS
1019
+
1020
+ =over
1021
+
1022
+ =item B<source>
1023
+
1024
+ The subversion item or items to copy from.
1025
+
1026
+ =item B<destination>
1027
+
1028
+ The destination URL to copy to.
1029
+
1030
+ =back
1031
+
1032
+ =head1 OPTIONS
1033
+
1034
+ =over
1035
+
1036
+ =item B<-t [--pin-externals or --tag]>
1037
+
1038
+ Update any svn:externals to ensure they have a version number,
1039
+ using the current destination version if none is already specified.
1040
+ Useful for tagging operations.
1041
+
1042
+ =item B<-b [--update-externals or --branch]>
1043
+
1044
+ Update any unversioned svn:externals which point to a location
1045
+ within one of the sources so that they point to the corresponding
1046
+ location within the destination.
1047
+
1048
+ Note: --pin-externals and --update-externals are mutually exclusive.
1049
+
1050
+ =item B<-m [--message] arg>
1051
+
1052
+ Specify commit message ARG
1053
+
1054
+ =item B<-F [--file] arg>
1055
+
1056
+ Read data from file ARG
1057
+
1058
+ =item B<-r [--revision] arg>
1059
+
1060
+ ARG (some commands also take ARG1:ARG2 range)
1061
+ A revision argument can be one of:
1062
+
1063
+ NUMBER revision number
1064
+ "{" DATE "}" revision at start of the date
1065
+ "HEAD" latest in repository
1066
+ "BASE" base rev of item's working copy
1067
+ "COMMITTED" last commit at or before BASE
1068
+ "PREV" revision just before COMMITTED
1069
+
1070
+ =item B<-q [--quiet]>
1071
+
1072
+ Print as little as possible
1073
+
1074
+ =item B<--username arg>
1075
+
1076
+ Specify a username ARG
1077
+
1078
+ =item B<--password arg>
1079
+
1080
+ Specify a password ARG
1081
+
1082
+ =item B<--no-auth-cache>
1083
+
1084
+ Do not cache authentication tokens
1085
+
1086
+ =item B<--force-log>
1087
+
1088
+ Force validity of log message source
1089
+
1090
+ =item B<--encoding arg>
1091
+
1092
+ Treat value as being in charset encoding ARG
1093
+
1094
+ =item B<--config-dir arg>
1095
+
1096
+ Read user configuration files from directory ARG
1097
+
1098
+ =item B<--[no]verbose>
1099
+
1100
+ Sets the script to give lots of output when it runs.
1101
+
1102
+ =item B<--help>
1103
+
1104
+ Print a brief help message and exits.
1105
+
1106
+ =back
1107
+
1108
+ =head1 DESCRIPTION
1109
+
1110
+ This script performs an B<svn copy> command. It allows extra processing to get
1111
+ around the following limitations of B<svn copy>:
1112
+
1113
+ svn:externals definitions are (in Subversion 1.0 and 1.1 at least) absolute paths.
1114
+ This means that an B<svn copy> used as a branch or tag operation on a tree with
1115
+ embedded svn:externals will not do what is expected. The svn:externals
1116
+ will still point at the original location and will not be pinned down.
1117
+
1118
+ B<svncopy --update-externals> (or B<svncopy --branch>) will update any
1119
+ unversioned svn:externals in the destination tree which point at locations
1120
+ within one of the source trees so that they point to the corresponding locations
1121
+ within the destination tree instead. This effectively updates the reference to
1122
+ point to the destination tree, and is the behaviour you want for branching.
1123
+
1124
+ B<svncopy --pin-externals> (or B<svncopy --tag>) will update any unversioned
1125
+ svn:externals in the destination tree to contain the current version of the
1126
+ directory listed in the svn:externals definition. This effectively pins
1127
+ the reference to the current version, and is the behaviour you want for tagging.
1128
+
1129
+ Note: both forms of the command leave unchanged any svn:externals which
1130
+ already contain a version number.
1131
+
1132
+ =cut
1133
+
1134
+ #------------------------------- END OF FILE ----------------------------------