Subversion Repositories DevTools

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
267 dpurdie 1
########################################################################
2
# Copyright (C) 2008 ERG Limited, All rights reserved
3
#
4
# Module name   : jats.sh
5
# Module type   : Makefile system
6
# Compiler(s)   : n/a
7
# Environment(s): jats
8
#
9
# Description   : JATS Subversion Interface Functions
10
#
11
#                 Requires a subversion client to be present on the machine
12
#                 Does require at least SubVersion 1.5
13
#                 Uses features not available in 1.4
14
#
15
#                 The package currently implements a set of functions
16
#                 There are some intentional limitations:
17
#                   1) Non recursive
18
#                   2) Errors terminate operation
19
#
20
#                 This package contains experimental argument passing
21
#                 processes. Sometimes use a hash of arguments
22
#
23
#......................................................................#
24
 
25
require 5.008_002;
26
use strict;
27
use warnings;
341 dpurdie 28
our $USER;
267 dpurdie 29
use JatsEnv;
30
 
31
package JatsSvn;
32
 
33
use JatsError;
34
use JatsSystem;
35
use JatsSvnCore qw(:All);
4185 dpurdie 36
use JatsLocateFiles;
267 dpurdie 37
 
38
use File::Path;             # Instead of FileUtils
39
use File::Basename;
40
use Cwd;
41
 
42
 
43
# automatically export what we need into namespace of caller.
44
use Exporter();
45
our (@ISA, @EXPORT, %EXPORT_TAGS, @EXPORT_OK);
46
@ISA         = qw(Exporter JatsSvnCore);
47
 
48
@EXPORT      = qw(
49
                    NewSession
50
                    NewSessionByWS
51
                    NewSessionByUrl
52
 
53
                    SvnRmView
54
                    SvnIsaSimpleLabel
55
                    SvnComment
56
 
57
                    SvnUserCmd
361 dpurdie 58
 
59
                    SvnPath2Url
369 dpurdie 60
                    SvnPaths
267 dpurdie 61
                );
62
@EXPORT_OK =  qw(
63
                );
64
 
65
%EXPORT_TAGS = (All => [@EXPORT, @EXPORT_OK]);
66
 
67
#
68
#   Global Variables
69
#
70
 
71
#-------------------------------------------------------------------------------
72
# Function        : SvnCo
73
#
74
# Description     : Create a workspace
75
#                   Can be used to extract files, without creating the
76
#                   subversion control files.
77
#
1403 dpurdie 78
# Inputs          : $self                   - Instance data
79
#                   $RepoPath               - Within the repository
80
#                   $Path                   - Local path
81
#                   Hash of Options
82
#                           export          - Bool: Export Only
2429 dpurdie 83
#                           escrow          - Bool: Less sanity testing
1403 dpurdie 84
#                           force           - Bool: Force export to overwrite
85
#                           print           - Bool: Don't print files exported
86
#                           pretext=aa      - Text: Display before operation
267 dpurdie 87
#
88
# Returns         : Nothing
89
#
90
sub SvnCo
91
{
1403 dpurdie 92
    my $self = shift;
93
    my $RepoPath = shift;
94
    my $path = shift;
95
    my %opt = @_;
2429 dpurdie 96
 
341 dpurdie 97
    Debug ("SvnCo", $RepoPath, $path);
1403 dpurdie 98
    Error ("SvnCi: Odd number of args") unless ((@_ % 2) == 0);
267 dpurdie 99
 
100
    #
1403 dpurdie 101
    #   Set some defaults
102
    #
103
    my $cmd = $opt{export} ? 'export' : 'checkout';
104
    my $print = exists $opt{print} ? $opt{print} : 1;
105
    $self->{CoText} =  $opt{pretext} || 'Extracting';
106
 
2429 dpurdie 107
    #   Define RE to be used to test extraction
108
    #       Bad news: Some Cots packages have /tags/
109
    #       Kludge  : Allow /tags/ in escrow mode
1403 dpurdie 110
    #
2429 dpurdie 111
    $self->{CoRe} = '((/)(tags|branches|trunk)(/|$))';
112
    $self->{CoRe} =~ s~tags\|~~ if ( $opt{escrow} );
113
 
114
    #
267 dpurdie 115
    #   Ensure that the output path does not exist
116
    #   Do not allow the user to create a local work space
117
    #   where one already exists
118
    #
119
    Error ("SvnCo: No PATH specified" ) unless ( $path );
1403 dpurdie 120
    Error ("SvnCo: Target path already exists", "Path: " . $path ) if ( ! $opt{force} && -e $path  );
267 dpurdie 121
 
122
    #
123
    #   Build up the command line
124
    #
1403 dpurdie 125
    my @args = $cmd;
267 dpurdie 126
    push @args, qw( --ignore-externals );
1403 dpurdie 127
    push @args, qw( --force ) if ( $opt{force} );
267 dpurdie 128
    push @args, $RepoPath, $path;
129
 
130
    my @co_list;
131
    if ( $self->SvnCmd ( @args,
132
                            {
133
                                'process' => \&ProcessCo,
134
                                'data' => \@co_list,
135
                                'credentials' => 1,
136
                                'nosavedata' => 1,
1403 dpurdie 137
                                'printdata' => $print,
267 dpurdie 138
                            }
139
                       ) || @co_list )
140
    {
141
        #
142
        #   We have a checkout limitation
143
        #   Delete the workspace and then report the error
144
        #
385 dpurdie 145
        #   Note: For some reason a simple rmtree doesn't work
146
        #         Nor does glob show all the directories
147
        #
267 dpurdie 148
        Verbose2 ("Remove WorkSpace: $path");
149
        rmtree( $path, IsVerbose(3) );
385 dpurdie 150
        rmtree( $path, IsVerbose(3) );
267 dpurdie 151
        Error ("Checking out Workspace", @{$self->{ERROR_LIST}}, @co_list );
152
    }
2429 dpurdie 153
 
154
    #
155
    #   Cleanup
156
    #
157
    delete $self->{CoText};
158
    delete $self->{CoRe};
267 dpurdie 159
    return;
160
 
161
    #
162
    #   Internal routine to scan each the checkout
163
    #
164
    #   Due to the structure of a SubVersion repository it would be
165
    #   possible for a user to extract the entire repository. This is
166
    #   not good as the repo could be very very large
167
    #
168
    #   Assume that the structure of the repo is such that our
169
    #   user is not allowed to extract a directory tree that contains
170
    #   key paths - such as /tags/ as this would indicate that they are
171
    #   attempting to extract something that is not a package
172
    #
173
    #
174
    sub ProcessCo
175
    {
176
        my $self = shift;
1329 dpurdie 177
        my $data = shift;
178
 
179
        if ( $self->{PRINTDATA} )
267 dpurdie 180
        {
1329 dpurdie 181
            #
182
            #   Pretty display for user
1403 dpurdie 183
            #   Hide some noise, but not much
1329 dpurdie 184
            #
1403 dpurdie 185
            unless ( $data =~ m~^Export complete.~ )
186
            {
187
                Information1 ( $self->{CoText} . ': ' . $data);
188
            }
1329 dpurdie 189
        }
190
 
2429 dpurdie 191
        #
192
        #   Detect user attempting to checkout too much of a repo
193
        #   If the extract contains a 'key' directory then create error
194
        #
195
        #   Re is provide by caller such that $1 is the dirpath
196
        #
197
        if ( $data =~ m~$self->{CoRe}~ )
1329 dpurdie 198
        {
267 dpurdie 199
            my $bad_dir = $1;
200
            push @{$self->{ERROR_LIST}}, "Checkout does not describe the root of a package. Contains: $bad_dir";
201
            return 1;
202
        }
203
 
204
        ##
205
        ##   Limit the size of the WorkSpace
206
        ##   This limit is a bit artificial, but does attempt to
207
        ##   limit views that encompass too much.
208
        ##
209
        #if ( $#{$self->{RESULT_LIST}} > 100 )
210
        #{
211
        #    Warning ("View is too large - DEBUG USE ONLY. WILL BE REMOVED" );
212
        #    push @{$self->{ERROR_LIST}}, "View too large";
213
        #    return 1;
214
        #}
215
    }
216
}
217
 
218
#-------------------------------------------------------------------------------
1403 dpurdie 219
# Function        : SvnSwitch
220
#
221
# Description     : Switches files and directories
222
#
223
# Inputs          : $self               - Instance data
224
#                   $RepoPath           - Within the repository
225
#                   $Path               - Local path
226
#                   Options             - Options
227
#                           --NoPrint   - Don't print files exported
2054 dpurdie 228
#                           --KeepWs    - Don't delete the WorkSpace on error
1403 dpurdie 229
#
230
# Returns         : Nothing
231
#
232
sub SvnSwitch
233
{
234
    my ($self, $RepoPath, $path, @opts) = @_;
235
    my $printdata = ! grep (/^--NoPrint/, @opts );
2054 dpurdie 236
    my $keepWs = grep (/^--KeepWs/, @opts );
1403 dpurdie 237
    Debug ("SvnSwitch", $RepoPath, $path);
238
 
239
    #
240
    #   Build up the command line
241
    #
242
    my @sw_list;
243
    if ( $self->SvnCmd ( 'switch', $RepoPath, $path,
244
                            {
245
                                'process' => \&ProcessSwitch,
246
                                'data' => \@sw_list,
247
                                'credentials' => 1,
248
                                'nosavedata' => 1,
249
                                'printdata' => $printdata,
250
                            }
251
                       ) || @sw_list )
252
    {
253
        #
254
        #   We have a switch problem
255
        #   Delete the workspace and then report the error
256
        #
257
        #   Note: For some reason a simple rmtree doesn't work
258
        #         Nor does glob show all the directories
259
        #
2054 dpurdie 260
        unless ( $keepWs )
261
        {
262
            Verbose2 ("Remove WorkSpace: $path");
263
            rmtree( $path, IsVerbose(3) );
264
            rmtree( $path, IsVerbose(3) );
265
            Error ("Switch elements", @{$self->{ERROR_LIST}}, @sw_list );
266
        }
267
        Warning("Switch error: Workspace state unkwown", @{$self->{ERROR_LIST}}, @sw_list);
1403 dpurdie 268
    }
269
    return;
270
 
271
    #
272
    #   Internal routine to scan each line of the Switch output
273
    #   Use to provide a nice display
274
    #
275
    sub ProcessSwitch
276
    {
277
        my $self = shift;
278
        my $data = shift;
279
 
280
        if ( $self->{PRINTDATA} )
281
        {
282
            #
283
            #   Pretty display for user
284
            #
285
            Information1 ("Switching : $data");
286
        }
287
    }
288
}
289
 
