Subversion Repositories DevTools

Rev

Rev 391 | Rev 1328 | Go to most recent revision | Details | Compare with Previous | 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);
36
 
37
use File::Path;             # Instead of FileUtils
38
use File::Basename;
39
use Cwd;
40
 
41
 
42
# automatically export what we need into namespace of caller.
43
use Exporter();
44
our (@ISA, @EXPORT, %EXPORT_TAGS, @EXPORT_OK);
45
@ISA         = qw(Exporter JatsSvnCore);
46
 
47
@EXPORT      = qw(
48
                    NewSession
49
                    NewSessionByWS
50
                    NewSessionByUrl
51
 
52
                    SvnRmView
53
                    SvnIsaSimpleLabel
54
                    SvnComment
55
 
56
                    SvnUserCmd
361 dpurdie 57
 
58
                    SvnPath2Url
369 dpurdie 59
                    SvnPaths
267 dpurdie 60
                );
61
@EXPORT_OK =  qw(
62
                );
63
 
64
%EXPORT_TAGS = (All => [@EXPORT, @EXPORT_OK]);
65
 
66
#
67
#   Global Variables
68
#
69
 
70
#-------------------------------------------------------------------------------
71
# Function        : SvnCo
72
#
73
# Description     : Create a workspace
74
#                   Can be used to extract files, without creating the
75
#                   subversion control files.
76
#
77
# Inputs          : $self               - Instance data
78
#                   $RepoPath           - Within the repository
79
#                   $Path               - Local path
80
#                   Options             - Options
81
#                           --Export    - Export Only
1270 dpurdie 82
#                           --NoPrint   - Don't print files exported
267 dpurdie 83
#
84
# Returns         : Nothing
85
#
86
sub SvnCo
87
{
88
    my ($self, $RepoPath, $path, @opts) = @_;
89
    my $export = grep (/^--Export/, @opts );
1270 dpurdie 90
    $self->{PRINTDATA} = ! grep (/^--NoPrint/, @opts );
341 dpurdie 91
    Debug ("SvnCo", $RepoPath, $path);
267 dpurdie 92
 
93
    #
94
    #   Ensure that the output path does not exist
95
    #   Do not allow the user to create a local work space
96
    #   where one already exists
97
    #
98
    Error ("SvnCo: No PATH specified" ) unless ( $path );
99
    Error ("SvnCo: Target path already exists", "Path: " . $path ) if ( -e $path );
100
 
101
    #
102
    #   Build up the command line
103
    #
104
    my @args = $export ? 'export' : 'checkout';
105
    push @args, qw( --ignore-externals );
106
    push @args, $RepoPath, $path;
107
 
1270 dpurdie 108
 
267 dpurdie 109
    my @co_list;
110
    if ( $self->SvnCmd ( @args,
111
                            {
112
                                'process' => \&ProcessCo,
113
                                'data' => \@co_list,
114
                                'credentials' => 1,
115
                                'nosavedata' => 1,
116
                            }
117
                       ) || @co_list )
118
    {
119
        #
120
        #   We have a checkout limitation
121
        #   Delete the workspace and then report the error
122
        #
385 dpurdie 123
        #   Note: For some reason a simple rmtree doesn't work
124
        #         Nor does glob show all the directories
125
        #
267 dpurdie 126
        Verbose2 ("Remove WorkSpace: $path");
127
        rmtree( $path, IsVerbose(3) );
385 dpurdie 128
        rmtree( $path, IsVerbose(3) );
267 dpurdie 129
        Error ("Checking out Workspace", @{$self->{ERROR_LIST}}, @co_list );
130
    }
1270 dpurdie 131
    $self->{PRINTDATA} = 0;
267 dpurdie 132
    return;
133
 
134
    #
135
    #   Internal routine to scan each the checkout
136
    #
137
    #   Due to the structure of a SubVersion repository it would be
138
    #   possible for a user to extract the entire repository. This is
139
    #   not good as the repo could be very very large
140
    #
141
    #   Assume that the structure of the repo is such that our
142
    #   user is not allowed to extract a directory tree that contains
143
    #   key paths - such as /tags/ as this would indicate that they are
144
    #   attempting to extract something that is not a package
145
    #
146
    #
147
    sub ProcessCo
148
    {
149
        my $self = shift;
1270 dpurdie 150
        my $data = shift;
151
 
152
        if ( $self->{PRINTDATA} )
267 dpurdie 153
        {
1270 dpurdie 154
            #
155
            #   Pretty display for user
156
            #
157
            Information1 ("Extracting: $data");
158
        }
159
 
160
        if (  $data =~ m~((/)(tags|branches|trunk)(/|$))~ )
161
        {
267 dpurdie 162
            my $bad_dir = $1;
163
            push @{$self->{ERROR_LIST}}, "Checkout does not describe the root of a package. Contains: $bad_dir";
164
            return 1;
165
        }
166
 
167
        ##
168
        ##   Limit the size of the WorkSpace
169
        ##   This limit is a bit artificial, but does attempt to
170
        ##   limit views that encompass too much.
171
        ##
172
        #if ( $#{$self->{RESULT_LIST}} > 100 )
173
        #{
174
        #    Warning ("View is too large - DEBUG USE ONLY. WILL BE REMOVED" );
175
        #    push @{$self->{ERROR_LIST}}, "View too large";
176
        #    return 1;
177
        #}
178
    }
179
}
180
 
181
#-------------------------------------------------------------------------------
182
# Function        : SvnCi
183
#
184
# Description     : Check in the specified WorkSpace
185
#
186
# Inputs          : $self           - Instance data
187
#                   A hash of named arguments
188
#                       comment     - Commit comment
379 dpurdie 189
#                       allowSame   - Allow no change to the workspace
267 dpurdie 190
#
191
# Returns         : Tag of the checkin
192
#
193
sub SvnCi
194
{
195
    my $self = shift;
196
    my %opt = @_;
197
    my $status_url;
379 dpurdie 198
    my $ws_rev;
267 dpurdie 199
 
200
    Debug ("SvnCi");
201
    Error ("SvnCi: Odd number of args") unless ((@_ % 2) == 0);
202
 
203
    #
204
    #   Validate the source path
205
    #   Note: populates @{$self->{RESULT_LIST}} with 'info' commands
206
    #
207
    my $path = SvnValidateWs ($self, 'SvnCi');
208
 
209
    #
210
    #   Scan the @{$self->{RESULT_LIST}}, which has the results of an 'info'
211
    #   command the locate the URL.
212
    #
213
    #   This contains the target view space
214
    #   Sanity test. Don't allow Checkin to a /tags/ area
215
    #
216
    foreach ( @{$self->{RESULT_LIST}})
217
    {
379 dpurdie 218
        if ( m~^URL:\s+(.+)~ ) {
267 dpurdie 219
            $status_url = $1;
379 dpurdie 220
        } elsif ( m~^Revision:\s+(\d+)~ ) {
221
            $ws_rev = $1;
267 dpurdie 222
        }
379 dpurdie 223
 
224
        last if ( defined $status_url && defined $ws_rev );
267 dpurdie 225
    }
226
 
227
    Error ("SvnCi: Cannot determine Repositoty URL")
228
        unless ( $status_url );
229
 
230
    Error ("SvnCi: Not allowed to commit to a 'tags' area", "URL: $status_url")
231
        if ( $status_url =~ m~/tags(/|$)~ );
232
 
233
    #
234
    #   Commit
235
    #
236
    $self->SvnCmd ( 'commit', $path
237
                    , '-m', SvnComment( $opt{'comment'}, 'Created by SvnCi' ),
238
                    , { 'credentials' => 1,
239
                        'process' => \&ProcessRevNo,
379 dpurdie 240
                        'error' => "SvnCi: Copy Error",
241
                         }
267 dpurdie 242
                        );
379 dpurdie 243
 
244
    #
245
    #   No error and no commit
246
    #   Workspace was not changed, may be allowed
247
    #
248
    if ( ! $self->{REVNO} && $opt{allowSame} )
249
    {
250
        Warning ("SvnCi: Workspace matches Repository. No commit");
251
        $self->{REVNO} = $ws_rev;
252
    }
253
 
267 dpurdie 254
    Error ("SvnCi: Cannot determine Revision Number", @{$self->{RESULT_LIST}})
255
        unless ( $self->{REVNO} );
256
 
257
    #
258
    #   Update the view
259
    #   Doing this so that the local view contains an up to date
260
    #   revision number. If not done, and a 'copy' is done based
261
    #   on this view then the branch information will indicate that
262
    #   the copy is based on an incorrect version.
263
    #   This can be confusing!
264
    #
265
    $self->SvnCmd ( 'update'   , $path
266
                        , '--ignore-externals'
267
                        , { 'credentials' => 1,
268
                            'error' => "SvnCi: Updating WorkSpace" }
269
                        );
270
    #
271
    #   Pass the updated revision number back to the user
272
    #
273
    $self->CalcRmReference($status_url);
379 dpurdie 274
    Message ("Commit Tag is: " . $self->{RMREF} );
267 dpurdie 275
    return $self->{RMREF} ;
276
}
277
 
