Subversion Repositories DevTools

Rev

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

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