290
#-------------------------------------------------------------------------------
267 dpurdie 291
# Function        : SvnCi
292
#
293
# Description     : Check in the specified WorkSpace
294
#
295
# Inputs          : $self           - Instance data
296
#                   A hash of named arguments
297
#                       comment     - Commit comment
379 dpurdie 298
#                       allowSame   - Allow no change to the workspace
267 dpurdie 299
#
300
# Returns         : Tag of the checkin
301
#
302
sub SvnCi
303
{
304
    my $self = shift;
305
    my %opt = @_;
306
    my $status_url;
379 dpurdie 307
    my $ws_rev;
267 dpurdie 308
 
309
    Debug ("SvnCi");
310
    Error ("SvnCi: Odd number of args") unless ((@_ % 2) == 0);
311
 
312
    #
313
    #   Validate the source path
1329 dpurdie 314
    #   Note: populates %{$self->{InfoWs}} with 'info' data
267 dpurdie 315
    #
316
    my $path = SvnValidateWs ($self, 'SvnCi');
317
 
318
    #
1329 dpurdie 319
    #   Examine %{$self->{InfoWs}}, which has the results of an 'info'
267 dpurdie 320
    #   command the locate the URL.
321
    #
322
    #   This contains the target view space
323
    #   Sanity test. Don't allow Checkin to a /tags/ area
324
    #
1329 dpurdie 325
    $status_url = $self->{InfoWs}{URL};
326
    $ws_rev = $self->{InfoWs}{Revision};
379 dpurdie 327
 
267 dpurdie 328
    Error ("SvnCi: Cannot determine Repositoty URL")
329
        unless ( $status_url );
330
 
331
    Error ("SvnCi: Not allowed to commit to a 'tags' area", "URL: $status_url")
332
        if ( $status_url =~ m~/tags(/|$)~ );
333
 
334
    #
335
    #   Commit
1329 dpurdie 336
    #   Will modify Repo, so kill the cached Info
337
    #   Will only be a real issue if we tag in the same session
267 dpurdie 338
    #
1329 dpurdie 339
    delete $self->{'InfoWs'};
340
    delete $self->{'InfoRepo'};
341
 
267 dpurdie 342
    $self->SvnCmd ( 'commit', $path
343
                    , '-m', SvnComment( $opt{'comment'}, 'Created by SvnCi' ),
344
                    , { 'credentials' => 1,
345
                        'process' => \&ProcessRevNo,
379 dpurdie 346
                        'error' => "SvnCi: Copy Error",
347
                         }
267 dpurdie 348
                        );
379 dpurdie 349
 
350
    #
351
    #   No error and no commit
352
    #   Workspace was not changed, may be allowed
353
    #
4076 dpurdie 354
    delete $self->{NoRepoChanges};
379 dpurdie 355
    if ( ! $self->{REVNO} && $opt{allowSame} )
356
    {
357
        Warning ("SvnCi: Workspace matches Repository. No commit");
358
        $self->{REVNO} = $ws_rev;
4076 dpurdie 359
        $self->{NoRepoChanges} = 1;
379 dpurdie 360
    }
361
 
267 dpurdie 362
    Error ("SvnCi: Cannot determine Revision Number", @{$self->{RESULT_LIST}})
363
        unless ( $self->{REVNO} );
364
 
365
    #
366
    #   Update the view
367
    #   Doing this so that the local view contains an up to date
368
    #   revision number. If not done, and a 'copy' is done based
369
    #   on this view then the branch information will indicate that
370
    #   the copy is based on an incorrect version.
371
    #   This can be confusing!
372
    #
373
    $self->SvnCmd ( 'update'   , $path
374
                        , '--ignore-externals'
375
                        , { 'credentials' => 1,
376
                            'error' => "SvnCi: Updating WorkSpace" }
377
                        );
378
    #
379
    #   Pass the updated revision number back to the user
380
    #
381
    $self->CalcRmReference($status_url);
379 dpurdie 382
    Message ("Commit Tag is: " . $self->{RMREF} );
267 dpurdie 383
    return $self->{RMREF} ;
384
}
385
 
386
#-------------------------------------------------------------------------------
387
# Function        : SvnCreatePackage
388
#
389
# Description     : Create a package and any associated files
390
#
391
# Inputs          : $self        - Instance data
392
#                   A hash of named arguments
1403 dpurdie 393
#                       package     - Name of the package
394
#                                     May include subdirs
395
#                       new         - True: Must not already exist
396
#                       replace     - True: Replace targets
397
#                       import      - DirTree to import
398
#                       label       - Tag for imported DirTree
399
#                       type        - Import TTB target
400
#                       printdata   - True: Print extracted files (default)
267 dpurdie 401
#
1403 dpurdie 402
#
267 dpurdie 403
# Returns         : Revision of the copy
404
#
405
sub SvnCreatePackage
406
{
407
    my $self = shift;
408
    my %opt = @_;
409
    my $target;
410
 
411
    Debug ("SvnCreatePackage", @_);
412
    Error ("Odd number of args to SvnCreatePackage") unless ((@_ % 2) == 0);
413
    my %dirs = ( 'trunk/'       => 0,
414
                 'tags/'        => 0,
415
                 'branches/'    => 0 );
416
 
417
    #
1403 dpurdie 418
    #   Sanity Tests and defaul values
267 dpurdie 419
    #
420
    my $package = $self->Full || Error ("SvnCreatePackage: No package name provided");
421
    Error ("SvnCreatePackage: Invalid import path") if ( $opt{'import'} && ! -d $opt{'import'} );
422
    Error ("SvnCreatePackage: Tag without Import") if ( $opt{'label'} && ! $opt{'import'} );
423
    $opt{'label'} = SvnIsaSimpleLabel( $opt{'label'} ) if (  $opt{'label'} );
1403 dpurdie 424
    $opt{'printdata'} = 1 unless ( exists $opt{'printdata'} );
267 dpurdie 425
 
426
    #
427
    #   Package path cannot contain any of the keyword paths tags,trunk,branches
428
    #   as this would place a package with a package
429
    #
430
    Error ("Package path contains a reserved word ($1)", "Path: $package")
431
        if (  $package =~ m~/(tags|branches|trunk)(/|$)~ );
432
 
433
    #
434
    #   Package path cannot be pegged, or look like one
435
    #
436
    Error ("Package name contains a Peg ($1)", "Path: $package")
437
        if ( $package =~ m~.*(@\d+)$~ );
438
 
439
    #
440
    #   Determine TTB target
1403 dpurdie 441
    #   The TTB type for branches and tags also conatins the branch or tag
267 dpurdie 442
    #
443
    $opt{'type'} = 'trunk' unless ( $opt{'type'} );
1403 dpurdie 444
    if ( $opt{'type'} =~ m~^(tags|branches|trunk)(/|$)(.*)~ ) {
445
        Error ("SvnCreatePackage: TTB type ($1) must be followed by a path element")
446
            if ( (($1 eq 'tags') or ($1 eq 'branches' )) && ! $3  );
447
        Error ('SvnCreatePackage: TTB type of trunk must not be followed by a path element: ' . $opt{'type'})
448
            if ( ($1 eq 'trunk') && $3  );
449
    } else {
450
        Error ("SvnCreatePackage: Invalid TTB Type: " . $opt{'type'} );
451
    }
267 dpurdie 452
 
453
    #
454
    #   Before we import data we must ensure that the targets do not exist
455
    #   Determine the import target(s)
456
    #
457
    my $import_target;
458
    my $copy_target;
459
 
1403 dpurdie 460
    $self->{DEVBRANCH} = 'trunk';
267 dpurdie 461
    if ( $opt{'import'} )
462
    {
463
        #
464
        #   Primary target
465
        #   trunk, branck or tag
466
        #
467
        $import_target = $package . '/' . $opt{'type'};
1403 dpurdie 468
        $self->{DEVBRANCH} = $opt{'type'} ;
267 dpurdie 469
 
470
        $self->SvnValidateTarget( 'target'    => $import_target,
471
                                  'delete'    => $opt{'replace'},
472
                                  'available' => 1 );
473
 
474
        #
475
        #   Secondary target
1403 dpurdie 476
        #   Are we tagging the import too
267 dpurdie 477
        #
1403 dpurdie 478
        if ( $opt{'label'} )
267 dpurdie 479
        {
480
            $copy_target = $package . '/tags/' . $opt{'label'};
481
            $self->SvnValidateTarget( 'target'    => $copy_target,
482
                                      'delete'    => $opt{'replace'},
483
                                      'available' => 1 );
484
        }
485
    }
486
 
487
    #
488
    #   Probe to see if the package exists
489
    #
490
    my ( $ref_files, $ref_dirs, $ref_svn, $found ) = $self->SvnScanPath ( 'SvnCreatePackage', $package );
491
    if ( @$ref_dirs )
492
    {
493
        Error ("SvnCreatePackage: Package directory exists",
494
               "Cannot create a package here. Unexpected subdirectories:", @$ref_dirs);
495
    }
496
 
497
    if ( @$ref_files )
498
    {
499
        Warning ("SvnCreatePackage: Unexpected files found",
500
               "Path: $package",
501
               "Unexpected files found: @$ref_files");
502
    }
503
 
4094 dpurdie 504
    #
505
    #   Check sanity of the users source directory - if importing
506
    #   The following directories are not allowed
507
    #       .svn - attempting to import an svn workspace
508
    #       tags, trunk, branches - attempt to import directory with reserved names
509
    #       .git, .hg, .cvs  - other  version control systems
510
    #
511
    if ( $import_target )
512
    {
513
        my $search = JatsLocateFiles->new("--Recurse=1",
514
                                           "--DirsOnly", 
515
                                           "--FullPath",
516
                                           "--FilterIn=.svn",
517
                                           "--FilterIn=.git",
518
                                           "--FilterIn=.hg",
519
                                           "--FilterIn=.cvs",
520
                                           "--FilterIn=tags",
521
                                           "--FilterIn=trunk",
522
                                           "--FilterIn=branches",
523
                                           );
524
        my @badDirs = $search->search($opt{'import'});
525
        if (@badDirs)
526
        {
527
            Error("SvnCreatePackage: Invalid directories found within imported source tree:", @badDirs);
528
        }
529
    }
530
 
267 dpurdie 531
    if ( @$ref_svn )
532
    {
533
        #
534
        #   Do we need a new one
535
        #
536
        Error ("SvnCreatePackage: Package exists: $package") if $opt{'new'};
537
 
538
        #
539
        #   Some subversion files have been found here
540
        #   Create the rest
541
        #   Assume that the directory tree is good
542
        #
543
        #
544
        #   Some, or all, of the required package subdirectories exist
545
        #   Determine the new ones to created so that it can be done
546
        #   in an atomic step
547
        #
548
        delete $dirs{$_} foreach  ( @$ref_svn );
549
        if ( keys %dirs )
550
        {
551
            Warning ("SvnCreatePackage: Not all package subdirs present",
552
                     "Remaining dirs will be created",
553
                     "Found: @$ref_svn") if @$ref_svn;
554
        }
555
        else
556
        {
557
            Warning ("SvnCreatePackage: Package already present");
558
        }
559
    }
560
    #
561
    #   Create package directories that have not been discovered
562
    #       trunk
563
    #       branches
564
    #       tags
565
    #
566
    my @dirs;
567
    push @dirs, $package . '/' . $_ foreach ( keys %dirs );
568
    $target = $package . '/trunk';
569
 
570
    #
571
    #   Create missing directories - if any
572
    #
573
    if ( @dirs )
574
    {
575
        $self->SvnCmd ('mkdir', @dirs
379 dpurdie 576
                       , '-m', $self->Path() . ': Created by SvnCreatePackage'
267 dpurdie 577
                       , '--parents'
385 dpurdie 578
                       , { 'credentials' => 1
579
                           ,'error' => "SvnCreatePackage"
580
                           ,'process' => \&ProcessRevNo
581
                         } );
267 dpurdie 582
    }
583
 
584
    #
585
    #   Import data into the package if required
586
    #   Import data. Possible cases:
587
    #       - Import to trunk - and then tag it
588
    #       - Import to branches
589
    #       - Import to tags
590
    #
591
    if ( $import_target )
592
    {
593
        Verbose ("Importing directory into new package: $opt{'import'}");
594
 
595
        $target = $import_target;
1403 dpurdie 596
        $self->{PRINTDATA} = $opt{'printdata'};
267 dpurdie 597
        $self->SvnCmd ('import', $opt{'import'}
598
                        , $target
599
                        , '-m', 'Import by SvnCreatePackage'
600
                        , '--force'
601
                        , { 'credentials' => 1
602
                           ,'error' => "Import Incomplete"
603
                           ,'process' => \&ProcessRevNo
1403 dpurdie 604
                           ,'printdata' => $opt{'printdata'}
267 dpurdie 605
                          })
606
    }
607
 
608
    #
609
    #   If imported to the trunk AND a label is provided
610
    #   then tag the import as well.
611
    #   A simple URL copy
612
    #
613
    if ( $copy_target )
614
    {
615
        Verbose ("Labeling imported trunk: $opt{'label'} ");
616
        $target = $copy_target;
617
        $self->SvnCmd ('copy'  , $import_target
618
                        , $target
619
                        , '-m', 'Import tagged by SvnCreatePackage'
620
                        , { 'credentials' => 1
621
                          , 'process' => \&ProcessRevNo
622
                          , 'error' => "Import Incomplete" } );
623
    }
624
 
625
    #
1403 dpurdie 626
    #   If we have done very little then we won't know the version
627
    #   of the repo. Need to force it
628
    #
629
    unless ( $self->{REVNO} || $self->{WSREVNO} )
630
    {
631
        $self->SvnInfo( $package, 'InfoRepo' );
632
        $self->{REVNO}  = $self->{'InfoRepo'}{'Last Changed Rev'} || Error ("SvnCreatePackage: Bad info for Repository");
633
    }
634
 
635
 
636
    #
267 dpurdie 637
    #   Pass the updated revision number back to the user
638
    #
639
    $self->CalcRmReference($target);
1403 dpurdie 640
    Message ("Create Package Rm Ref : " . $self->RmRef);
641
    Message ("Create Package Vcs Tag: " . $self->SvnTag);
267 dpurdie 642
    return $self->{RMREF} ;
643
}
644
 