278
#-------------------------------------------------------------------------------
279
# Function        : SvnCreatePackage
280
#
281
# Description     : Create a package and any associated files
282
#
283
# Inputs          : $self        - Instance data
284
#                   A hash of named arguments
285
#                       package  - Name of the package
286
#                                  May include subdirs
287
#                       new      - True: Must not already exist
288
#                       replace  - True: Replace targets
289
#                       import   - DirTree to import
290
#                       label    - Tag for imported DirTree
291
#                       type     - Import TTB target
292
#
293
# Returns         : Revision of the copy
294
#
295
sub SvnCreatePackage
296
{
297
    my $self = shift;
298
    my %opt = @_;
299
    my $target;
300
 
301
    Debug ("SvnCreatePackage", @_);
302
    Error ("Odd number of args to SvnCreatePackage") unless ((@_ % 2) == 0);
303
    my %dirs = ( 'trunk/'       => 0,
304
                 'tags/'        => 0,
305
                 'branches/'    => 0 );
306
 
307
    #
308
    #   Sanity Tests
309
    #
310
    my $package = $self->Full || Error ("SvnCreatePackage: No package name provided");
311
    Error ("SvnCreatePackage: Invalid import path") if ( $opt{'import'} && ! -d $opt{'import'} );
312
    Error ("SvnCreatePackage: Tag without Import") if ( $opt{'label'} && ! $opt{'import'} );
313
    $opt{'label'} = SvnIsaSimpleLabel( $opt{'label'} ) if (  $opt{'label'} );
314
 
315
    #
316
    #   Package path cannot contain any of the keyword paths tags,trunk,branches
317
    #   as this would place a package with a package
318
    #
319
    Error ("Package path contains a reserved word ($1)", "Path: $package")
320
        if (  $package =~ m~/(tags|branches|trunk)(/|$)~ );
321
 
322
    #
323
    #   Package path cannot be pegged, or look like one
324
    #
325
    Error ("Package name contains a Peg ($1)", "Path: $package")
326
        if ( $package =~ m~.*(@\d+)$~ );
327
 
328
    #
329
    #   Determine TTB target
330
    #
331
    $opt{'type'} = 'trunk' unless ( $opt{'type'} );
332
    Error ("Invalid TTB type") unless ( $opt{'type'} =~ m{^(tags|branches|trunk)$} );
333
    Error ("Import without label") if ( $opt{'type'} ne  'trunk'  && ! $opt{'label'} );
334
 
335
    #
336
    #   Before we import data we must ensure that the targets do not exist
337
    #   Determine the import target(s)
338
    #
339
    my $import_target;
340
    my $copy_target;
341
 
342
    if ( $opt{'import'} )
343
    {
344
        #
345
        #   Primary target
346
        #   trunk, branck or tag
347
        #
348
        $import_target = $package . '/' . $opt{'type'};
349
        $import_target .= '/' .$opt{'label'} if ( $opt{'type'} ne 'trunk');
350
 
351
        $self->SvnValidateTarget( 'target'    => $import_target,
352
                                  'delete'    => $opt{'replace'},
353
                                  'available' => 1 );
354
 
355
        #
356
        #   Secondary target
357
        #   If primary is a trunk and a label is provided
358
        #
359
        if ( $opt{'type'} eq 'trunk' && $opt{'label'} )
360
        {
361
            $copy_target = $package . '/tags/' . $opt{'label'};
362
            $self->SvnValidateTarget( 'target'    => $copy_target,
363
                                      'delete'    => $opt{'replace'},
364
                                      'available' => 1 );
365
        }
366
    }
367
 
368
    #
369
    #   Probe to see if the package exists
370
    #
371
    my ( $ref_files, $ref_dirs, $ref_svn, $found ) = $self->SvnScanPath ( 'SvnCreatePackage', $package );
379 dpurdie 372
 
267 dpurdie 373
    if ( @$ref_dirs )
374
    {
375
        Error ("SvnCreatePackage: Package directory exists",
376
               "Cannot create a package here. Unexpected subdirectories:", @$ref_dirs);
377
    }
378
 
379
    if ( @$ref_files )
380
    {
381
        Warning ("SvnCreatePackage: Unexpected files found",
382
               "Path: $package",
383
               "Unexpected files found: @$ref_files");
384
    }
385
 
386
    if ( @$ref_svn )
387
    {
388
        #
389
        #   Do we need a new one
390
        #
391
        Error ("SvnCreatePackage: Package exists: $package") if $opt{'new'};
392
 
393
        #
394
        #   Some subversion files have been found here
395
        #   Create the rest
396
        #   Assume that the directory tree is good
397
        #
398
        #
399
        #   Some, or all, of the required package subdirectories exist
400
        #   Determine the new ones to created so that it can be done
401
        #   in an atomic step
402
        #
403
        delete $dirs{$_} foreach  ( @$ref_svn );
404
        if ( keys %dirs )
405
        {
406
            Warning ("SvnCreatePackage: Not all package subdirs present",
407
                     "Remaining dirs will be created",
408
                     "Found: @$ref_svn") if @$ref_svn;
409
        }
410
        else
411
        {
412
            Warning ("SvnCreatePackage: Package already present");
413
        }
414
    }
415
 
416
    #
417
    #   Create package directories that have not been discovered
418
    #       trunk
419
    #       branches
420
    #       tags
421
    #
422
    my @dirs;
423
    push @dirs, $package . '/' . $_ foreach ( keys %dirs );
424
    $target = $package . '/trunk';
425
 
426
    #
427
    #   Create missing directories - if any
428
    #
429
    if ( @dirs )
430
    {
431
        $self->SvnCmd ('mkdir', @dirs
379 dpurdie 432
                       , '-m', $self->Path() . ': Created by SvnCreatePackage'
267 dpurdie 433
                       , '--parents'
385 dpurdie 434
                       , { 'credentials' => 1
435
                           ,'error' => "SvnCreatePackage"
436
                           ,'process' => \&ProcessRevNo
437
                         } );
267 dpurdie 438
    }
439
 
440
    #
441
    #   Import data into the package if required
442
    #   Import data. Possible cases:
443
    #       - Import to trunk - and then tag it
444
    #       - Import to branches
445
    #       - Import to tags
446
    #
447
    if ( $import_target )
448
    {
449
        Verbose ("Importing directory into new package: $opt{'import'}");
450
 
451
        $target = $import_target;
452
        $self->{PRINTDATA} = 1;
453
        $self->SvnCmd ('import', $opt{'import'}
454
                        , $target
455
                        , '-m', 'Import by SvnCreatePackage'
456
                        , '--force'
457
                        , { 'credentials' => 1
458
                           ,'error' => "Import Incomplete"
459
                           ,'process' => \&ProcessRevNo
460
                          })
461
    }
462
 
463
    #
464
    #   If imported to the trunk AND a label is provided
465
    #   then tag the import as well.
466
    #   A simple URL copy
467
    #
468
    if ( $copy_target )
469
    {
470
        Verbose ("Labeling imported trunk: $opt{'label'} ");
471
        $target = $copy_target;
472
        $self->SvnCmd ('copy'  , $import_target
473
                        , $target
474
                        , '-m', 'Import tagged by SvnCreatePackage'
475
                        , { 'credentials' => 1
476
                          , 'process' => \&ProcessRevNo
477
                          , 'error' => "Import Incomplete" } );
478
    }
479
 
480
    #
481
    #   Pass the updated revision number back to the user
482
    #
483
    $self->CalcRmReference($target);
379 dpurdie 484
    Message ("Create Package Tag is: " . $self->{RMREF} );
267 dpurdie 485
    return $self->{RMREF} ;
486
}
487
 
