Subversion Repositories DevTools

Rev

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