645
#-------------------------------------------------------------------------------
646
# Function        : SvnRmView
647
#
648
# Description     : Remove a Subversion view
649
#                   Will run sanity checks and only remove the view if
650
#                   all is well
651
#
652
# Inputs          : A hash of named arguments
653
#                       path     - Path to local workspace
654
#                       modified - Array of files that are allowed to be modified
655
#                       force    - True: Force deletion
656
#
657
# Returns         :
658
#
659
sub SvnRmView
660
{
661
    my %opt = @_;
662
    Debug ("SvnRmView");
663
    Error ("Odd number of args to SvnRmView") unless ((@_ % 2) == 0);
664
 
665
    #
666
    #   Sanity test
667
    #
668
    my $path = $opt{'path'} || '';
669
    my $path_length = length ($path);
670
    Verbose2 ("Delete WorkSpace: $path");
671
 
672
    #
673
    #   If the path does not exist then assume that its already deleted
674
    #
675
    unless ( $path && -e $path )
676
    {
677
        Verbose2 ("SvnRmView: Path does not exist");
678
        return;
679
    }
680
 
681
    #
682
    #   Create information about the workspace
683
    #   This will also validate the path
684
    #
361 dpurdie 685
    my $session = NewSessionByWS ( $path, 0, 1 );
267 dpurdie 686
 
687
    #
688
    #   Validate the path
689
    #
690
    $session->SvnValidateWs ($path, 'SvnRmView');
691
 
692
    #
693
    #   Ask subversion if there are any files to be updated
694
    #   Prevent deletion of a view that has modified files
695
    #
696
    unless ( $opt{'force'} )
697
    {
698
        $session->SvnWsModified ( 'cmd' => 'SvnRmView', %opt );
699
    }
700
 
701
    #
702
    #   Now we can delete it
703
    #
704
    Verbose2 ("Remove WorkSpace: $path");
705
    rmtree( $path, IsVerbose(3) );
706
}
707
 
708
 
709
#-------------------------------------------------------------------------------
710
# Function        : SvnCopyWs
711
#
712
# Description     : Copy a workspace to a new place in the repository
713
#                   Effectively 'label' the workspace
714
#
715
#                   It would appear that the 'copy' command is very clever
716
#                   If a version-controlled file has been changed
717
#                   in the source workspace, then it will automatically be
718
#                   copied. This is a trap.
719
#
720
#                   Only allow a 'copy' if there are no modified
721
#                   files in the work space (unless overridden)
722
#
1329 dpurdie 723
#                   Only allow a 'copy' if the local workspace is
724
#                   up to date with respect with the repo. It possible
725
#                   to do a 'commit' and then a 'copy' (tag) and have
726
#                   unexpected results as the workspace has not been
727
#                   updated. This is a trap.
267 dpurdie 728
#
1329 dpurdie 729
#
267 dpurdie 730
# Inputs          : $self        - Instance data
731
#                   A hash of named arguments
732
#                       path     - Path to local workspace
733
#                       target   - Location within the repository to copy to
734
#                       comment  - Commit comment
735
#                       modified - Array of files that are allowed to
736
#                                  be modified in the workspace.
385 dpurdie 737
#                       noswitch        - True: Don't switch to the new URL
738
#                       replace         - True: Delete existing tag if present
739
#                       allowLocalMods  - True: Allow complex tagging
1403 dpurdie 740
#                       noupdatecheck   - True: Do not check that the WS is up to date
267 dpurdie 741
#
742
# Returns         : Revision of the copy
743
#
744
sub SvnCopyWs
745
{
746
    my $self = shift;
747
    my %opt = @_;
385 dpurdie 748
    my $rv;
267 dpurdie 749
    Debug ("SvnCopyWs");
750
    Error ("Odd number of args to SvnCopyWs") unless ((@_ % 2) == 0);
751
    Error ("SvnCopyWs: No Workspace" ) unless ( $self->{WS} );
752
 
753
    #
754
    #   Insert defaults
755
    #
756
    my $target = $opt{target} || Error ("SvnCopyWs: Target not specified" );
757
 
758
    #
759
    #   Validate the source path
760
    #
761
    my $path = SvnValidateWs ($self, 'SvnCopyWs');
762
 
763
    #
764
    #   Validate the target
765
    #   Cannot have a 'peg'
766
    #
767
    Error ("SvnCopyWs: Target contains a Peg: ($1)", $target)
768
        if ( $target =~ m~(@\d+)\s*$~ );
769
 
770
    #
1329 dpurdie 771
    #   Ensure the Workspace is up to date
772
    #       Determine the state of the Repo and the Workspace
773
    #
1403 dpurdie 774
    unless ( $opt{noupdatecheck} )
775
    {
776
        $self->SvnInfo( $self->{WS} , 'InfoWs' );
777
        $self->SvnInfo( $self->FullWs, 'InfoRepo' );
1329 dpurdie 778
 
1403 dpurdie 779
        my $wsLastChangedRev = $self->{'InfoWs'}{'Last Changed Rev'} || Error ("SvnCopyWs: Bad info for Workspace");
780
        my $repoLastChangedRev = $self->{'InfoRepo'}{'Last Changed Rev'} || Error ("SvnCopyWs: Bad info for Repository");
1329 dpurdie 781
 
1403 dpurdie 782
        Verbose("WS Rev  : $wsLastChangedRev");
783
        Verbose("Repo Rev: $repoLastChangedRev");
784
        Error ('SvnCopyWs: The repository has been modified since the workspace was last updated.',
785
               'Possibly caused by a commit without an update.',
786
               'Update the workspace and try again.',
787
               "Last Changed Rev. Repo: $repoLastChangedRev. Ws:$wsLastChangedRev") if ( $repoLastChangedRev > $wsLastChangedRev );
788
    }
1329 dpurdie 789
 
790
    #
267 dpurdie 791
    #   Examine the workspace and ensure that there are no modified
792
    #   files - unless they are expected
793
    #
794
    $self->SvnWsModified ( 'cmd' => 'SvnCopyWs', %opt );
385 dpurdie 795
 
267 dpurdie 796
    #
797
    #   Validate the repository
798
    #   Ensure that the target does not exist
799
    #   The target may be deleted if it exists and allowed by the user
800
    #
801
    $self->SvnValidateTarget ( 'cmd'    => 'SvnCopyWs',
802
                        'target' => $target,
803
                        'delete' => $opt{replace},
804
                        'comment' => 'Deleted by SvnCopyWs'
805
                        );
369 dpurdie 806
 
267 dpurdie 807
    #
808
    #   Copy source to destination
1329 dpurdie 809
    #   Assuming the WorkSpace is up to date then, even though the source is a
810
    #   WorkSpace, the copy does not transfer data from the WorkSpace.
811
    #   It appears as though its all done on the server. This is good - and fast.
267 dpurdie 812
    #
1329 dpurdie 813
    #   If the Workspace is not up to date, then files that SVN thinks have not
814
    #   been transferred will be transferred - hence the need to update after
815
    #   a commit.
816
    #
385 dpurdie 817
    #   Moreover, files that are modified in the local workspace will
818
    #   be copied and checked into the target, but this is not nice.
267 dpurdie 819
    #
385 dpurdie 820
    $rv = $self->SvnCmd ( 'cp'  , $path
267 dpurdie 821
                        , $target
822
                        , '--parents'
823
                        , '-m', SvnComment( $opt{'comment'}, 'Created by SvnCopyWs' ),
824
                        , { 'process' => \&ProcessRevNo,
1403 dpurdie 825
                            'credentials' => 1,
826
                            'printdata' => 1,
827
                             }
385 dpurdie 828
                        );
829
    if ($rv)
267 dpurdie 830
    {
831
        #
832
        #   Error in copy
833
        #   Attempt to delete the target. Don't worry if we can't do that
834
        #
835
        my @err1 = @{$self->{ERROR_LIST}};
836
        $self->SvnCmd ( 'delete'
837
                    , $target
838
                    , '-m', 'Deleted by SvnCopyWs after creation failure'
839
                    , { 'credentials' => 1, }
840
               );
841
        Error ("SvnCopyWs: Copy Error", @err1);
842
    }
843
 
844
    Error ("SvnCopyWs: Cannot determine Revision Number", @{$self->{RESULT_LIST}})
845
        unless ( $self->{REVNO} );
846
 
847
    Verbose2 ("Copy committed as revision: " . $self->{REVNO} );
848
 
849
    unless ( $opt{'noswitch'} )
850
    {
851
        #
852
        #   Switch to the new URL
853
        #   This will link the Workspace with the copy that we have just made
854
        #
855
        $self->SvnCmd ( 'switch', $target
856
                         , $path
857
                         , { 'credentials' => 1,
858
                             'error' => "SvnCopyWs: Cannot switch to new URL" }
859
               );
860
    }
861
 
862
    #
863
    #   Pass the updated revision number back to the user
864
    #
1403 dpurdie 865
    $self->CalcRmReference($target);
353 dpurdie 866
    #Message ("Tag is: " . $self->{RMREF} );
267 dpurdie 867
    return $self->{RMREF} ;
868
}
869
 