488
#-------------------------------------------------------------------------------
489
# Function        : SvnRmView
490
#
491
# Description     : Remove a Subversion view
492
#                   Will run sanity checks and only remove the view if
493
#                   all is well
494
#
495
# Inputs          : A hash of named arguments
496
#                       path     - Path to local workspace
497
#                       modified - Array of files that are allowed to be modified
498
#                       force    - True: Force deletion
499
#
500
# Returns         :
501
#
502
sub SvnRmView
503
{
504
    my %opt = @_;
505
    Debug ("SvnRmView");
506
    Error ("Odd number of args to SvnRmView") unless ((@_ % 2) == 0);
507
 
508
    #
509
    #   Sanity test
510
    #
511
    my $path = $opt{'path'} || '';
512
    my $path_length = length ($path);
513
    Verbose2 ("Delete WorkSpace: $path");
514
 
515
    #
516
    #   If the path does not exist then assume that its already deleted
517
    #
518
    unless ( $path && -e $path )
519
    {
520
        Verbose2 ("SvnRmView: Path does not exist");
521
        return;
522
    }
523
 
524
    #
525
    #   Create information about the workspace
526
    #   This will also validate the path
527
    #
361 dpurdie 528
    my $session = NewSessionByWS ( $path, 0, 1 );
267 dpurdie 529
 
530
    #
531
    #   Validate the path
532
    #
533
    $session->SvnValidateWs ($path, 'SvnRmView');
534
 
535
    #
536
    #   Ask subversion if there are any files to be updated
537
    #   Prevent deletion of a view that has modified files
538
    #
539
    unless ( $opt{'force'} )
540
    {
541
        $session->SvnWsModified ( 'cmd' => 'SvnRmView', %opt );
542
    }
543
 
544
    #
545
    #   Now we can delete it
546
    #
547
    Verbose2 ("Remove WorkSpace: $path");
548
    rmtree( $path, IsVerbose(3) );
549
}
550
 
551
 
552
#-------------------------------------------------------------------------------
553
# Function        : SvnCopyWs
554
#
555
# Description     : Copy a workspace to a new place in the repository
556
#                   Effectively 'label' the workspace
557
#
558
#                   It would appear that the 'copy' command is very clever
559
#                   If a version-controlled file has been changed
560
#                   in the source workspace, then it will automatically be
561
#                   copied. This is a trap.
562
#
563
#                   Only allow a 'copy' if there are no modified
564
#                   files in the work space (unless overridden)
565
#
566
#
567
# Inputs          : $self        - Instance data
568
#                   A hash of named arguments
569
#                       path     - Path to local workspace
570
#                       target   - Location within the repository to copy to
571
#                       comment  - Commit comment
572
#                       modified - Array of files that are allowed to
573
#                                  be modified in the workspace.
385 dpurdie 574
#                       noswitch        - True: Don't switch to the new URL
575
#                       replace         - True: Delete existing tag if present
576
#                       allowLocalMods  - True: Allow complex tagging
267 dpurdie 577
#
578
# Returns         : Revision of the copy
579
#
580
sub SvnCopyWs
581
{
582
    my $self = shift;
583
    my %opt = @_;
385 dpurdie 584
    my $rv;
267 dpurdie 585
    Debug ("SvnCopyWs");
586
    Error ("Odd number of args to SvnCopyWs") unless ((@_ % 2) == 0);
587
    Error ("SvnCopyWs: No Workspace" ) unless ( $self->{WS} );
588
 
589
    #
590
    #   Insert defaults
591
    #
592
    my $target = $opt{target} || Error ("SvnCopyWs: Target not specified" );
593
 
594
    #
595
    #   Validate the source path
596
    #
597
    my $path = SvnValidateWs ($self, 'SvnCopyWs');
598
 
599
    #
600
    #   Validate the target
601
    #   Cannot have a 'peg'
602
    #
603
    Error ("SvnCopyWs: Target contains a Peg: ($1)", $target)
604
        if ( $target =~ m~(@\d+)\s*$~ );
605
 
606
    #
607
    #   Examine the workspace and ensure that there are no modified
608
    #   files - unless they are expected
609
    #
610
    $self->SvnWsModified ( 'cmd' => 'SvnCopyWs', %opt );
385 dpurdie 611
 
267 dpurdie 612
    #
613
    #   Validate the repository
614
    #   Ensure that the target does not exist
615
    #   The target may be deleted if it exists and allowed by the user
616
    #
617
    $self->SvnValidateTarget ( 'cmd'    => 'SvnCopyWs',
618
                        'target' => $target,
619
                        'delete' => $opt{replace},
620
                        'comment' => 'Deleted by SvnCopyWs'
621
                        );
369 dpurdie 622
 
267 dpurdie 623
    #
624
    #   Copy source to destination
625
    #   It would appear that even though the source is a WorkSpace, the copy
626
    #   does not transfer data from the WorkSpace. It appears as though its all
627
    #   done on the server. This is good - and fast.
628
    #
385 dpurdie 629
    #   Moreover, files that are modified in the local workspace will
630
    #   be copied and checked into the target, but this is not nice.
267 dpurdie 631
    #
385 dpurdie 632
    $self->{PRINTDATA} = 1;
633
    $rv = $self->SvnCmd ( 'cp'  , $path
267 dpurdie 634
                        , $target
635
                        , '--parents'
636
                        , '-m', SvnComment( $opt{'comment'}, 'Created by SvnCopyWs' ),
637
                        , { 'process' => \&ProcessRevNo,
638
                            'credentials' => 1, }
385 dpurdie 639
                        );
640
    $self->{PRINTDATA} = 0;
641
    if ($rv)
267 dpurdie 642
    {
643
        #
644
        #   Error in copy
645
        #   Attempt to delete the target. Don't worry if we can't do that
646
        #
647
        my @err1 = @{$self->{ERROR_LIST}};
648
        $self->SvnCmd ( 'delete'
649
                    , $target
650
                    , '-m', 'Deleted by SvnCopyWs after creation failure'
651
                    , { 'credentials' => 1, }
652
               );
653
        Error ("SvnCopyWs: Copy Error", @err1);
654
    }
655
 
656
    Error ("SvnCopyWs: Cannot determine Revision Number", @{$self->{RESULT_LIST}})
657
        unless ( $self->{REVNO} );
658
 
659
    Verbose2 ("Copy committed as revision: " . $self->{REVNO} );
660
 
661
    unless ( $opt{'noswitch'} )
662
    {
663
        #
664
        #   Switch to the new URL
665
        #   This will link the Workspace with the copy that we have just made
666
        #
667
        $self->SvnCmd ( 'switch', $target
668
                         , $path
669
                         , { 'credentials' => 1,
670
                             'error' => "SvnCopyWs: Cannot switch to new URL" }
671
               );
672
    }
673
 
674
    #
675
    #   Pass the updated revision number back to the user
676
    #
677
    $self->CalcRmReference($target );
353 dpurdie 678
    #Message ("Tag is: " . $self->{RMREF} );
267 dpurdie 679
    return $self->{RMREF} ;
680
}
681
 
682
#-------------------------------------------------------------------------------
683
# Function        : SvnWsModified
684
#
685
# Description     : Test a Workspace for modified files
686
#                   Allow some files to be modified
687
#
688
# Inputs          : $self           - Instance data
689
#                   A hash of named arguments
690
#                       path        - Path to local workspace
691
#                       modified    - Files that are allowed to be modified
692
#                                     Relative to the 'path'
693
#                                     May be a single file or an array of files
385 dpurdie 694
#                       allowLocalMods - Only warn about local mods
267 dpurdie 695
#                       cmd         - Command name for error reporting
696
#
697
# Returns         :
698
#
699
sub SvnWsModified
700
{
701
    my $self = shift;
702
    my %opt = @_;
703
    Debug ("SvnWsModified");
704
    Error ("Odd number of args to SvnWsModified") unless ((@_ % 2) == 0);
705
 
706
    my $cmd = $opt{'cmd'} || 'SvnWsModified';
707
 
708
    #
709
    #   Validate the path
710
    #
711
    SvnValidateWs ($self, $cmd);
712
    my $path = $self->{WS};
713
    my $path_length = length ($path);
714
    Verbose2 ("Test Workspace for Modifications: $path");
715
 
716
    #
717
    #   Ask subversion if there are any files to be updated
718
    #
719
    $self->SvnCmd ('status', $path, {'error' => "Svn status command error"} );
720
 
721
    #
722
    #   Examine the list of modified files
723
    #
724
    if ( @{$self->{RESULT_LIST}} )
725
    {
726
        #
351 dpurdie 727
        #   Create a hash of files that are allowed to change
267 dpurdie 728
        #   These are files relative to the base of the view
729
        #
730
        #   The svn command has the 'path' prepended, so this
731
        #   will be removed as we process the commands
732
        #
733
        my %allowed;
734
        my @unexpected;
735
 
736
        if ( exists $opt{'modified'}  )
737
        {
738
            $allowed{'/' . $_} = 1 foreach ( ref ($opt{'modified'}) ? @{$opt{'modified'}} : $opt{'modified'}  );
739
        }
740
 
741
        #
742
        #   Process the list of modified files
743
        #   Do this even if we aren't allowed modified files as we
744
        #   still need to examine the status and kill off junk entries
745
        #   ie: ?, I, ! and ~
746
        #
747
        #    First column: Says if item was added, deleted, or otherwise changed
748
        #      ' ' no modifications
749
        #      'A' Added
750
        #      'C' Conflicted
751
        #      'D' Deleted
752
        #      'I' Ignored
753
        #      'M' Modified
754
        #      'R' Replaced
755
        #      'X' item is unversioned, but is used by an externals definition
756
        #      '?' item is not under version control
757
        #      '!' item is missing (removed by non-svn command) or incomplete
758
        #      '~' versioned item obstructed by some item of a different kind
759
        #
760
        foreach my $entry ( @{$self->{RESULT_LIST}} )
761
        {
762
            #
763
            #   Extract filename from line
351 dpurdie 764
            #       First 8 chars are status
267 dpurdie 765
            #       Remove WS path too
766
            #
351 dpurdie 767
            my $file = substr ( $entry, 8 + $path_length );
267 dpurdie 768
            next if ( $allowed{$file} );
769
 
770
            #
771
            #   Examine the first char and rule out funny things
772
            #
773
            my $f1 =  substr ($entry, 0,1 );
774
            next if ( $f1 =~ m{[?I!~]} );
775
            push @unexpected, $entry;
776
        }
385 dpurdie 777
 
778
        if ( @unexpected )
779
        {
780
            if ( $opt{allowLocalMods} ) {
781
                Message ("Workspace contains locally modified files:", @unexpected);
782
            } else {
783
                Error ("Workspace contains unexpected modified files", @unexpected);
784
            }
785
        }
267 dpurdie 786
    }
787
}
788
 
789
#-------------------------------------------------------------------------------
790
# Function        : SvnListPackages
791
#
792
# Description     : Determine a list of packages within the repo
793
#                   This turns out to be a very slow process
794
#                   so don't use it unless you really really need to
795
#
796
# Inputs          : $repo       - Name of the repository
797
#
798
# Returns         : 
799
#
800
sub SvnListPackages
801
{
802
    my ($repo) = @_;
803
 
804
    my @path_list = $repo;
805
    my @list;
806
    my $scanned = 0;
807
    Debug ("SvnListPackages");
808
    while ( @path_list )
809
    {
810
        my $path = shift @path_list;
811
        $scanned++;
812
print "Reading: $path\n";
813
        my ( $ref_files, $ref_dirs, $ref_svn, $found ) = SvnScanPath ( 'Listing Packages', $path );
814
 
815
        #
816
        #   If there are Subversion dirs (ttb) in this directory
817
        #   then this is a package. Add to list
818
        #
819
        push @list, $path if ( @$ref_svn );
820
 
821
        #
822
        #   Add subdirs to the list of paths to explore
823
        #
824
        foreach  ( @$ref_dirs )
825
        {
826
            chop;                                 # Remove trailing '/'
827
            push @path_list, $path . '/' . $_;    # Extend the path
828
        }
829
    }
830
 
831
    Message ("Found:", @list );
832
    Message ("Dirs Scanned: $scanned");
833
    Message ("Packages Found: $#list");
834
}
835
 
836
#-------------------------------------------------------------------------------
837
# Function        : ListLabels
838
#
839
# Description     : List labels within a given package
840
#
841
# Inputs          : $self               - Instance data
842
#                   $path               - path to label source
843
#
844
# Returns         : Ref to an array
845
#
846
sub ListLabels
847
{
848
    my ($self, $path) = @_;
849
    Debug ("ListLabels");
850
 
851
    my ( $ref_files, $ref_dirs, $ref_svn, $found ) = $self->SvnScanPath ( 'Listing Versions', $path );
852
 
853
    Error ("List: Path not found: $path") unless ( $found );
854
 
855
    #
856
    #   Dont report files - just directories
857
    #
858
    return $ref_dirs;
859
}
860
 
861
 