870
#-------------------------------------------------------------------------------
871
# Function        : SvnWsModified
872
#
873
# Description     : Test a Workspace for modified files
874
#                   Allow some files to be modified
875
#
876
# Inputs          : $self           - Instance data
877
#                   A hash of named arguments
1431 dpurdie 878
#                       path            - Path to local workspace
879
#                       modifiedRoot    - Alternate base for files
880
#                       modified        - Files that are allowed to be modified
881
#                                         Relative to the 'path' or 'modifiedRoot'
882
#                                         May be a single file or an array of files
883
#                       allowLocalMods  - Only warn about local mods
884
#                       cmd             - Command name for error reporting
267 dpurdie 885
#
886
# Returns         :
887
#
888
sub SvnWsModified
889
{
890
    my $self = shift;
891
    my %opt = @_;
892
    Debug ("SvnWsModified");
893
    Error ("Odd number of args to SvnWsModified") unless ((@_ % 2) == 0);
894
 
895
    my $cmd = $opt{'cmd'} || 'SvnWsModified';
896
 
897
    #
898
    #   Validate the path
899
    #
900
    SvnValidateWs ($self, $cmd);
901
    my $path = $self->{WS};
1431 dpurdie 902
    my $modifiedRoot = $opt{'modifiedRoot'} || $path;
903
    my $path_length = length ($modifiedRoot);
267 dpurdie 904
    Verbose2 ("Test Workspace for Modifications: $path");
905
 
906
    #
907
    #   Ask subversion if there are any files to be updated
908
    #
909
    $self->SvnCmd ('status', $path, {'error' => "Svn status command error"} );
910
 
911
    #
912
    #   Examine the list of modified files
913
    #
914
    if ( @{$self->{RESULT_LIST}} )
915
    {
916
        #
351 dpurdie 917
        #   Create a hash of files that are allowed to change
267 dpurdie 918
        #   These are files relative to the base of the view
919
        #
920
        #   The svn command has the 'path' prepended, so this
921
        #   will be removed as we process the commands
922
        #
923
        my %allowed;
924
        my @unexpected;
925
 
926
        if ( exists $opt{'modified'}  )
927
        {
928
            $allowed{'/' . $_} = 1 foreach ( ref ($opt{'modified'}) ? @{$opt{'modified'}} : $opt{'modified'}  );
929
        }
930
 
931
        #
932
        #   Process the list of modified files
933
        #   Do this even if we aren't allowed modified files as we
934
        #   still need to examine the status and kill off junk entries
935
        #   ie: ?, I, ! and ~
936
        #
937
        #    First column: Says if item was added, deleted, or otherwise changed
938
        #      ' ' no modifications
939
        #      'A' Added
940
        #      'C' Conflicted
941
        #      'D' Deleted
942
        #      'I' Ignored
943
        #      'M' Modified
944
        #      'R' Replaced
945
        #      'X' item is unversioned, but is used by an externals definition
946
        #      '?' item is not under version control
947
        #      '!' item is missing (removed by non-svn command) or incomplete
948
        #      '~' versioned item obstructed by some item of a different kind
949
        #
950
        foreach my $entry ( @{$self->{RESULT_LIST}} )
951
        {
952
            #
953
            #   Extract filename from line
351 dpurdie 954
            #       First 8 chars are status
267 dpurdie 955
            #       Remove WS path too
956
            #
1329 dpurdie 957
            if ( length $entry >= 8 + $path_length)
958
            {
959
                my $file = substr ( $entry, 8 + $path_length );
960
                next if ( $allowed{$file} );
961
            }
267 dpurdie 962
 
963
            #
964
            #   Examine the first char and rule out funny things
965
            #
966
            my $f1 =  substr ($entry, 0,1 );
967
            next if ( $f1 =~ m{[?I!~]} );
968
            push @unexpected, $entry;
969
        }
385 dpurdie 970
 
971
        if ( @unexpected )
972
        {
973
            if ( $opt{allowLocalMods} ) {
974
                Message ("Workspace contains locally modified files:", @unexpected);
975
            } else {
976
                Error ("Workspace contains unexpected modified files", @unexpected);
977
            }
978
        }
267 dpurdie 979
    }
980
}
981
 
982
#-------------------------------------------------------------------------------
983
# Function        : SvnListPackages
984
#
985
# Description     : Determine a list of packages within the repo
986
#                   This turns out to be a very slow process
987
#                   so don't use it unless you really really need to
988
#
1403 dpurdie 989
# Inputs          : $self       - Instance data
990
#                   $repo       - Name of the repository
991
#                   Last argument may be a hash of options.
992
#                           Progress    - True: Show progress
993
#                           Show        - >1 : display matched Tags and stats
994
#                                         >2 : display Packages
995
#                           Tag         - Enable Tag Matching
996
#                                         Value is the tag to match
267 dpurdie 997
#
1403 dpurdie 998
# Returns         : Ref to an array of all packages
999
#                   Ref to an array of all packahes with matched tag
267 dpurdie 1000
#
1001
sub SvnListPackages
1002
{
1403 dpurdie 1003
    #
1004
    #   Extract arguments and options
1005
    #   If last argument is a hesh, then its a hash of options
1006
    #
1007
    my $opt;
1008
    $opt = pop @_
1009
        if (@_ > 0 and UNIVERSAL::isa($_[-1],'HASH'));
267 dpurdie 1010
 
1403 dpurdie 1011
    my ($self, $repo) = @_;
1012
 
1013
    my @path_list = '';
267 dpurdie 1014
    my @list;
1403 dpurdie 1015
    my @mlist;
267 dpurdie 1016
    my $scanned = 0;
1017
    Debug ("SvnListPackages");
1018
    while ( @path_list )
1019
    {
1020
        my $path = shift @path_list;
1403 dpurdie 1021
        if ( $opt->{Progress} )
1022
        {
1023
            Message ("Reading: " . ( $path || 'RepoRoot') );
1024
        }
267 dpurdie 1025
        $scanned++;
1403 dpurdie 1026
        my ( $ref_files, $ref_dirs, $ref_svn, $found ) = $self->SvnScanPath ( 'Listing Packages', join( '/', $repo, $path) );
267 dpurdie 1027
 
1028
        #
1029
        #   If there are Subversion dirs (ttb) in this directory
1030
        #   then this is a package. Add to list
1031
        #
1032
        push @list, $path if ( @$ref_svn );
1033
 
1034
        #
1035
        #   Add subdirs to the list of paths to explore
1036
        #
1037
        foreach  ( @$ref_dirs )
1038
        {
1403 dpurdie 1039
            chop;                                   # Remove trailing '/'
1040
            push @path_list, $path ? join('/', $path , $_) : $_; # Extend the path
267 dpurdie 1041
        }
1042
    }
1043
 
1403 dpurdie 1044
    if ( $opt->{Tag} )
1045
    {
1046
        my $tag = $opt->{Tag};
1047
        foreach my $path ( sort @list )
1048
        {
1049
            Message ("Testing: $path") if ( $opt->{Progress} );
1050
            if ( $self->SvnTestPath ( 'Listing Packages', join('/', $repo, $path, 'tags', $tag) ) )
1051
            {
1052
                push @mlist, $path;
1053
            }
1054
        }
1055
    }
1056
 
1057
    if ( $opt->{Show} )
1058
    {
1059
        Message ("Found Tags:", @mlist );
1060
        Message ("Found Packages:", @list ) if  $opt->{Show} > 2;
1061
        Message ("Tags Found: " . scalar @mlist );
1062
        Message ("Packages Found: " . scalar @list );
1063
        Message ("Dirs Scanned: $scanned");
1064
    }
1065
 
1066
    return \@list, \@mlist;
267 dpurdie 1067
}
1068
 
1069
#-------------------------------------------------------------------------------
1070
# Function        : ListLabels
1071
#
1072
# Description     : List labels within a given package
1073
#
1074
# Inputs          : $self               - Instance data
1075
#                   $path               - path to label source
1076
#
1077
# Returns         : Ref to an array
1078
#
1079
sub ListLabels
1080
{
1081
    my ($self, $path) = @_;
1082
    Debug ("ListLabels");
1083
 
1084
    my ( $ref_files, $ref_dirs, $ref_svn, $found ) = $self->SvnScanPath ( 'Listing Versions', $path );
1085
 
1086
    Error ("List: Path not found: $path") unless ( $found );
1087
 
1088
    #
1089
    #   Dont report files - just directories
1090
    #
1091
    return $ref_dirs;
1092
}
1093
 
1094
 