862
#-------------------------------------------------------------------------------
863
# Function        : SvnLocateWsRoot
864
#
865
# Description     : Given a WorkSpace, determine the root of the work space
866
#                   This is not as simple as you might think
867
#
868
#                   Algorithm
869
#                       svn ls ..
870
#                       Am I in the parent directory
871
#                       Repeat
872
#
369 dpurdie 873
#                   Updates 'WS' and 'WSURL'
874
#
267 dpurdie 875
# Inputs          : $self               - Instance data
876
#                   $test               - True: Don't die on error
877
#
878
# Returns         : Root of workspace as an absolute address
879
#                   Will not return if there is an error
880
#
881
sub SvnLocateWsRoot
882
{
883
    my ($self, $test) = @_;
884
    my @path;
885
    my $path = $self->{WS};
886
 
887
    Debug ("SvnLocateWsRoot");
888
    Error ("SvnLocateWsRoot: No Workspace") unless ( $path  );
889
    Verbose2 ("SvnLocateWsRoot: Start in $path");
890
 
891
    #
892
    #   Validate the source path
893
    #
894
    if ( SvnValidateWs ($self, 'SvnLocateWsRoot', $test) )
895
    {
896
        return undef;
897
    }
898
 
899
    #
900
    #   Need to sanitize the users path to ensure that the following
901
    #   algorithm works. Need:
902
    #       1) Absolute Path
903
    #       2) Not ending in '/'
904
    #
905
 
906
    #
907
    #   If we have a relative path then prepend the current directory
908
    #   An absolute path is:
909
    #           /aaa/aa/aa
910
    #       or  c:/aaa/aa/aa
911
    #
912
    $path = getcwd() . '/' . $path
913
        unless ( $path =~ m~^/|\w:/~  );
914
 
915
    #
916
    #   Walk the bits and remove ".." directories
917
    #       Done by pushing non-.. elements and poping last entry for .. elements.
918
    #   Have a leading "/" which is good.
919
    #
920
    #   Create a array of directories in the path
921
    #   Split on one or more \ or / separators
922
    #
923
    foreach ( split /[\\\/]+/ , $path )
924
    {
925
        next if ( $_ eq '.' );
926
        unless ( $_ eq '..' )
927
        {
928
            push @path, $_;
929
        }
930
        else
931
        {
932
            Error ("SvnLocateWsRoot: Bad Pathname: $path")
933
                if ( $#path <= 0 );
934
            pop @path;
935
        }
936
    }
937
 
369 dpurdie 938
    #
939
    #   Need to adjust the WSURL too
940
    #   Break into parts and pop them off as we go
941
    #   Add a dummy one to allow for the first iteration
942
    #
943
    my @wsurl = (split (/[\\\/]+/ , $self->{WSURL}), 'Dummy');
944
 
267 dpurdie 945
    Verbose2 ("Clean absolute path elements: @path");
946
    PATH_LOOP:
947
    while ( @path )
948
    {
949
        #
950
        #   This directory element. Append / to assist in compare
951
        #   Determine parent path
952
        #
953
        my $name = pop (@path) . '/';
954
        my $parent = join ('/', @path );
369 dpurdie 955
        pop @wsurl;
267 dpurdie 956
 
957
        #
958
        #   Examine the parent directory
959
        #   Get a list of all elements in the parent
960
        #   Need to ensure that this directory is one of them
961
        #
962
        #   Ignore any errors - assume that they are because the
963
        #   parent is not a part of the work space. This will terminate the
964
        #   search.
965
        #
966
        $self->SvnCmd ('list', $parent, '--depth', 'immediates' );
967
        foreach my $entry ( @{$self->{RESULT_LIST}} )
968
        {
969
            next PATH_LOOP
970
                if ( $entry eq $name );
971
        }
972
 
973
        #
974
        #   Didn't find 'dir' in directory svn listing of parent
975
        #   This parent is not a part of the same WorkSpace as 'dir'
976
        #   We have a winner.
977
        #
978
        chop $name;                         #   Chop the '/' previously added
979
        $self->{WS} = $parent . '/' . $name;
369 dpurdie 980
 
981
        #
982
        #   Reform the WSURL. Elements have been removed as we tested up the
983
        #   path
984
        #
985
        $self->{WSURL} = join '/', @wsurl;
986
 
267 dpurdie 987
        return $self->{WS};
988
    }
989
 
990
    #
991
    #   Shouldn't get this far
992
    #
993
    Error ("SvnLocateWsRoot: Root not found");
994
}
995
 
996
#-------------------------------------------------------------------------------
997
# Function        : SvnValidateWs
998
#
999
# Description     : Validate the path to a working store
1000
#
1001
# Inputs          : $self           - Instance data
1002
#                   $user           - Optional prefix for error messages
1003
#                   $test           - True: Just test, Else Error
1004
#
1005
# Returns         : Will not return if not a workspace
1006
#                   Returns the users path
1007
#                   @{$self->{RESULT_LIST}} will be populated with info about the
1008
#                   item requested as per an 'info' call
1009
#
1010
sub SvnValidateWs
1011
{
1012
    my ($self, $user, $test) = @_;
1013
    Debug ("SvnValidateWs");
1014
 
1015
    $user = "Invalid Subversion Workspace" unless ( $user );
1016
    my $path = $self->{WS} ;
1017
 
1018
    #
1019
    #   Only validate it one
1020
    #
1021
    return $path if ( $self->{WS_VALIDATED} );
1022
 
1023
    #
1024
    #   Validate the source path
1025
    #   Must exist and must be a directory
1026
    #
1027
    if ( ! $path ) {
1028
        @{$self->{ERROR_LIST}} = "$user: No path specified";
1029
 
1030
    } elsif ( ! -e $path ) {
1031
        @{$self->{ERROR_LIST}} = "$user: Path does not exist: $path";
1032
 
1033
    } elsif ( ! -d $path ) {
1034
        @{$self->{ERROR_LIST}} = "$user: Path is not a directory";
1035
    } else {
1036
        #
1037
        #   Determine the source path is an fact a view
1038
        #   The info command can do this. Use depth empty to limit the work done
1039
        #
1040
        $self->SvnCmd ('info', $path, '--depth', 'empty' );
1041
 
1042
        #
1043
        #   Error. Prepend nice message
1044
        #
1045
        unshift @{$self->{ERROR_LIST}}, "$user: Path is not a WorkSpace: $path"
1046
            if ( @{$self->{ERROR_LIST}} );
1047
    }
1048
 
1049
    #
1050
    #   Figure out what to do
1051
    #
1052
    if ( $test )
1053
    {
1054
        return @{$self->{ERROR_LIST}};
1055
    }
1056
    else
1057
    {
1058
        Error @{$self->{ERROR_LIST}} if @{$self->{ERROR_LIST}};
1059
        $self->{WS_VALIDATED} = 1;
1060
        return $path;
1061
    }
1062
}
1063
 
1064
#-------------------------------------------------------------------------------
1065
# Function        : SvnValidatePackageRoot
1066
#
1067
# Description     : Validate a package root
1068
#
1069
# Inputs          : $self           - Instance data
1070
#
1071
# Returns         : Will only return if valid
1072
#                   Returns a cleaned package root
1073
#
1074
sub SvnValidatePackageRoot
1075
{
379 dpurdie 1076
    my ($self, $warning_only) = @_;
267 dpurdie 1077
    Debug ("SvnValidatePackageRoot");
1078
    my $url = $self->Full || Error ("SvnValidatePackageRoot: No URL");
1079
 
1080
    Error ("Package path contains a reserved word ($self->{TAGTYPE})", "Path: $url")
1081
        if (  $self->{TAGTYPE} );
1082
 
1083
    Error ("Package name contains a Peg ($self->{PEG})", "Path: $url")
1084
        if ( $self->{PEG} );
1085
 
1086
    #
1087
    #   Ensure that the target path does exist
1088
    #   Moreover it needs to be a directory and it should have a
1089
    #   a ttb structure
1090
    #
1091
    my ( $ref_files, $ref_dirs, $ref_svn, $found ) = $self->SvnScanPath ( 'Package Base Test', $url );
1092
 
1093
    #
379 dpurdie 1094
    #   Only looking for package path
1095
    #
1096
    if ( !$found && $warning_only )
1097
    {
1098
        return $url;
1099
    }
1100
 
1101
    #
267 dpurdie 1102
    #   Examine the results to see if we have a valid package base
1103
    #
1104
    Error ("Package Base Test: Not a valid package") unless ( $found );
1105
 
1106
    #
1107
    #   Extra bits found
1108
    #   Its not the root of a package
1109
    #
1110
    if ( @$ref_files )
1111
    {
1112
        Warning ("Package Base Test: Files exists",
1113
               "Unexpected files found:", @$ref_files );
1114
    }
1115
 
1116
    #
1117
    #   Need a truck directory
1118
    #   If we don't have a truck we don't have a package
1119
    #
1120
    my $trunk_found = grep ( /trunk\//, @$ref_svn );
1121
    Error ("Invalid Package Base. Does not contain a 'trunk' directory")
1122
        unless ( $trunk_found );
1123
 
1124
    return $url;
1125
}
1126
 
1127
 
1128
#-------------------------------------------------------------------------------
1129
# Function        : SvnIsaSimpleLabel
1130
#
1131
# Description     : Check a label
1132
#                       Must not contain a PEG
1133
#                       Must not contain invalid characters (@ or /)
1134
#                       Must not contain a :: sequence (will confuse other tools)
1135
#                       Handle special label of TIMESTAMP
1136
#
1137
# Inputs          : $label          - to test
1138
#
1139
# Returns         : Will not return on error
1140
#                   Returns label on success
1141
#
1142
sub SvnIsaSimpleLabel
1143
{
1144
    my ($label) = @_;
1145
    Debug ("SvnIsaSimpleLabel, $label");
1146
 
1147
    Error ("No label provided") unless ( $label );
1148
    Error ("Invalid label. Peg (\@nnn) is not allowed: \"$label\"" ) if ( $label =~ m~@\d+$~ );
1149
    Error ("Invalid label. Package Path is not allowed: \"$label\"" ) if ( $label =~ m~/~ );
383 dpurdie 1150
    Error ("Invalid label. Invalid Start Character: \"$label\"" ) unless ( $label =~ m~^[0-9a-zA-Z]~ );
1151
    Error ("Invalid label. Invalid End Character: \"$label\"" ) unless ( $label =~ m~[0-9a-zA-Z]$~ );
267 dpurdie 1152
    Error ("Invalid label. Invalid Characters: \"$label\"" ) unless ( $label =~ m~^[-.:0-9a-zA-Z_]+$~ );
1153
    Error ("Invalid label. Double :: not allowed: \"$label\"" ) if ( $label =~m~::~ );
1154
 
1155
    #
1156
    #   Allow for a label of TIMESTAMP and have it expand
383 dpurdie 1157
    #   Create a label based on users name and a date-time that can be sorted
267 dpurdie 1158
    #
1159
    if ( $label eq 'TIMESTAMP' )
1160
    {
341 dpurdie 1161
        ::EnvImport ('USER' );
1162
        my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
1163
        $label = sprintf("%s_%4.4u.%2.2u.%2.2u.%2.2u%2.2u%2.2u",
1164
            $::USER, $year+1900, $mon+1, $mday, $hour, $min, $sec );
267 dpurdie 1165
    }
1166
    return $label;
1167
}
1168
 
1169
#-------------------------------------------------------------------------------
1170
# Function        : NewSession
1171
#
1172
# Description     : Create a new empty SvnSession Class
1173
#
1174
# Inputs          : None
1175
#
1176
# Returns         : Class
1177
#
1178
sub NewSession
1179
{
1180
    Debug ("NewSession");
1181
    my $self  = SvnSession();
1182
 
1183
    #
1184
    #   Document Class Variables
1185
    #
1186
    $self->{URL} = '';                  # Repo URL prefix
1187
    $self->{WS}  = '';                  # Users WorkSpace
1188
    $self->{PROTOCOL} = '';             # Named Access Protocol
1189
    $self->{PKGROOT} = '';              # Package root
1190
 
1191
    #
1192
    #   Create a class
1193
    #   Bless my self
1194
    #
1195
    bless ($self, __PACKAGE__);
1196
    return $self;
1197
}
1198
 
1199
#-------------------------------------------------------------------------------
1200
# Function        : NewSessionByWS
1201
#
1202
# Description     : Establish a new SVN Session based on a Workspace
1203
#                   Given a workspace path determine the SvnServer and other
1204
#                   relevent information.
1205
#
1206
#                   Requires some rules
1207
#                       * The package is rooted within a 'ttb'
1208
#
1209
# Inputs          : $path                   - Path to WorkSpace
1210
#                   $test                   - No Error on no WS
361 dpurdie 1211
#                   $slack                  - Less stringent
267 dpurdie 1212
#
1213
# Returns         : Ref to Session Information
1214
#
1215
sub NewSessionByWS
1216
{
361 dpurdie 1217
    my ($path, $test, $slack) = @_;
267 dpurdie 1218
    Debug ("NewSessionByWS", @_);
1219
 
1220
    #
1221
    #   Create a basic Session
1222
    #   Populate it with information that is known
1223
    #
1224
    my $self = NewSession();
1225
    $self->{WS} = $path;
1226
 
1227
    #
1228
    #   Validate the path provided
1229
    #   In the process populate the @{$self->{RESULT_LIST}} with information
1230
    #   about the workspace.
1231
    #
1232
    if ($self->SvnValidateWs ( undef, 1) )
1233
    {
1234
        return $self if ( $test );
1235
        Error ( @{$self->{ERROR_LIST}} );
1236
    }
1237
 
1238
    #
1239
    #   Extract useful info
1240
    #       URL: svn://auperaws996vm21/test/MixedView/trunk
1241
    #       Repository Root: svn://auperaws996vm21/test
1242
    #
1243
    my $url;
1244
    my $reporoot;
369 dpurdie 1245
    my $repoVersion;
267 dpurdie 1246
 
1247
    foreach ( @{$self->{RESULT_LIST}} )
1248
    {
1249
        $url = $1 if ( m~^URL:\s+(.+)~ );
1250
        $reporoot = $1 if ( m~^Repository Root:\s+(.+)~ );
369 dpurdie 1251
        $repoVersion = $1 if ( m~^Revision:\s+(.+)~ );
1252
        last if ( $url && $reporoot && $repoVersion);
267 dpurdie 1253
    }
1254
    Error ("JatsSvn Internal error. Can't parse info")
1255
        unless ( $url && $reporoot );
1256
 
1257
    #
1258
    #   Need the length of the path to the repository
1259
    #   but not the name of the repostory itself.
1260
    #
1261
    #   Remove that from the head of the URL to give a
1262
    #   path within the repository, that includes the repos name
1263
    #
1264
    $reporoot = (fileparse( $reporoot ))[1];
1265
    $url = substr ($url, length ($reporoot));
1266
    $self->{WSURL} = $url;
1267
    chop $reporoot;
1268
 
1269
    Verbose2 ("SvnLocatePackageRoot: $reporoot, $url" );
1270
 
1271
    #
1272
    #   Remove anything after a ttb ( truck, tags, branch ) element
1273
    #   This will be the root of the package within the repo
1274
    #
1275
    if (  $url =~ m~(.+)/((tags|branches|trunk)(/|$).*)~ )
1276
    {
1277
        $url = $1;
1278
        $self->{WSTYPE} = $3;
1279
    }
1280
    else
1281
    {
361 dpurdie 1282
        #
1283
        #   If we are being slack (ie deleting the workspace)
1284
        #   Then generate a warning, not an error
1285
        #
1286
        my $fnc = $slack ? \&Warning : \&Error;
1287
        $fnc->("SvnLocatePackageRoot. Non standard repository format",
1288
               "Url must contain 'tags' or 'branches' or 'trunk'",
267 dpurdie 1289
               "Url: $url");
361 dpurdie 1290
        $self->{WSTYPE} = 'trunk';
267 dpurdie 1291
    }
1292
 
1293
    #
1294
    #   Insert known information
1295
    #
1296
    $self->{URL} = $reporoot . '/';
1297
    $self->{PKGROOT} = $url;
369 dpurdie 1298
    $self->{WSREVNO} = $repoVersion;
267 dpurdie 1299
 
1300
    #
1301
    #   Create useful information
1302
    #
1303
    SplitPackageUrl($self);
1304
    return $self;
1305
}
1306
 
1307
#-------------------------------------------------------------------------------
1308
# Function        : NewSessionByUrl
1309
#
1310
# Description     : Establish a new SVN Session based on a user URL
1311
#
1312
# Inputs          : $uurl                   - Users URL
361 dpurdie 1313
#                   $ttb_test               - Test and warn for TTB structure
267 dpurdie 1314
#                   $session                - Optional: Existing session
1315
#
1316
# Returns         : Ref to Session Information
1317
#
1318
sub NewSessionByUrl
1319
{
361 dpurdie 1320
    my ($uurl, $ttb_test, $self ) = @_;
267 dpurdie 1321
    Debug ("NewSessionByUrl", @_);
1322
    Error ("No Repostory Path specified") unless ( $uurl );
1323
 
1324
    #
1325
    #   Create a basic Session
1326
    #   Populate it with information that is known
1327
    #
1328
    $self = NewSession() unless ( $self );
1329
 
1330
    #
369 dpurdie 1331
    #   Examine the URL and convert a Repository Path into a URL
353 dpurdie 1332
    #   as provided by configuration information within the environment
267 dpurdie 1333
    #
361 dpurdie 1334
    ($self->{URL}, $self->{PKGROOT} ) = SvnPath2Url ($uurl);
1335
 
1336
    #
1337
    #   Create useful information
1338
    #
1339
    SplitPackageUrl($self);
1340
 
1341
    #
1342
    #   Warn of non-standard URLs
1343
    #   These may create problems latter
1344
    #
1345
    if ( $ttb_test )
1346
    {
1347
        Warning("Non standard repository format",
1348
                 "Url should contain 'tags' or 'branches' or 'trunk'",
1349
                 "Url: $self->{PKGROOT}") unless $self->{TAGTYPE};
1350
    }
1351
 
1352
    return $self;
1353
}
1354
 
1355
#-------------------------------------------------------------------------------
1356
# Function        : SvnPath2Url
1357
#
1358
# Description     : Convert a repository path to a Full Url
1359
#                   Also handles Full Url
1360
#
1361
# Inputs          : $rpath             - Repository Path
1362
#                                        May be a full URL
1363
#
1364
# Returns         : List context
1365
#                   Two items that can be joined
1366
#                   URL                - URL
1367
#                   PKG_ROOT           - Package Root
1368
#
1369
#                   Scalar context: Joined URL and Package Root
1370
#                                   Fully formed URL
1371
#
1372
sub SvnPath2Url
1373
{
1374
    my ($rpath) = @_;
1375
    my $processed = 0;
1376
    my $url;
1377
    my $pkgroot;
1378
 
1379
    #
1380
    #   Examine the argument and convert a Repository Path into a URL
1381
    #   as provided by configuration information within the environment
1382
    #
1383
    $rpath =~ m~(.+?)/(.*)~;
1384
    my $fe = $1 || $rpath;
353 dpurdie 1385
    my $rest = $2 || '';
361 dpurdie 1386
 
353 dpurdie 1387
    if ( $SVN_URLS{$fe} )
267 dpurdie 1388
    {
361 dpurdie 1389
        $url = $SVN_URLS{$fe};
1390
        $pkgroot = $rest;
353 dpurdie 1391
        $processed = 1;
341 dpurdie 1392
    }
1393
 
353 dpurdie 1394
    if ( ! $processed )
341 dpurdie 1395
    {
267 dpurdie 1396
        #
353 dpurdie 1397
        #   Examine the URL and determine if we have a FULL Url or
1398
        #   a path within the 'default' server
1399
        #
1400
        foreach my $key ( @SVN_URLS_LIST )
1401
        {
361 dpurdie 1402
            if ( $rpath =~ m~^$SVN_URLS{$key}(.*)~ )
353 dpurdie 1403
            {
361 dpurdie 1404
                $url = $SVN_URLS{$key};
1405
                $pkgroot = $1;
353 dpurdie 1406
                $processed = 1;
1407
                last;
1408
            }
1409
        }
1410
    }
267 dpurdie 1411
 
353 dpurdie 1412
    #
1413
    #   Last attempt
1414
    #   Treat as a raw URL - some operations won't be allowed
1415
    #
1416
    if ( ! $processed )
267 dpurdie 1417
    {
361 dpurdie 1418
        if ( $rpath =~ m~^((file|http|https|svn):///?([^/]+)/)(.+)~ )
353 dpurdie 1419
        {
1420
            #       http://server/
1421
            #       https://server/
1422
            #       svn://server/
1423
            #       file://This/Isa/Bad/Guess
1424
            #
361 dpurdie 1425
            $url = $1;
1426
            $pkgroot = $4;
353 dpurdie 1427
        }
369 dpurdie 1428
        elsif ($SVN_URLS{''} )
353 dpurdie 1429
        {
1270 dpurdie 1430
            if ( exists $ENV{'GBE_ABT'} && $ENV{'GBE_ABT'})
1431
            {
1432
                Error ("Attempt to use default repository within automated build", "Path: " . $rpath);
1433
            }
361 dpurdie 1434
            $url = $SVN_URLS{''};
1435
            $pkgroot = $rpath;
353 dpurdie 1436
        }
1437
        else
1438
        {
1439
            #
1440
            #   User default (site configured) Repo Root
1441
            #
1442
            Error ("No site repository configured for : $fe",
1443
                   "Configure GBE_SVN_URL_" . uc($fe) );
1444
        }
267 dpurdie 1445
    }
1446
 
1447
    #
361 dpurdie 1448
    #   May want two elements, may want one
267 dpurdie 1449
    #
361 dpurdie 1450
    return $url, $pkgroot if ( wantarray );
1451
    return $url . $pkgroot;
267 dpurdie 1452
}
1453
 
369 dpurdie 1454
#-------------------------------------------------------------------------------
1455
# Function        : SvnPaths
1456
#
1457
# Description     : Extract SVN path conversion information
1458
#
1459
# Inputs          : Nothing
1460
#
1461
# Returns         : Two refs
1462
#                   Hash of SVN URLS
1463
#                   Array for search order
1464
#
1465
sub SvnPaths
1466
{
1467
    return \%SVN_URLS, \@SVN_URLS_LIST;
1468
}
361 dpurdie 1469
 
267 dpurdie 1470
#-------------------------------------------------------------------------------
1471
# Function        : SplitPackageUrl
1472
#
1473
# Description     : Slip the package URL into a few useful bits
1474
#
1475
# Inputs          : $self           - Instance data
1476
#
1477
# Returns         : Nothing
1478
#
1479
sub SplitPackageUrl
1480
{
1481
    my ($self) = @_;
353 dpurdie 1482
    Debug ("SplitPackageUrl", $self->{URL}, $self->{PKGROOT});
267 dpurdie 1483
 
1484
    #
1485
    #   Remove any protocol that may be present
1486
    #       http://server/
341 dpurdie 1487
    #       https://server/
267 dpurdie 1488
    #       svn://server/
1489
    #       file://This/Isa/Bad/Guess
1490
    #
341 dpurdie 1491
    if ( $self->{URL} =~ m~^(file|http|https|svn)://([^/]+)~ )
267 dpurdie 1492
    {
1493
        $self->{PROTOCOL} = $1;
1494
        $self->{SERVER} = $2;
1495
    }
1496
 
1497
    if ( $self->{PKGROOT} =~ m~(.*)(@\d+)$~ )
1498
    {
1499
        $self->{PEG} = $2;
1500
    }
1501
 
1502
    #
1503
    #   Determine TTB type
1504
    #   Need to handle
1505
    #       .../trunk
1506
    #       .../trunk@nnnnn
1507
    #       .../tags/version@nnnnn
1508
    #       .../branches/version@nnnnn
1509
    #
1510
    #
1511
    if (  $self->{PKGROOT} =~ m~/?(.*)/(tags|branches|trunk)(/|$|@)(.*)$~ )
1512
    {
1513
        $self->{PATH}         = $1;
1514
        $self->{TAGTYPE}      = $2;
1515
        $self->{VERSION}      = $4;
1516
    }
1517
    else
1518
    {
1519
        $self->{PATH} = $self->{PKGROOT};
1520
    }
1521
 
1522
    DebugDumpData ('SplitPackageUrl', $self ) if ( IsDebug(2) );
1523
}
1524
 
1525
#-------------------------------------------------------------------------------
1526
# Function        : Full
1527
#                   FullWs
1528
#                   Repo
1529
#                   Peg
1530
#                   Type
1531
#                   WsType
1532
#                   Path
1533
#                   Version
1534
#                   RmRef
385 dpurdie 1535
#                   RmPath
267 dpurdie 1536
#
1537
# Description     : Accessor functions
1538
#
1539
# Inputs          : $self       - Instance data
1540
#                                 self (is $_[0])
1541
#
1542
# Returns         : Data Item
1543
#
369 dpurdie 1544
sub Full        { return $_[0]->{URL} . $_[0]->{PKGROOT} ; }
1545
sub FullWs      { return $_[0]->{URL} . $_[0]->{WSURL} ; }
1546
sub FullWsRev   { return $_[0]->{URL} . $_[0]->{WSURL} . '@' . $_[0]->{WSREVNO} ; }
1547
sub Peg         { return $_[0]->{PEG} ; }
1548
sub Type        { return $_[0]->{TAGTYPE} || '' ; }
1549
sub WsType      { return $_[0]->{WSTYPE}  || '' ; }
1550
sub Path        { return $_[0]->{PATH} ; }
1551
sub Version     { return $_[0]->{VERSION} ; }
1552
sub RmRef       { return $_[0]->{RMREF} ; }
385 dpurdie 1553
sub RmPath      { my $path = $_[0]->{RMREF}; $path =~ s~@.*?$~~ ;return  $path; }
267 dpurdie 1554
 
1555
#-------------------------------------------------------------------------------
1556
# Function        : Print
1557
#
1558
# Description     : Debug display the URL
1559
#
1560
# Inputs          : $self           - Instance data
1561
#                   $header
1562
#                   $indent
1563
#
1564
# Returns         : Nothing
1565
#
1566
sub Print
1567
{
1568
    my ($self, $header, $indent) = @_;
1569
    print "$header\n" if $header;
1570
    $indent = 4 unless ( defined $indent );
1571
    $indent = ' ' x $indent;
1572
 
1573
 
1574
    print $indent . "PROTOCOL:" . $self->{PROTOCOL} . "\n";
1575
    print $indent . "SERVER  :" . $self->{SERVER} . "\n";
1576
    print $indent . "URL     :" . $self->{URL} . "\n";
1577
    print $indent . "PKGROOT :" . $self->{PKGROOT} . "\n";
1578
    print $indent . "PATH    :" . $self->{PATH} . "\n";
353 dpurdie 1579
    print $indent . "TAGTYPE :" . ($self->{TAGTYPE} || '') . "\n";
1580
    print $indent . "VERSION :" . ($self->{VERSION} || '') . "\n";
1581
    print $indent . "PEG     :" . ($self->{PEG} || '') . "\n";
267 dpurdie 1582
    print $indent . "FULL    :" . $self->Full . "\n";
1583
}
1584
 
1585
#-------------------------------------------------------------------------------
1586
# Function        : BranchName
1587
#
1588
# Description     : Create a full URL to a branch or tag based on the
1589
#                   current entry
1590
#
1591
#                   URL must have a TTB format
1592
#
1593
# Inputs          : $self           - Instance data
1594
#                   $branch         - Name of the branch
1595
#                   $type           - Optional branch type
1596
#
1597
# Returns         : Full URL name to the new branch
1598
#
1599
sub BranchName
1600
{
1601
    my ($self, $branch, $type ) = @_;
1602
    Debug ( "BranchName", $branch );
1603
 
1604
    $type = 'branches' unless ( $type );
1605
    my $root = $self->{PKGROOT};
1606
 
1607
    $root =~ s~/(tags|branches|trunk)(/|$|@).*~~;
1608
 
1609
    return $self->{URL} . $root . '/' . $type . '/' . $branch;
1610
}
1611
 
379 dpurdie 1612
#-------------------------------------------------------------------------------
1613
# Function        : setRepoProperty
1614
#
1615
# Description     : Sets a Repository property
1616
#                   This may well fail unless the Repo is setup to allow such
1617
#                   chnages and the user is allowed to make such changes
1618
#
1619
# Inputs          : $name
1620
#                   $value
1621
#
1622
# Returns         : Will not return on error
1623
#
1624
sub setRepoProperty
1625
{
1626
    my ($self, $name, $value ) = @_;
1627
    Debug ( "setRepoProperty", $name, $value );
1628
    #
1629
    #   Ensure that the Repo version is known
1630
    #   This should be set by a previous operation
1631
    #
1632
    unless ( defined $self->{REVNO} )
1633
    {
1634
        Error ("setRepoProperty. Release Revision Number not known");
1635
    }
1636
 
1637
    #
1638
    #   Execute the command
1639
    #
1640
    if ( $self->SvnCmd ( 'propset' , $name, '--revprop', '-r',  $self->{REVNO}, $value, $self->Full,
1641
                            {
1642
                                'credentials' => 1,
1643
                                'nosavedata' => 1,
1644
                            }
1645
                       ) )
1646
    {
1647
        #
1648
        #   Property NOT set
1649
        #
1650
        Error ("setRepoProperty: $name - FAILED");
1651
    }
1652
}
1653
 
267 dpurdie 1654
#------------------------------------------------------------------------------
1655
1;