1095
#-------------------------------------------------------------------------------
1096
# Function        : SvnLocateWsRoot
1097
#
1098
# Description     : Given a WorkSpace, determine the root of the work space
1099
#                   This is not as simple as you might think
1100
#
1101
#                   Algorithm
1102
#                       svn ls ..
1103
#                       Am I in the parent directory
1104
#                       Repeat
1105
#
369 dpurdie 1106
#                   Updates 'WS' and 'WSURL'
1107
#
267 dpurdie 1108
# Inputs          : $self               - Instance data
1109
#                   $test               - True: Don't die on error
1110
#
1111
# Returns         : Root of workspace as an absolute address
1112
#                   Will not return if there is an error
1113
#
1114
sub SvnLocateWsRoot
1115
{
1116
    my ($self, $test) = @_;
1117
    my @path;
1118
    my $path = $self->{WS};
1403 dpurdie 1119
    my $found;
267 dpurdie 1120
 
1121
    Debug ("SvnLocateWsRoot");
1122
    Error ("SvnLocateWsRoot: No Workspace") unless ( $path  );
1123
    Verbose2 ("SvnLocateWsRoot: Start in $path");
1124
 
1125
    #
1126
    #   Validate the source path
1127
    #
1128
    if ( SvnValidateWs ($self, 'SvnLocateWsRoot', $test) )
1129
    {
1130
        return undef;
1131
    }
1132
 
1133
    #
1403 dpurdie 1134
    #   Under Subversion 1.7 the process is a lot easier
267 dpurdie 1135
    #
1403 dpurdie 1136
    if ( exists $self->{'InfoWs'}{'Working Copy Root Path'} )
1137
    {
1138
        #
1139
        #   WS is now known
1140
        #
1141
        $self->{WS} = $self->{'InfoWs'}{'Working Copy Root Path'};
267 dpurdie 1142
 
1403 dpurdie 1143
        #
1144
        #   Calculate WSURL
1145
        #
2049 dpurdie 1146
        $self->{WSURL} = join('/', $self->{PKGROOT}, $self->{DEVBRANCH})
1147
            if ($self->{DEVBRANCH});
1403 dpurdie 1148
        $found = 1;
1149
    }
1150
    else
267 dpurdie 1151
    {
1403 dpurdie 1152
        # Preversion 1.7
1153
        Warning ("Using svn < 1.7. This is not recommended");
267 dpurdie 1154
 
1403 dpurdie 1155
        #
1156
        #   Need to sanitize the users path to ensure that the following
1157
        #   algorithm works. Need:
1158
        #       1) Absolute Path
1159
        #       2) Not ending in '/'
1160
        #
369 dpurdie 1161
 
267 dpurdie 1162
        #
1403 dpurdie 1163
        #   If we have a relative path then prepend the current directory
1164
        #   An absolute path is:
1165
        #           /aaa/aa/aa
1166
        #       or  c:/aaa/aa/aa
267 dpurdie 1167
        #
1403 dpurdie 1168
        $path = getcwd() . '/' . $path
1169
            unless ( $path =~ m~^/|\w:/~  );
267 dpurdie 1170
 
1171
        #
1403 dpurdie 1172
        #   Walk the bits and remove ".." directories
1173
        #       Done by pushing non-.. elements and poping last entry for .. elements.
1174
        #   Have a leading "/" which is good.
267 dpurdie 1175
        #
1403 dpurdie 1176
        #   Create a array of directories in the path
1177
        #   Split on one or more \ or / separators
267 dpurdie 1178
        #
1403 dpurdie 1179
        foreach ( split /[\\\/]+/ , $path )
267 dpurdie 1180
        {
1403 dpurdie 1181
            next if ( $_ eq '.' );
1182
            unless ( $_ eq '..' )
1183
            {
1184
                push @path, $_;
1185
            }
1186
            else
1187
            {
1188
                Error ("SvnLocateWsRoot: Bad Pathname: $path")
1189
                    if ( $#path <= 0 );
1190
                pop @path;
1191
            }
267 dpurdie 1192
        }
1193
 
1194
        #
1403 dpurdie 1195
        #   Need to adjust the WSURL too
1196
        #   Break into parts and pop them off as we go
1197
        #   Add a dummy one to allow for the first iteration
267 dpurdie 1198
        #
1403 dpurdie 1199
        my @wsurl = (split (/[\\\/]+/ , $self->{WSURL}), 'Dummy');
369 dpurdie 1200
 
1403 dpurdie 1201
        Verbose2 ("Clean absolute path elements: @path");
1202
        PATH_LOOP:
1203
        while ( @path )
1204
        {
1205
            #
1206
            #   This directory element. Append / to assist in compare
1207
            #   Determine parent path
1208
            #
1209
            my $name = pop (@path) . '/';
1210
            my $parent = join ('/', @path );
1211
            pop @wsurl;
369 dpurdie 1212
 
1403 dpurdie 1213
            #
1214
            #   Examine the parent directory
1215
            #   Get a list of all elements in the parent
1216
            #   Need to ensure that this directory is one of them
1217
            #
1218
            #   Ignore any errors - assume that they are because the
1219
            #   parent is not a part of the work space. This will terminate the
1220
            #   search.
1221
            #
1222
            $self->SvnCmd ('list', $parent, '--depth', 'immediates' );
1223
            foreach my $entry ( @{$self->{RESULT_LIST}} )
1224
            {
1225
                next PATH_LOOP
1226
                    if ( $entry eq $name );
1227
            }
1228
 
1229
            #
1230
            #   Didn't find 'dir' in directory svn listing of parent
1231
            #   This parent is not a part of the same WorkSpace as 'dir'
1232
            #   We have a winner.
1233
            #
1234
            chop $name;                         #   Chop the '/' previously added
1235
            $self->{WS} = $parent . '/' . $name;
1236
 
1237
            #
1238
            #   Reform the WSURL. Elements have been removed as we tested up the
1239
            #   path
1240
            #
1241
            $self->{WSURL} = join '/', @wsurl;
1242
            $found = 1;
1243
            last;
1244
        }
267 dpurdie 1245
    }
1246
 
1247
    #
1248
    #   Shouldn't get this far
1249
    #
1403 dpurdie 1250
    Error ("SvnLocateWsRoot: Root not found")
1251
        unless ( $found );
1252
 
1253
    #
1254
    #   Refresh Info
1255
    #   Must kill cached copy
1256
    #
1257
    delete $self->{'InfoWs'};
1258
    $self->SvnInfo($self->{WS}, 'InfoWs');
1259
    return $self->{WS};
1260
 
267 dpurdie 1261
}
1262
 
1263
#-------------------------------------------------------------------------------
1264
# Function        : SvnValidateWs
1265
#
1266
# Description     : Validate the path to a working store
1267
#
1268
# Inputs          : $self           - Instance data
1269
#                   $user           - Optional prefix for error messages
1270
#                   $test           - True: Just test, Else Error
1271
#
1272
# Returns         : Will not return if not a workspace
1273
#                   Returns the users path
1329 dpurdie 1274
#                   Populates the hash: $self->{InfoWs}
267 dpurdie 1275
#
1276
sub SvnValidateWs
1277
{
1278
    my ($self, $user, $test) = @_;
1279
    Debug ("SvnValidateWs");
1280
 
1281
    $user = "Invalid Subversion Workspace" unless ( $user );
1329 dpurdie 1282
    my $path = $self->{WS};
267 dpurdie 1283
 
1284
    #
1329 dpurdie 1285
    #   Only validate it once
267 dpurdie 1286
    #
1287
    return $path if ( $self->{WS_VALIDATED} );
1288
 
1289
    #
1290
    #   Validate the source path
1291
    #   Must exist and must be a directory
1292
    #
1293
    if ( ! $path ) {
1294
        @{$self->{ERROR_LIST}} = "$user: No path specified";
1295
 
1296
    } elsif ( ! -e $path ) {
1297
        @{$self->{ERROR_LIST}} = "$user: Path does not exist: $path";
1298
 
1299
    } elsif ( ! -d $path ) {
1300
        @{$self->{ERROR_LIST}} = "$user: Path is not a directory";
1301
    } else {
1302
        #
1303
        #   Determine the source path is an fact a view
1304
        #   The info command can do this. Use depth empty to limit the work done
1305
        #
1329 dpurdie 1306
        $self->SvnInfo($path, 'InfoWs');
267 dpurdie 1307
 
1308
        #
1309
        #   Error. Prepend nice message
1310
        #
1311
        unshift @{$self->{ERROR_LIST}}, "$user: Path is not a WorkSpace: $path"
1312
            if ( @{$self->{ERROR_LIST}} );
1313
    }
1314
 
1315
    #
1316
    #   Figure out what to do
1317
    #
1318
    if ( $test )
1319
    {
1320
        return @{$self->{ERROR_LIST}};
1321
    }
1322
    else
1323
    {
1324
        Error @{$self->{ERROR_LIST}} if @{$self->{ERROR_LIST}};
1325
        $self->{WS_VALIDATED} = 1;
1326
        return $path;
1327
    }
1328
}
1329
 
1330
#-------------------------------------------------------------------------------
1331
# Function        : SvnValidatePackageRoot
1332
#
1333
# Description     : Validate a package root
1334
#
1335
# Inputs          : $self           - Instance data
1336
#
1337
# Returns         : Will only return if valid
1338
#                   Returns a cleaned package root
1339
#
1340
sub SvnValidatePackageRoot
1341
{
379 dpurdie 1342
    my ($self, $warning_only) = @_;
267 dpurdie 1343
    Debug ("SvnValidatePackageRoot");
1344
    my $url = $self->Full || Error ("SvnValidatePackageRoot: No URL");
1345
 
1346
    Error ("Package path contains a reserved word ($self->{TAGTYPE})", "Path: $url")
1347
        if (  $self->{TAGTYPE} );
1348
 
1349
    Error ("Package name contains a Peg ($self->{PEG})", "Path: $url")
1350
        if ( $self->{PEG} );
1351
 
1352
    #
1353
    #   Ensure that the target path does exist
1354
    #   Moreover it needs to be a directory and it should have a
1355
    #   a ttb structure
1356
    #
1357
    my ( $ref_files, $ref_dirs, $ref_svn, $found ) = $self->SvnScanPath ( 'Package Base Test', $url );
1358
 
1359
    #
379 dpurdie 1360
    #   Only looking for package path
1361
    #
1362
    if ( !$found && $warning_only )
1363
    {
1364
        return $url;
1365
    }
1366
 
1367
    #
267 dpurdie 1368
    #   Examine the results to see if we have a valid package base
1369
    #
1370
    Error ("Package Base Test: Not a valid package") unless ( $found );
1371
 
1372
    #
1373
    #   Extra bits found
1374
    #   Its not the root of a package
1375
    #
1376
    if ( @$ref_files )
1377
    {
1378
        Warning ("Package Base Test: Files exists",
1379
               "Unexpected files found:", @$ref_files );
1380
    }
1381
 
1382
    #
1383
    #   Need a truck directory
1384
    #   If we don't have a truck we don't have a package
1385
    #
1386
    my $trunk_found = grep ( /trunk\//, @$ref_svn );
1387
    Error ("Invalid Package Base. Does not contain a 'trunk' directory")
1388
        unless ( $trunk_found );
1389
 
1390
    return $url;
1391
}
1392
 
1393
 
1394
#-------------------------------------------------------------------------------
1395
# Function        : SvnIsaSimpleLabel
1396
#
1397
# Description     : Check a label
1398
#                       Must not contain a PEG
1399
#                       Must not contain invalid characters (@ or /)
1400
#                       Must not contain a :: sequence (will confuse other tools)
1401
#                       Handle special label of TIMESTAMP
2429 dpurdie 1402
#                           Create a .WIP so that it can be deleted
267 dpurdie 1403
#
1404
# Inputs          : $label          - to test
1405
#
1406
# Returns         : Will not return on error
1407
#                   Returns label on success
1408
#
1409
sub SvnIsaSimpleLabel
1410
{
1411
    my ($label) = @_;
1412
    Debug ("SvnIsaSimpleLabel, $label");
1413
 
1414
    Error ("No label provided") unless ( $label );
1415
    Error ("Invalid label. Peg (\@nnn) is not allowed: \"$label\"" ) if ( $label =~ m~@\d+$~ );
1416
    Error ("Invalid label. Package Path is not allowed: \"$label\"" ) if ( $label =~ m~/~ );
383 dpurdie 1417
    Error ("Invalid label. Invalid Start Character: \"$label\"" ) unless ( $label =~ m~^[0-9a-zA-Z]~ );
1418
    Error ("Invalid label. Invalid End Character: \"$label\"" ) unless ( $label =~ m~[0-9a-zA-Z]$~ );
267 dpurdie 1419
    Error ("Invalid label. Invalid Characters: \"$label\"" ) unless ( $label =~ m~^[-.:0-9a-zA-Z_]+$~ );
1420
    Error ("Invalid label. Double :: not allowed: \"$label\"" ) if ( $label =~m~::~ );
1421
 
1422
    #
1423
    #   Allow for a label of TIMESTAMP and have it expand
383 dpurdie 1424
    #   Create a label based on users name and a date-time that can be sorted
267 dpurdie 1425
    #
1426
    if ( $label eq 'TIMESTAMP' )
1427
    {
341 dpurdie 1428
        ::EnvImport ('USER' );
1429
        my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
2429 dpurdie 1430
        $label = sprintf("%s_%4.4u.%2.2u.%2.2u.%2.2u%2.2u%2.2u.WIP",
341 dpurdie 1431
            $::USER, $year+1900, $mon+1, $mday, $hour, $min, $sec );
267 dpurdie 1432
    }
1433
    return $label;
1434
}
1435
 
1436
#-------------------------------------------------------------------------------
1437
# Function        : NewSession
1438
#
1439
# Description     : Create a new empty SvnSession Class
1440
#
1441
# Inputs          : None
1442
#
1443
# Returns         : Class
1444
#
1445
sub NewSession
1446
{
1447
    Debug ("NewSession");
1448
    my $self  = SvnSession();
1449
 
1450
    #
1451
    #   Document Class Variables
1452
    #
1453
    $self->{URL} = '';                  # Repo URL prefix
1454
    $self->{WS}  = '';                  # Users WorkSpace
1455
    $self->{PROTOCOL} = '';             # Named Access Protocol
1456
    $self->{PKGROOT} = '';              # Package root
1457
 
1458
    #
1459
    #   Create a class
1460
    #   Bless my self
1461
    #
1462
    bless ($self, __PACKAGE__);
1463
    return $self;
1464
}
1465
 
1466
#-------------------------------------------------------------------------------
1467
# Function        : NewSessionByWS
1468
#
1469
# Description     : Establish a new SVN Session based on a Workspace
1470
#                   Given a workspace path determine the SvnServer and other
1471
#                   relevent information.
1472
#
1473
#                   Requires some rules
1474
#                       * The package is rooted within a 'ttb'
1475
#
1476
# Inputs          : $path                   - Path to WorkSpace
1477
#                   $test                   - No Error on no WS
361 dpurdie 1478
#                   $slack                  - Less stringent
267 dpurdie 1479
#
1480
# Returns         : Ref to Session Information
1481
#
1482
sub NewSessionByWS
1483
{
361 dpurdie 1484
    my ($path, $test, $slack) = @_;
267 dpurdie 1485
    Debug ("NewSessionByWS", @_);
1486
 
1487
    #
1488
    #   Create a basic Session
1489
    #   Populate it with information that is known
1490
    #
1491
    my $self = NewSession();
1492
    $self->{WS} = $path;
1493
 
1494
    #
1495
    #   Validate the path provided
1329 dpurdie 1496
    #   In the process populate $self->{InfoWs} with info about the workspace.
267 dpurdie 1497
    #
1498
    if ($self->SvnValidateWs ( undef, 1) )
1499
    {
1500
        return $self if ( $test );
1501
        Error ( @{$self->{ERROR_LIST}} );
1502
    }
1503
 
1504
    #
1505
    #   Extract useful info
1506
    #       URL: svn://auperaws996vm21/test/MixedView/trunk
1507
    #       Repository Root: svn://auperaws996vm21/test
1508
    #
1329 dpurdie 1509
    my $url = $self->{'InfoWs'}{'URL'};
1510
    my $reporoot = $self->{'InfoWs'}{'Repository Root'};
1511
    my $repoVersion = $self->{'InfoWs'}{'Revision'};
1403 dpurdie 1512
    my $devBranch;
267 dpurdie 1513
 
1514
    Error ("JatsSvn Internal error. Can't parse info")
1515
        unless ( $url && $reporoot );
1516
 
1517
    #
1518
    #   Need the length of the path to the repository
1519
    #   but not the name of the repostory itself.
1520
    #
1521
    #   Remove that from the head of the URL to give a
1522
    #   path within the repository, that includes the repos name
1523
    #
1524
    $reporoot = (fileparse( $reporoot ))[1];
1525
    $url = substr ($url, length ($reporoot));
1526
    $self->{WSURL} = $url;
1527
    chop $reporoot;
1528
 
1529
    Verbose2 ("SvnLocatePackageRoot: $reporoot, $url" );
1530
 
1531
    #
1532
    #   Remove anything after a ttb ( truck, tags, branch ) element
1533
    #   This will be the root of the package within the repo
1534
    #
1535
    if (  $url =~ m~(.+)/((tags|branches|trunk)(/|$).*)~ )
1536
    {
1537
        $url = $1;
1538
        $self->{WSTYPE} = $3;
1403 dpurdie 1539
        if ( $3 eq 'trunk' ) {
1540
            $devBranch = $3;
1541
        } elsif ( $3 eq 'branches' ) {
2026 dpurdie 1542
            my @bits = split('/', $2);
1543
            $devBranch = join('/', @bits[0 .. 1]);
1403 dpurdie 1544
        }
267 dpurdie 1545
    }
1546
    else
1547
    {
361 dpurdie 1548
        #
1549
        #   If we are being slack (ie deleting the workspace)
1550
        #   Then generate a warning, not an error
1551
        #
1552
        my $fnc = $slack ? \&Warning : \&Error;
1553
        $fnc->("SvnLocatePackageRoot. Non standard repository format",
1554
               "Url must contain 'tags' or 'branches' or 'trunk'",
267 dpurdie 1555
               "Url: $url");
361 dpurdie 1556
        $self->{WSTYPE} = 'trunk';
267 dpurdie 1557
    }
1558
 
1559
    #
1560
    #   Insert known information
1561
    #
1562
    $self->{URL} = $reporoot . '/';
1563
    $self->{PKGROOT} = $url;
369 dpurdie 1564
    $self->{WSREVNO} = $repoVersion;
1403 dpurdie 1565
    $self->{DEVBRANCH} = $devBranch;
267 dpurdie 1566
 
1567
    #
1568
    #   Create useful information
1569
    #
1570
    SplitPackageUrl($self);
1571
    return $self;
1572
}
1573
 
1574
#-------------------------------------------------------------------------------
1575
# Function        : NewSessionByUrl
1576
#
1577
# Description     : Establish a new SVN Session based on a user URL
1578
#
1579
# Inputs          : $uurl                   - Users URL
361 dpurdie 1580
#                   $ttb_test               - Test and warn for TTB structure
267 dpurdie 1581
#                   $session                - Optional: Existing session
1582
#
1583
# Returns         : Ref to Session Information
1584
#
1585
sub NewSessionByUrl
1586
{
361 dpurdie 1587
    my ($uurl, $ttb_test, $self ) = @_;
267 dpurdie 1588
    Debug ("NewSessionByUrl", @_);
1589
    Error ("No Repostory Path specified") unless ( $uurl );
1590
 
1591
    #
1592
    #   Create a basic Session
1593
    #   Populate it with information that is known
1594
    #
1595
    $self = NewSession() unless ( $self );
1596
 
1597
    #
369 dpurdie 1598
    #   Examine the URL and convert a Repository Path into a URL
353 dpurdie 1599
    #   as provided by configuration information within the environment
267 dpurdie 1600
    #
361 dpurdie 1601
    ($self->{URL}, $self->{PKGROOT} ) = SvnPath2Url ($uurl);
1602
 
1603
    #
1604
    #   Create useful information
1605
    #
1606
    SplitPackageUrl($self);
1607
 
1608
    #
1609
    #   Warn of non-standard URLs
1610
    #   These may create problems latter
1611
    #
1612
    if ( $ttb_test )
1613
    {
1614
        Warning("Non standard repository format",
1615
                 "Url should contain 'tags' or 'branches' or 'trunk'",
1616
                 "Url: $self->{PKGROOT}") unless $self->{TAGTYPE};
1617
    }
1618
 
1619
    return $self;
1620
}
1621
 
1622
#-------------------------------------------------------------------------------
1623
# Function        : SvnPath2Url
1624
#
1625
# Description     : Convert a repository path to a Full Url
1626
#                   Also handles Full Url
1627
#
1628
# Inputs          : $rpath             - Repository Path
1629
#                                        May be a full URL
1630
#
1631
# Returns         : List context
1632
#                   Two items that can be joined
1633
#                   URL                - URL
1634
#                   PKG_ROOT           - Package Root
1635
#
1636
#                   Scalar context: Joined URL and Package Root
1637
#                                   Fully formed URL
1638
#
1639
sub SvnPath2Url
1640
{
1641
    my ($rpath) = @_;
1642
    my $processed = 0;
1643
    my $url;
1644
    my $pkgroot;
1645
 
1646
    #
1647
    #   Examine the argument and convert a Repository Path into a URL
1648
    #   as provided by configuration information within the environment
1649
    #
1650
    $rpath =~ m~(.+?)/(.*)~;
1651
    my $fe = $1 || $rpath;
353 dpurdie 1652
    my $rest = $2 || '';
1653
    if ( $SVN_URLS{$fe} )
267 dpurdie 1654
    {
361 dpurdie 1655
        $url = $SVN_URLS{$fe};
1656
        $pkgroot = $rest;
353 dpurdie 1657
        $processed = 1;
341 dpurdie 1658
    }
1659
 
353 dpurdie 1660
    if ( ! $processed )
341 dpurdie 1661
    {
267 dpurdie 1662
        #
353 dpurdie 1663
        #   Examine the URL and determine if we have a FULL Url or
1664
        #   a path within the 'default' server
1665
        #
1666
        foreach my $key ( @SVN_URLS_LIST )
1667
        {
361 dpurdie 1668
            if ( $rpath =~ m~^$SVN_URLS{$key}(.*)~ )
353 dpurdie 1669
            {
361 dpurdie 1670
                $url = $SVN_URLS{$key};
1671
                $pkgroot = $1;
353 dpurdie 1672
                $processed = 1;
1673
                last;
1674
            }
1675
        }
1676
    }
267 dpurdie 1677
 
353 dpurdie 1678
    #
1679
    #   Last attempt
1680
    #   Treat as a raw URL - some operations won't be allowed
1681
    #
1682
    if ( ! $processed )
267 dpurdie 1683
    {
361 dpurdie 1684
        if ( $rpath =~ m~^((file|http|https|svn):///?([^/]+)/)(.+)~ )
353 dpurdie 1685
        {
1686
            #       http://server/
1687
            #       https://server/
1688
            #       svn://server/
1689
            #       file://This/Isa/Bad/Guess
1690
            #
361 dpurdie 1691
            $url = $1;
1692
            $pkgroot = $4;
353 dpurdie 1693
        }
369 dpurdie 1694
        elsif ($SVN_URLS{''} )
353 dpurdie 1695
        {
1329 dpurdie 1696
            if ( exists $ENV{'GBE_ABT'} && $ENV{'GBE_ABT'})
1697
            {
1698
                Error ("Attempt to use default repository within automated build", "Path: " . $rpath);
1699
            }
361 dpurdie 1700
            $url = $SVN_URLS{''};
1701
            $pkgroot = $rpath;
353 dpurdie 1702
        }
1703
        else
1704
        {
1705
            #
1706
            #   User default (site configured) Repo Root
1707
            #
1708
            Error ("No site repository configured for : $fe",
1709
                   "Configure GBE_SVN_URL_" . uc($fe) );
1710
        }
267 dpurdie 1711
    }
1712
 
1713
    #
361 dpurdie 1714
    #   May want two elements, may want one
267 dpurdie 1715
    #
361 dpurdie 1716
    return $url, $pkgroot if ( wantarray );
1717
    return $url . $pkgroot;
267 dpurdie 1718
}
1719
 
369 dpurdie 1720
#-------------------------------------------------------------------------------
1721
# Function        : SvnPaths
1722
#
1723
# Description     : Extract SVN path conversion information
1724
#
1725
# Inputs          : Nothing
1726
#
1727
# Returns         : Two refs
1728
#                   Hash of SVN URLS
1729
#                   Array for search order
1730
#
1731
sub SvnPaths
1732
{
1733
    return \%SVN_URLS, \@SVN_URLS_LIST;
1734
}
361 dpurdie 1735
 
267 dpurdie 1736
#-------------------------------------------------------------------------------
1737
# Function        : SplitPackageUrl
1738
#
1403 dpurdie 1739
# Description     : Split the package URL into a few useful bits
267 dpurdie 1740
#
1741
# Inputs          : $self           - Instance data
1742
#
1743
# Returns         : Nothing
1744
#
1745
sub SplitPackageUrl
1746
{
1747
    my ($self) = @_;
353 dpurdie 1748
    Debug ("SplitPackageUrl", $self->{URL}, $self->{PKGROOT});
267 dpurdie 1749
 
1750
    #
1751
    #   Remove any protocol that may be present
1752
    #       http://server/
341 dpurdie 1753
    #       https://server/
267 dpurdie 1754
    #       svn://server/
1755
    #       file://This/Isa/Bad/Guess
1756
    #
341 dpurdie 1757
    if ( $self->{URL} =~ m~^(file|http|https|svn)://([^/]+)~ )
267 dpurdie 1758
    {
1759
        $self->{PROTOCOL} = $1;
1760
        $self->{SERVER} = $2;
1761
    }
1762
 
1763
    if ( $self->{PKGROOT} =~ m~(.*)(@\d+)$~ )
1764
    {
1765
        $self->{PEG} = $2;
1766
    }
1767
 
1768
    #
1769
    #   Determine TTB type
1770
    #   Need to handle
1771
    #       .../trunk
1772
    #       .../trunk@nnnnn
1773
    #       .../tags/version@nnnnn
1774
    #       .../branches/version@nnnnn
1775
    #
1776
    #
1777
    if (  $self->{PKGROOT} =~ m~/?(.*)/(tags|branches|trunk)(/|$|@)(.*)$~ )
1778
    {
1779
        $self->{PATH}         = $1;
1780
        $self->{TAGTYPE}      = $2;
1781
        $self->{VERSION}      = $4;
1782
    }
1783
    else
1784
    {
1785
        $self->{PATH} = $self->{PKGROOT};
1786
    }
1787
 
1788
    DebugDumpData ('SplitPackageUrl', $self ) if ( IsDebug(2) );
1789
}
1790
 
1791
#-------------------------------------------------------------------------------
1792
# Function        : Full
1793
#                   FullWs
1794
#                   Repo
1795
#                   Peg
1796
#                   Type
1797
#                   WsType
1798
#                   Path
1799
#                   Version
1800
#                   RmRef
385 dpurdie 1801
#                   RmPath
267 dpurdie 1802
#
1803
# Description     : Accessor functions
1804
#
1805
# Inputs          : $self       - Instance data
1806
#                                 self (is $_[0])
1807
#
1808
# Returns         : Data Item
1809
#
369 dpurdie 1810
sub Full        { return $_[0]->{URL} . $_[0]->{PKGROOT} ; }
1811
sub FullWs      { return $_[0]->{URL} . $_[0]->{WSURL} ; }
1812
sub FullWsRev   { return $_[0]->{URL} . $_[0]->{WSURL} . '@' . $_[0]->{WSREVNO} ; }
1403 dpurdie 1813
sub FullPath    { return $_[0]->{URL} . $_[0]->{PATH} ; }
369 dpurdie 1814
sub Peg         { return $_[0]->{PEG} ; }
1403 dpurdie 1815
sub DevBranch   { return $_[0]->{DEVBRANCH} || '' ; }
369 dpurdie 1816
sub Type        { return $_[0]->{TAGTYPE} || '' ; }
1817
sub WsType      { return $_[0]->{WSTYPE}  || '' ; }
1818
sub Path        { return $_[0]->{PATH} ; }
1819
sub Version     { return $_[0]->{VERSION} ; }
1820
sub RmRef       { return $_[0]->{RMREF} ; }
385 dpurdie 1821
sub RmPath      { my $path = $_[0]->{RMREF}; $path =~ s~@.*?$~~ ;return  $path; }
1403 dpurdie 1822
sub SvnTag      { return $_[0]->{SVNTAG} || '' ; }
267 dpurdie 1823
 
1824
#-------------------------------------------------------------------------------
1825
# Function        : Print
1826
#
1827
# Description     : Debug display the URL
1828
#
1829
# Inputs          : $self           - Instance data
1830
#                   $header
1831
#                   $indent
1832
#
1833
# Returns         : Nothing
1834
#
1835
sub Print
1836
{
1837
    my ($self, $header, $indent) = @_;
1838
    print "$header\n" if $header;
1839
    $indent = 4 unless ( defined $indent );
1840
    $indent = ' ' x $indent;
1841
 
1842
 
1403 dpurdie 1843
    print $indent . "PROTOCOL :" . $self->{PROTOCOL} . "\n";
1844
    print $indent . "SERVER   :" . $self->{SERVER} . "\n";
1845
    print $indent . "URL      :" . $self->{URL} . "\n";
1846
    print $indent . "PKGROOT  :" . $self->{PKGROOT} . "\n";
1847
    print $indent . "PATH     :" . $self->{PATH} . "\n";
1848
    print $indent . "TAGTYPE  :" . ($self->{TAGTYPE} || '') . "\n";
1849
    print $indent . "VERSION  :" . ($self->{VERSION} || '') . "\n";
1850
    print $indent . "PEG      :" . ($self->{PEG} || '') . "\n";
1851
    print $indent . "DEVBRANCH:" . ($self->{DEVBRANCH} || '') . "\n";
1852
    print $indent . "SVNTAG   :" . ($self->{SVNTAG} || '') . "\n";
1853
#    print $indent . "FULL    :" . $self->Full . "\n";
1854
 
1855
    print $indent . "Full         :" . $self->Full . "\n";
2049 dpurdie 1856
    print $indent . "FullWs       :" . $self->FullWs    . "\n";
1403 dpurdie 1857
#    print $indent . "FullWsRev    :" . $self->FullWsRev . "\n";
1858
    print $indent . "FullPath     :" . $self->FullPath  . "\n";
1859
    print $indent . "Peg          :" . $self->Peg       . "\n";
1860
    print $indent . "DevBranch    :" . $self->DevBranch . "\n";
1861
    print $indent . "Type         :" . $self->Type      . "\n";
1862
    print $indent . "WsType       :" . $self->WsType    . "\n";
1863
    print $indent . "Path         :" . $self->Path      . "\n";
1864
    print $indent . "Version      :" . $self->Version   . "\n";
1865
    print $indent . "RmRef        :" . ($self->RmRef || '') . "\n";
1866
#    print $indent . "RmPath       :" . ($self->RmPath|| '') . "\n";
267 dpurdie 1867
}
1868
 
1869
#-------------------------------------------------------------------------------
1870
# Function        : BranchName
1871
#
1872
# Description     : Create a full URL to a branch or tag based on the
1873
#                   current entry
1874
#
1875
#                   URL must have a TTB format
1876
#
1877
# Inputs          : $self           - Instance data
1878
#                   $branch         - Name of the branch
1879
#                   $type           - Optional branch type
1880
#
1881
# Returns         : Full URL name to the new branch
1882
#
1883
sub BranchName
1884
{
1885
    my ($self, $branch, $type ) = @_;
1886
    Debug ( "BranchName", $branch );
1887
 
1888
    $type = 'branches' unless ( $type );
1889
    my $root = $self->{PKGROOT};
1890
 
1891
    $root =~ s~/(tags|branches|trunk)(/|$|@).*~~;
1892
 
1893
    return $self->{URL} . $root . '/' . $type . '/' . $branch;
1894
}
1895
 
379 dpurdie 1896
#-------------------------------------------------------------------------------
1897
# Function        : setRepoProperty
1898
#
1899
# Description     : Sets a Repository property
1900
#                   This may well fail unless the Repo is setup to allow such
2429 dpurdie 1901
#                   changes and the user is allowed to make such changes
379 dpurdie 1902
#
1903
# Inputs          : $name
1904
#                   $value
1403 dpurdie 1905
#                   $allowError     - Support for bad repositories
379 dpurdie 1906
#
1403 dpurdie 1907
# Returns         : 0 - Change made
1908
#                   Will not return on error
379 dpurdie 1909
#
1910
sub setRepoProperty
1911
{
1403 dpurdie 1912
    my ($self, $name, $value, $allowError ) = @_;
1913
    my $retval = 0;
2429 dpurdie 1914
    my $rv;
1403 dpurdie 1915
 
379 dpurdie 1916
    Debug ( "setRepoProperty", $name, $value );
1917
    #
1918
    #   Ensure that the Repo version is known
1919
    #   This should be set by a previous operation
1920
    #
1921
    unless ( defined $self->{REVNO} )
1922
    {
1923
        Error ("setRepoProperty. Release Revision Number not known");
1924
    }
1925
 
2429 dpurdie 1926
 
1927
 
379 dpurdie 1928
    #
1929
    #   Execute the command
2429 dpurdie 1930
    #   Appears tp fail random;y - so try a few times
379 dpurdie 1931
    #
4076 dpurdie 1932
    #Debug ( "setRepoProperty", $name, $value, $self->{REVNO});
2429 dpurdie 1933
    for (my $ii = 0; $ii < 3; $ii++ )
1934
    {
1935
    $rv = $self->SvnCmd ( 'propset' , $name, '--revprop', '-r',  $self->{REVNO}, $value, $self->Full,
379 dpurdie 1936
                            {
1937
                                'credentials' => 1,
1938
                                'nosavedata' => 1,
1939
                            }
2429 dpurdie 1940
                       );
1941
        last unless ( $rv );
1942
        Warning("setRepoProperty: Failure attempt: $ii");
1943
DebugDumpData('setRepoProperty Failure', $self );
1944
        sleep (1);
1945
    }
1946
 
1947
    if ($rv)
379 dpurdie 1948
    {
1949
        #
1950
        #   Property NOT set
1951
        #
1403 dpurdie 1952
        if ( $allowError )
1953
        {
1954
            Warning ("setRepoProperty: $name - FAILED");
1955
            $retval = 1;
1956
        }
1957
        else
1958
        {
1959
            Error ("setRepoProperty: $name - FAILED");
1960
        }
379 dpurdie 1961
    }
1403 dpurdie 1962
 
1963
    return $retval;
379 dpurdie 1964
}
1965
 
1403 dpurdie 1966
#-------------------------------------------------------------------------------
1967
# Function        : backTrackSvnLabel
1968
#
1969
# Description     : Examine a Svn Tag and backtrack until we find the branch
1970
#                   that was used to create the label
1971
#
1972
# Inputs          : $self                   - Instance Data
1973
#                   $src_label              - Label to process
1974
#                                             Label within the current instance
1975
#                   A hash of named arguments
1976
#                       data                - Scalar ref. Hash of good stuff returned
1977
#                       printdata           - Print RAW svn data
1978
#                       onlysimple          - Do not do exhaustive scan
1979
#                       savedevbranch       - Save Dev Branch in session
2764 dpurdie 1980
#                                             Used in label clone
1403 dpurdie 1981
#
1982
# Returns         : Branch from which the label was taken
1983
#                   or the label prefixed with 'tags'.
1984
#
1985
sub backTrackSvnLabel
1986
{
1987
    my $self = shift;
1988
    my $src_label = shift;
1989
    my %opt = @_;
1990
    my $branch;
1991
 
1992
    Debug ("backTrackSvnLabel");
1993
    Error ("backTrackSvnLabel: Odd number of args") unless ((@_ % 2) == 0);
1994
 
1995
    #
1996
    #   May need to read and process data twice
1997
    #   First   - stop on copy. May it fast
1998
    #   Second  - all the log.
1999
 
2000
    #
2001
    #   extract data
2002
    #
2003
    foreach my $mode ( '--stop-on-copy', '' )
2004
    {
2005
        #   Init stored data
2006
        #   Used to communicate with callback function(s)
2007
        #
2008
        Information ("backTrackSvnLabel: Performing exhaustive search") unless $mode;
2009
        $self->{btData} = ();
2010
        $self->{btData}{results}{base} = $self->FullPath();
2011
        $self->{btData}{results}{label} = $src_label;
2012
        $self->{btData}{results}{changeSets} = 0;
2013
        $self->{btData}{results}{distance} = 0;
2014
 
2015
        #
2016
        #   Linux does not handle empty arguments in the same
2017
        #   manner as windows. Solution: pass an array
2018
        #
2019
        my @mode;
2020
        push @mode, $mode if ( $mode);
2021
        my $spath = $self->FullPath() . '/' . $src_label;
2022
 
2023
        Verbose2("backTrackSvnLabel. Log from $spath");
2024
        $self->SvnCmd ( 'log', '-v', '--xml', '-q'
2025
                        , @mode
2026
                        , $spath
2027
                        , { 'credentials' => 1,
2028
                            'process' => \&ProcessBackTrack,
2029
                            'printdata' => $opt{printdata},
2030
                            'nosavedata' => 1,
2031
                             }
2032
                            );
2033
 
2034
        last if ( $self->{btData}{good} );
2035
        last if ( $opt{onlysimple} );
2036
    }
2037
 
2038
    #
2039
    #   Did not backtrack to a branch (or trunk)
2040
    #   Return the users label
2041
    #
2042
    unless ( $self->{btData}{good} )
2043
    {
2044
        $branch = $src_label;
2045
    }
2046
    else
2047
    {
2048
        $branch = $self->{btData}{results}{devBranch};
2049
        if ( $opt{savedevbranch} )
2050
        {
2051
            $self->{btData}{results}{devBranch} =~ m~^(.*?)(@|$)~;
2052
            $self->{DEVBRANCH} = $1;
2053
        }
2054
 
2055
    }
2056
 
2057
    #
2058
    #   Return data to the user
2059
    #
2060
    if ( my $refData = $opt{data} )
2061
    {
2062
        Error ('Internal: backTrackSvnLabel. Arg to "data" must be ref to a scalar')
2063
            unless ( ref($refData) eq 'SCALAR' );
2064
        $$refData = $self->{btData}{results};
2065
    }
2066
 
2067
    #
2068
    #   Clean up the data
2069
    #
2070
    delete $self->{btData};
2071
    return $branch;
2072
}
2073
 
2074
#-------------------------------------------------------------------------------
2075
# Function        : ProcessBackTrack
2076
#
2077
# Description     :
2078
#                   Parse
2079
#                       <logentry
2080
#                          revision="24272">
2081
#                       <author>bivey</author>
2082
#                       <date>2005-07-25T15:45:35.000000Z</date>
2083
#                       <paths>
2084
#                       <path
2085
#                          prop-mods="false"
2086
#                          text-mods="false"
2087
#                          kind="dir"
2088
#                          copyfrom-path="/enqdef/branches/Stockholm"
2089
#                          copyfrom-rev="24271"
2090
#                          action="A">/enqdef/tags/enqdef_24.0.1.sls</path>
2091
#                       </paths>
2092
#                       <msg>COTS/enqdef: Tagged by Jats Svn Import</msg>
2093
#                       </logentry>
2094
#
2095
#
2096
#                   Uses:   $self->{btData}     - Scratch Data
2097
#
2098
# Inputs          : $self           - Class Data
2099
#                   $line           - Input data to parse
2100
#
2101
# Returns         : 0 - Do not terminate input command
2102
#
2103
sub  ProcessBackTrack
2104
{
2105
    my ($self, $line ) = @_;
2106
    Message ( $line ) if $self->{PRINTDATA};
2107
 
2108
    $line =~ s~\s+$~~;
2109
    next unless ( $line );
2110
#    Debug0('', $line);
2111
 
2112
    my $workSpace =  \%{$self->{btData}};
2113
    if ( $line =~ m~<logentry$~ ) {
3045 dpurdie 2114
        #
2115
        #   Start of a logentry
2116
        #
1403 dpurdie 2117
        $workSpace->{mode} = 'l';
2118
        $workSpace->{rev} = 0;
2119
        $workSpace->{changesSeen} = 0;
2120
 
2121
    } elsif ( $line =~ m~</logentry>$~ ) {
2122
        $workSpace->{mode} = '';
2123
        #
2124
        #   End of a <logenty>
2125
        #   See if we have a result - a dev branch not copied from a tag
2126
        #
2127
        if ( exists $workSpace->{devBranch} )
2128
        {
2129
            $workSpace->{results}{distance}++;
2130
            $workSpace->{devBranch} =~ m~/((tags|branches|trunk)(/|\@).*)~;
2131
            my $devBranch = $1;
2132
 
2133
            push @{$workSpace->{results}{paths}}, $devBranch;
2134
            unless ( $devBranch =~ m ~^tags~ )
2135
            {
2136
                $workSpace->{results}{devBranch} = $devBranch;
2137
                $workSpace->{results}{isaBranch} = 1;
2138
                $workSpace->{good} = 1;
2139
                return 1;
2140
            }
2141
        }
2142
 
2143
    } elsif ( $line =~ m~<path$~ ) {
2144
        $workSpace->{mode} = 'p';
2145
        Error ('Path without Rev') unless ( $workSpace->{rev} );
2146
 
2147
    } elsif ( $line =~ m~</paths>$~ ) {
2148
        $workSpace->{mode} = '';
2149
    }
2150
    return 0 unless ( $workSpace->{mode} );
2151
 
2152
    if ( $workSpace->{mode} eq 'l' )
2153
    {
2154
        #
2155
        #   Processing logentry data
3045 dpurdie 2156
        #       Only need the revision
1403 dpurdie 2157
        #
2158
        $workSpace->{rev} = $1
2159
            if ( $line =~ m~revision=\"(\d+)\"~ );
2160
 
2161
    } elsif ( $workSpace->{mode} eq 'p' ) {
2162
        #
2163
        #   Processing Paths
2164
        #       Entries appear to be in a random order
2165
        #       Not always the same order
2166
        #
2167
        my $end = 0;
3045 dpurdie 2168
        if ( $line =~ s~\s*(.+?)="(.*)">(.*)</path>$~~ )
1403 dpurdie 2169
        {
2170
            #
2171
            #   Last entry has two items
2172
            #       Attribute
2173
            #       Data Item
2174
            #
2175
            $end = 1;
2176
            $workSpace->{path}{$1} = $2;
2177
            $workSpace->{path}{DATA} = $3;
2178
        }
2179
        elsif ($line =~ m~\s*(.*?)="(.*)"~ )
2180
        {
3045 dpurdie 2181
            #
2182
            #   Attribute
2183
            #
1403 dpurdie 2184
            $workSpace->{path}{$1} = $2;
2185
        }
2186
#        else
2187
#        {
2188
#            Warning ("Cannot decode XML log: $line");
2189
#        }
2190
 
2191
        if ( $end )
2192
        {
2193
            #
2194
            #   If the Repo is created by a pre 1.6 SVN, then kind will be
2195
            #   empty. Have a guess.
2196
            #
2197
            if ( $workSpace->{path}{'kind'} eq '' )
2198
            {
2199
                if ( exists $workSpace->{path}{'copyfrom-path'} ) {
2200
                    $workSpace->{path}{'kind'} = 'dir';
2201
                } else {
2202
                    $workSpace->{path}{'kind'} = 'file';
2203
                }
2204
            }
2205
 
2206
            if ( $workSpace->{path}{'kind'} eq 'dir' &&  exists $workSpace->{path}{'copyfrom-path'} )
2207
            {
2208
                my $srev = $workSpace->{path}{'copyfrom-rev'};
2209
                my $from = $workSpace->{path}{'copyfrom-path'};
3347 dpurdie 2210
                if ( $from =~ m~/trunk$~ || $from =~ m~/branches/[^/]+~ )
1403 dpurdie 2211
                {
2212
                    $workSpace->{devBranch} = $from . '@' . $srev;
2213
                }
2214
            }
2215
 
2216
            elsif ( $workSpace->{path}{'kind'} eq 'file' )
2217
            {
2218
                #
2219
                #   Track files that have been changed between tag and branch
2220
                #   The log is presented as newest first
2221
                #   The files have a tag-name component.
2222
                #       Remove the tag name - so that we can compare files
2223
                #       Save the first instance of changed files
2224
                #           Others will be in older versions
2225
                #           and thus of no interest
2226
                #
2227
                #   Count the chnage sets that have changes
2228
                #   Having changes in multiple change sets indicates
2229
                #   development on a /tags/ - which is BAD
2230
                #
2231
                $workSpace->{path}{'DATA'} =~ m~(.+)/((tags|branches|trunk)(/|$).*)~;
2232
                my $file =  $2;
2233
                my $full = $file;
2234
                $file =~ s~^tags/(.+?)/~~;
2235
 
2236
                if ( ! exists $workSpace->{files}{$file}  )
2237
                {
2238
                    push @{$workSpace->{results}{files}}, $full . '@' . $workSpace->{rev};
2239
                }
2240
                $workSpace->{files}{$file}++;
2241
                $workSpace->{firstFile} = $file unless ( defined $workSpace->{firstFile} );
2242
 
2243
                unless ( $workSpace->{changesSeen} )
2244
                {
2245
                    unless( $workSpace->{firstFile} eq $file )
2246
                    {
2247
                        $workSpace->{results}{changeSets}++;
2248
                        $workSpace->{changesSeen}++;
2249
                    }
2250
                }
2251
 
2252
                if ( scalar keys %{$workSpace->{files}} > 1 )
2253
                {
2254
                    $workSpace->{results}{multipleChanges} = 1;
2255
                    Verbose ("backTrackSvnLabel: Changes in multiple versions");
2256
                }
2257
            }
2258
 
2259
            delete $workSpace->{path};
2260
        }
2261
    }
2262
 
2263
    #
2264
    #   Return 0 to keep on going
2265
    return 0;
2266
}
2267
 
267 dpurdie 2268
#------------------------------------------------------------------------------
2269
1;