Subversion Repositories DevTools

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
6133 dpurdie 1
########################################################################
7300 dpurdie 2
# COPYRIGHT - VIX IP PTY LTD ("VIX"). ALL RIGHTS RESERVED.
6133 dpurdie 3
#
4
# Module name   : jats_generate_deployable.pl
5
# Module type   : Makefile system
6
# Compiler(s)   : Perl
7
# Environment(s): jats build system
8
#
9
# Description   : Extracts current package version list from release manager
10
#                 based on the 'IS_DEPOLYABLE' flag in a given Release
11
#                 and copies resultant packages to release specific
12
#                 directory.
13
#                 
14
#                 Based on jats_update_release.pl and jats_gen_bom.pl but it is 
15
#                 intended to be used by the PULSE digital distribution process
16
#......................................................................#
17
 
18
require 5.008_002;
19
use File::Basename;
20
use File::Copy;
21
use File::Path;
22
use strict;
23
use warnings;
24
use JatsEnv;
25
use JatsError;
26
use JatsRmApi;
27
use ArrayHashUtils;
28
use FileUtils;
29
use DBI;
30
use Getopt::Long;
31
use Pod::Usage;                             # required for help support
32
use JSON;
33
 
34
#
35
#   Config Options
36
#
37
my $VERSION = "1.0.0";                      # Update this
38
my $opt_help = 0;
39
my $opt_verbose = $ENV{'GBE_VERBOSE'};      # Allow global verbose
40
my $opt_rtagid;
41
my $opt_rootdir = '.';
42
my $opt_test;
43
my $opt_showFilters;
44
my @opt_addFilters;
45
my @opt_delFilters;
46
my $opt_showFiles;
47
my @opt_addFiles;
48
my @opt_delFiles;
7307 dpurdie 49
my $opt_symlinks;
7319 dpurdie 50
my $opt_ignore;
6133 dpurdie 51
 
52
#
53
#   Constants
54
#
55
my $CONFFILE = '.bomGen';
7304 dpurdie 56
my $BOMFILE = '.bomCots';
6133 dpurdie 57
my $MANIFEST = 'MANIFEST.json';
58
my $TFVARS   = 'MANIFEST.tf';
59
 
60
#
61
#   Globals
62
#
63
my $DM_DB;              # Data Base Interface
64
my %bomList;            # All files in the BOM
65
my $bomInfo;            # Sbom meta data
7300 dpurdie 66
my %baseList;           # List of files in bin
6133 dpurdie 67
 
68
#
69
#   Configuration file vars
70
#
71
my @confFilters;
72
my @confFiles;
73
my %filtersUsed;
74
 
75
#-------------------------------------------------------------------------------
76
# Function        : Main
77
#
78
# Description     : Main entry point
79
#                   Parse user options
80
#
81
# Inputs          :
82
#
83
# Returns         :
84
#
85
 
86
my $result = GetOptions (
87
                "help:+"            => \$opt_help,              # flag, multiple use allowed
88
                "manual:3"          => \$opt_help,              # flag, multiple use allowed
89
                "verbose:+"         => \$opt_verbose,           # flag
90
                "rtagid|rtag_id=s"  => \$opt_rtagid,            # Number
91
                "rootdir=s"         => \$opt_rootdir,           # string
7307 dpurdie 92
                "symlinks!"         => \$opt_symlinks,          # Boolean
6133 dpurdie 93
 
94
                "addfilter=s"       => \@opt_addFilters,        # multiple strings
95
                "delfilter=s"       => \@opt_delFilters,        # multiple strings
96
                "showfilters"       => \$opt_showFilters,       # flag
97
 
98
                "addfiles=s"       => \@opt_addFiles,           # multiple strings
99
                "delfiles=s"       => \@opt_delFiles,           # multiple strings
100
                "showfiles"        => \$opt_showFiles,          # flag
101
 
102
                "test"              => \$opt_test,              # flag
7319 dpurdie 103
                'ignoreManifest'    => \$opt_ignore,            # flag
6133 dpurdie 104
                );
105
 
106
#
107
#   Process help and manual options
108
#
109
pod2usage(-verbose => 0, -message => "Version: $VERSION")  if ($opt_help == 1  || ! $result);
110
pod2usage(-verbose => 1)  if ($opt_help == 2 );
111
pod2usage(-verbose => 2)  if ($opt_help > 2);
112
 
113
ErrorConfig( 'name'    => 'GenDeploy',
114
             'verbose' => $opt_verbose );
115
 
116
#
117
#   Sanity tests
118
#
119
 
120
# Supplied rootdir must exists as a directory
121
Error("Root dir not specified") 
122
    unless defined $opt_rootdir;
123
Error("Root dir not a valid directory: ", $opt_rootdir )
124
    unless( -d $opt_rootdir );
125
 
126
    # Environment var GBE_DPKG must exists as a directory
127
Error("GBE_DPKG Environment var is not a directory")
128
    unless ( -d $ENV{GBE_DPKG} );
129
 
130
LoadFilterConfig();
131
ProcessFilterArgs();
132
 
133
#   Non Filter operations
134
#   Must supply an rtagid
135
Error("Need --rtagid", "Example: -rtagid=2362" )
136
    unless ($opt_rtagid);
137
 
7304 dpurdie 138
Error("No Filters defined.", "Add filters before creating BOM") 
139
    unless ( @confFilters );
140
 
6133 dpurdie 141
#
142
#   This command is destined to be used in a directory where group permissions
143
#   are important. Ensure that the user is not killing group access
144
#
145
umask 0002;
146
 
147
#
148
#   Body of the processing
149
#       Save generation time into the meta data
150
my $now = time;
151
$bomInfo->{version} = "2.0.0";
152
$bomInfo->{timestamp}{epoch} = $now;
153
$bomInfo->{timestamp}{utc} = gmtime($now);
154
 
7307 dpurdie 155
Message("Copying packages from $ENV{GBE_DPKG} to $opt_rootdir") unless $opt_symlinks;
156
Message("Symlink packages from $opt_rootdir to $ENV{GBE_DPKG}") if $opt_symlinks;
6133 dpurdie 157
 
158
#
159
#   Processing
160
#
161
connectRM(\$DM_DB);
162
GetReleaseInfo();               # Get Release Metadata
163
GetPackageData();               # Get RM Data
164
RemoveDuplicates();             # Need P or D, but not both
165
CopyInNew();                    # Copy new files
166
RemoveExcess();                 # Remove files no longer required
167
GenFileData();                  # Generate file metadata
168
WriteManifest();                # Save out meta data
169
exit 0;
170
 
171
#-------------------------------------------------------------------------------
172
# Function        : GenFileData 
173
#
174
# Description     : Generate meta data on each file
175
#                   Much of this is a guess.
176
#                   Assume files look like:
177
#                   
178
#                       VIXcryptoKeyManager-1.0.2061.cr-WIN32.exe
179
#                       erg-pkgmnt_1.0.3010.cr_UBUNTU16_P.deb
180
#                       erg-pkgmnt_1.0.3010.cr_RHEL7_P.rpm
181
#                       xxxxxx.sh - bit trickier
182
#
183
# Inputs          : None
184
#
185
# Returns         : Populates $bomInfo
186
#
187
sub GenFileData
188
{
189
    my @elist;
190
    my @edup;
191
    foreach my $file (sort keys %bomList)
192
    {
193
        my $data;
194
        my $alias;
195
 
196
        $bomList{$file}{version} =~ m~(.*)\.([a-z]+)$~;
197
        my $pvfull = $1;
198
        my $proj = $2;
199
        my $pv = $pvfull;
200
        $pv =~ s~\.\d+$~~;
201
 
202
        if ($file =~ m~^(.*)-(.*)\.(.*)-(WIN.*)\.(exe)$~i)
203
        {
204
            $data->{name} = $1;
205
            $data->{version} = $2;
206
            $data->{prj} = $3;
207
            $data->{arch} = $4;
208
            $data->{type} = $5;
209
        }
7311 dpurdie 210
        elsif ( $file =~ m~^(.*)-(.*)\.([a-z]+)-(.*)\.(.*)\.(rpm)$~i)
211
        {
212
            $data->{name} = $1;
213
            $data->{version} = $2;
214
            $data->{prj} = $3;
215
            $data->{release} = $4;
216
            $data->{arch} = $5;
217
            $data->{type} = $6;
218
            $data->{release} =~ s~[PD]$~~;
219
            $alias = join('.', $data->{name}, $data->{prj}, $data->{release}, $data->{arch}, $data->{type} );
220
        }
6133 dpurdie 221
        elsif ( $file =~ m~^(.*)_(.*)\.([^_]+)_(.*)\.(deb|tgz|rpm)$~i)
222
        {
7319 dpurdie 223
            # name_version.project_arch_type
224
#Debug0("File Mode 3: $file");
6133 dpurdie 225
            $data->{name} = $1;
226
            $data->{version} = $2;
227
            $data->{prj} = $3;
228
            $data->{arch} = $4;
229
            $data->{type} = $5;
230
            $data->{arch} =~ s~_[PD]~~;
7304 dpurdie 231
            $alias = join('.', $data->{name}, $data->{prj}, $data->{arch}, $data->{type} );
6133 dpurdie 232
        }
7306 dpurdie 233
        elsif ( $file =~ m~^(.*)-(.*)-(.*)\.(.*)\.(rpm)$~i)
234
        {
7319 dpurdie 235
            # name-version-release.arch.rpm
7306 dpurdie 236
            $data->{name} = $1;
237
            $data->{version} = $2;
238
            $data->{release} = $3;
239
            $data->{arch} = $4;
240
            $data->{type} = $5;
241
            $alias = join('.', $data->{name}, $data->{arch}, $data->{type} );
242
        }
7319 dpurdie 243
        elsif ( $file =~ m~^(.*)-(.*)-(.*)-(.*)\.(rpm)$~i)
244
        {
245
            # name-version-release-arch.rpm
246
            # Not a valid form - but that doesn't stop Oracle
247
            $data->{name} = $1;
248
            $data->{version} = $2;
249
            $data->{release} = $3;
250
            $data->{arch} = $4;
251
            $data->{type} = $5;
252
            $alias = join('.', $data->{name}, $data->{arch}, $data->{type} );
253
        }
254
 
6133 dpurdie 255
        elsif ( $file =~ m~^(.*)-($pv)\.(.*)\.(rpm)$~i)
256
        {
257
            # COTS package
258
            $data->{name} = $1;
259
            $data->{version} = $2;
260
            $data->{arch} = $3;
261
            $data->{type} = $4;
262
            $data->{prj} = $proj;
263
        }
264
        elsif ( $file =~ m~^(.*)_($pv)\.(tgz)$~i)
265
        {
266
            # COTS package
267
            $data->{name} = $1;
268
            $data->{version} = $2;
269
            $data->{arch} = 'UNKNOWN';
270
            $data->{type} = $3;
271
            $data->{prj} = $proj;
272
        }
273
        elsif ( $file =~ m~^(.*)-($pv)\.(.*)\.(deb)$~i)
274
        {
7319 dpurdie 275
#Debug0("File Mode 8: $file");
6133 dpurdie 276
            # COTS package
277
            $data->{name} = $1;
278
            $data->{version} = $2;
279
            $data->{arch} = $3;
280
            $data->{type} = $4;
281
            $data->{prj} = $proj;
282
        }
7319 dpurdie 283
        elsif ( $file =~ m~^(.*)_(.*)-(.*)_(.*)\.(deb)$~i)
284
        {
285
#Debug0("File Mode 9: $file");
286
            # COTS package
287
            $data->{name} = $1;
288
            $data->{version} = $2;
289
            $data->{arch} = $3;
290
            $data->{type} = $4;
291
            $data->{prj} = $proj;
292
        }
293
 
6133 dpurdie 294
        elsif ( $file =~ m~^(.*)\.(sh|zip|msi|tar\.gz)$~i)
295
        {
296
            $data->{name} = $1;
297
            $data->{arch} = 'NOARCH';
298
            $data->{type} = $2;
299
 
300
            $data->{version} = $pvfull;
301
            $data->{prj} = $proj;
302
        }
303
 
7306 dpurdie 304
        unless (($data && $data->{name} && $data->{prj} && $data->{type}) || $alias) {
6133 dpurdie 305
            push @elist, $file;
7319 dpurdie 306
Debug0("File: $file");
6133 dpurdie 307
            next;
308
        }
309
        $data->{fullname} = $file;
310
 
311
        #
312
        #   Create a nice alias
313
        #       ERG -> VIX  (not done)
314
        #       All lowercase
315
        #
7304 dpurdie 316
        $alias = join ('.', $data->{name}, $data->{prj}, $data->{type} ) unless defined $alias;
6133 dpurdie 317
        $alias = lc ($alias);
318
        #$alias =~ s~^erg~vix~;
319
        #$alias =~ s~^vix~vix-~;
320
        #$alias =~ s~^vix--~vix-~;
321
        push (@edup, join( ' : ', $alias, $file ,$bomInfo->{files}{$alias}{fullname})  ) if exists $bomInfo->{files}{$alias};
322
 
323
        delete $data->{type};
324
        $bomInfo->{files}{$alias} = $data;
7304 dpurdie 325
#Debug0("Alias: $file-> $alias");
6133 dpurdie 326
    }
327
 
7319 dpurdie 328
    if ($opt_ignore)
329
    {
330
        Warning ("Cannot extract file metadata from:", @elist)  if (@elist);
331
        Warning ("Duplicate aliases for:", @edup)  if (@edup);
332
    }
333
    else
334
    {
335
        ReportError ("Cannot extract file metadata from:", @elist)  if (@elist);
336
        ReportError ("Duplicate aliases for:", @edup)  if (@edup);
337
    }
6133 dpurdie 338
    ErrorDoExit();
339
}
340
 
341
#-------------------------------------------------------------------------------
342
# Function        : CopyInNew 
343
#
344
# Description     : Copy in new files
345
#                   Don't copy in files that already exist - assume that the
7307 dpurdie 346
#                   files don't change without a chnage to the file name
6133 dpurdie 347
#
348
# Inputs          : 
349
#
350
# Returns         : 
351
#
352
sub CopyInNew
353
{
354
    #
355
    #   Ensure the output directory exists
356
    #
357
    if ( ! -d $opt_rootdir )
358
    {
359
        if ( defined($opt_test) )
360
        {
361
            Message("mkdir $opt_rootdir");
362
        }
363
        else
364
        {
365
            eval { mkpath($opt_rootdir) };
366
            Error("Failed to make project directory tree $opt_rootdir") if ( $@ || ! -d $opt_rootdir );
367
        }
368
    }
369
 
370
    #
7300 dpurdie 371
    #   Generate a list of all files in the directory
372
    #
373
    foreach my $file ( glob("$opt_rootdir/*") ) {
374
        $baseList{$file}{data} = 1;
375
    }
376
 
377
    #
6133 dpurdie 378
    #   Determine the files to be transferred
379
    #
380
    my @filelist;
381
    foreach my $file ( keys %bomList)
382
    {
383
        push (@filelist, $file) unless ( -f "$opt_rootdir/$file" );
384
    }
385
 
386
    #
387
    #   Perform the actual copy
388
    #
389
    if ( @filelist )
390
    {
391
        #Message("Copying files for package $PKG_NAME version $PKG_VERSION");
392
        if ( defined($opt_test) )
393
        {
394
            Message( map("$_...", @filelist) );
395
        }
396
        else
397
        {
398
            eval { mkpath($opt_rootdir) };
399
            Error("Failed to make destination directory") if ( $@ || ! -d $opt_rootdir );
400
            foreach my $file ( @filelist )
401
            {
402
                my $srcFile = $bomList{$file}{path};
7307 dpurdie 403
                if ($opt_symlinks)
6133 dpurdie 404
                {
7307 dpurdie 405
                   Verbose("Symlink: $file");
406
                   my $fname = catdir( $opt_rootdir, StripDir($srcFile));
407
                    if ( ! symlink ($srcFile, $fname) )
408
                    {
409
                        Warning("Failed to symlink $file ($!)");
410
                    }
411
 
6133 dpurdie 412
                }
7307 dpurdie 413
                else
414
                {
415
                    Verbose("Copy: $file");
416
                    if ( ! copy($srcFile, $opt_rootdir) )
417
                    {
418
                        Warning("Failed to copy $file ($!)");
419
                    }
420
                }
6133 dpurdie 421
            }
422
        }
423
    }
424
}
425
 
426
#-------------------------------------------------------------------------------
427
# Function        : RemoveExcess 
428
#
429
# Description     : Remove excess files from the output directory 
430
#
431
# Inputs          : 
432
#
433
# Returns         : 
434
#
435
sub RemoveExcess
436
{
437
    my @filelist;
438
    my %keepList = map { $_ => 1 } @confFiles;
439
 
440
    #
441
    #   Find all files in the output directory
442
    #   Use the 'keepList' so that we don't pickup files that should
443
    #   be in the directory. README.md, MANIFEST ...
444
    #
445
    foreach my $srcPath ( glob("$opt_rootdir/*") )
446
    {
447
        my $dstFile = basename($srcPath);
448
        next if exists $keepList{$dstFile};
449
        next unless ( -f $srcPath );
450
 
451
        push (@filelist, $dstFile) unless (exists $bomList{$dstFile} );
452
    }
453
 
454
    if ( @filelist)
455
    {
7304 dpurdie 456
        Verbose ("Delete execess files", @filelist );
6133 dpurdie 457
        unless ( defined($opt_test) )
458
        {
459
            foreach my $file ( @filelist )
460
            {
7304 dpurdie 461
                Verbose2("Delete: $file...");
6133 dpurdie 462
                if ( unlink("$opt_rootdir/$file") ne 1 )
463
                {
464
                    Warning("Failed to delete: $file. ($!)");
465
                }
466
            }
467
        }
468
    }
7300 dpurdie 469
 
470
    #
471
    #   Report changed files
472
    #   Generate a list of all files in the directory
473
    #
474
    foreach my $file ( glob("$opt_rootdir/*") ) {
475
        $baseList{$file}{data} |= 2;
476
    }
477
 
478
    #
479
    #   Determined added, removed and replaced
480
    #       
481
    #
482
    my (@replaced, @added, @removed, @unchanged, %newList);
483
    foreach my $entry ( keys %baseList )
484
    {
485
        (my $key = $entry) =~ s~\d+~z~g;
486
        $baseList{$entry}{key} = $key;
487
 
488
        (my $name = $entry) =~ s~^\./~~;
489
        $newList{$key}{$baseList{$entry}{data}} = $entry;
490
    }
491
 
492
    foreach my $key ( sort keys %newList )
493
    {
494
        if (exists $newList{$key}{1} && exists $newList{$key}{2}  ) {
495
            push @replaced, "$newList{$key}{1}   =>   $newList{$key}{2}";
496
        } elsif (exists $newList{$key}{1}) {
497
            push @removed, $newList{$key}{1};
498
        } elsif (exists $newList{$key}{2}) {
499
            push @added, $newList{$key}{2};
500
        } elsif (exists $newList{$key}{3}) {
501
            push @unchanged, $newList{$key}{3};
502
        }
503
    }
504
 
505
    Message ("Unchanged: " .(@unchanged ? scalar(@unchanged ) : 'None') );
506
    Message ("Replaced: " . (@replaced ?  scalar(@replaced )  : 'None'), @replaced);
507
    Message ("Added: " .    (@added    ?  scalar(@added )     : 'None'), @added);
508
    Message ("Removed: " .  (@removed  ?  scalar(@removed )   : 'None'), @removed);
6133 dpurdie 509
}
510
 
511
#-------------------------------------------------------------------------------
512
# Function        : RemoveDuplicates 
513
#
514
# Description     : Scan the BOM file list and remove duplicate installers
515
#                   Duplicate installers are that that have both a P and a D
516
#                   flavor of the installer
517
#                   
518
#                   This test has some nasty built-in knowledge (assumtions)
519
#                   It assumes that:
520
#                       Windows installers are only created for one flavor
521
#                           Don't need to worry about windoes installers
522
#                       Non windows installers are of the form:
523
#                           Name_Architecture_Type.deb    
524
#
525
# Inputs          : 
526
#
527
# Returns         : 
528
#
529
sub RemoveDuplicates
530
{
531
    my %baseNames;
7311 dpurdie 532
    my ($base, $type, $suf, $debugName);
6133 dpurdie 533
    foreach my $file ( keys %bomList)
534
    {
7311 dpurdie 535
        undef $base;
536
 
6133 dpurdie 537
        #
538
        #   Only process files that are of the expected form
539
        #
540
        if( $file =~ m~(.*)_([PD])(\.(deb|rpm|tgz))$~ )
541
        {
7311 dpurdie 542
            # ie: erg-udcrypt_1.0.3043.vss_UBUNTU16_P.deb
543
            $base=$1;
544
            $type=$2;
545
            $suf=$3;
546
            $debugName = $base . '_D' . $suf;  
6133 dpurdie 547
 
7311 dpurdie 548
        }
549
        elsif ( $file =~ m~^(.*-.*\.[a-z]+-.*)([PD])(\..*\.rpm)$~i)
550
        {
551
            # ie: ERGtds-25.0.7011.mas-el7D.x86_64.rpm
552
            $base=$1;
553
            $type=$2;
554
            $suf=$3;
555
            $debugName = $base . 'D' . $suf;  
556
        }
557
 
558
        if (defined $base)
559
        {
6133 dpurdie 560
            if (exists $baseNames{$base} )
561
            {
562
                Verbose("Remove debug installer: $file. Kill: $debugName");
563
                delete $bomList{$debugName};
564
            }
565
 
566
            $baseNames{$base} = $type;
567
        }
568
    }
569
}
570
 
571
#-------------------------------------------------------------------------------
572
# Function        : LoadFilterConfig  
573
#
574
# Description     : Load Filter Config
575
#                   Retain filter config for future reference 
576
#
577
# Inputs          : 
578
#
579
# Returns         : 
580
#
581
sub LoadFilterConfig
582
{
583
    if ( -f "$opt_rootdir/$CONFFILE" )
584
    {
585
        Message("Loading Config File");
7304 dpurdie 586
        my $perl_scalar = ReadJsonFile("$opt_rootdir/$CONFFILE");
6133 dpurdie 587
        Error ("Invalid format in Config file")
588
            unless (ref($perl_scalar->{filters}) eq 'ARRAY');
589
 
590
        push (@confFilters, @{$perl_scalar->{filters}});
591
        push (@confFiles, @{$perl_scalar->{keptfiles}}) if exists ($perl_scalar->{keptfiles});
592
    }
593
}
594
 
595
#-------------------------------------------------------------------------------
7304 dpurdie 596
# Function        : ReadJsonFile 
597
#
598
# Description     : Read a JSON file and return the data 
599
#
600
# Inputs          : $fname  - Name of the file to read 
601
#
602
# Returns         : Ref to the JSON 
603
#
604
sub ReadJsonFile
605
{
606
    my ($fname) = @_;
607
    local $/;
608
    open(my $fh, $fname ) || Error("Failed to open $fname. $!");
609
    my $json_text = <$fh>;
610
    my $perl_scalar = decode_json( $json_text );
611
    close($fh);
612
 
613
    return $perl_scalar;
614
}
615
 
616
#-------------------------------------------------------------------------------
617
# Function        : WriteJsonFile 
618
#
619
# Description     : Write data into a file as JSON
620
#
621
# Inputs          : $fname  - Name of file to write
622
#                   $data   - Ref to data to write 
623
#
624
# Returns         : Will not return on error
625
#
626
sub WriteJsonFile
627
{
628
    my ($fname, $data) = @_;
629
    FileCreate ($fname, to_json( $data, { ascii => 1, pretty => 1 }));
630
}
631
 
632
 
633
#-------------------------------------------------------------------------------
6133 dpurdie 634
# Function        : ProcessFilterArgs
635
#
636
# Description     : Process the filter based arguments 
637
#
638
# Inputs          : 
639
#
640
# Returns         : 
641
#
642
sub ProcessFilterArgs
643
{
644
    my $filterArgSeen;
645
    my $writeConf;
646
 
647
 
648
 
649
    if ( @opt_addFilters )
650
    {
651
        Message ("Adding command line filters to the release config file");
652
        foreach my $element (@opt_addFilters) {
653
            UniquePush (\@confFilters, $_ ) foreach  ( split(/,/, $element));
654
        }
655
        $writeConf = 1;
656
    }
657
 
658
    if ( @opt_delFilters )
659
    {
660
        Message ("Deleting command line filters to the release config file");
661
        foreach my $element (@opt_delFilters) {
662
            ArrayDelete (\@confFilters, $_ ) foreach  ( split(/,/, $element));
663
        }
664
        $writeConf = 1;
665
    }
666
 
667
    if ( @opt_addFiles )
668
    {
669
        Message ("Adding command line files to the release config file");
670
        foreach my $element (@opt_addFiles) {
671
            UniquePush (\@confFiles, $_ ) foreach  ( split(/,/, $element));
672
        }
673
        $writeConf = 1;
674
    }
675
 
676
    if ( @opt_delFiles )
677
    {
678
        Message ("Deleting command line files to the release config file");
679
        foreach my $element (@opt_delFiles) {
680
            ArrayDelete (\@confFiles, $_ ) foreach  ( split(/,/, $element));
681
        }
682
        $writeConf = 1;
683
    }
684
 
685
    #
686
    #   Save filter information
687
    #
688
    if ( $writeConf && ! defined($opt_test) )
689
    {
690
        Verbose ("Write config file");
691
 
692
        #
693
        #   Add known files
694
        #
7304 dpurdie 695
        UniquePush (\@confFiles, $CONFFILE, $BOMFILE, $MANIFEST, $TFVARS);
6133 dpurdie 696
 
697
        my $config;
698
        push @{$config->{filters}},@confFilters;
699
        push @{$config->{keptfiles}},@confFiles;
7304 dpurdie 700
        WriteJsonFile("$opt_rootdir/$CONFFILE",$config);
6133 dpurdie 701
    }
702
 
703
    #
704
    #   Display information to the user
705
    #
706
    if ($opt_showFilters)
707
    {
708
        Message ("Configured Filters",@confFilters );
709
        $filterArgSeen = 1;
710
    }
711
 
712
    if ($opt_showFiles)
713
    {
714
        Message ("Configured Files. Keep:",@confFiles );
715
        $filterArgSeen = 1;
716
    }
717
 
718
 
719
    #
720
    #   Terminate program on any filter operations
721
    #
722
    exit 0 if ( $writeConf || $filterArgSeen);
723
}
724
 
725
#-------------------------------------------------------------------------------
726
# Function        : WriteManifest 
727
#
728
# Description     : Save the filter config file if required
729
#
730
# Inputs          : 
731
#
732
# Returns         : 
733
#
734
sub WriteManifest
735
{
736
    return if defined($opt_test);
737
 
738
    #
739
    #   Create JSON metadata
740
    #
741
    Verbose ("Write JSON Manifest");
742
    my $jsonString = to_json( $bomInfo, { ascii => 1, pretty => 1, canonical => 1 } ); 
743
    FileCreate ($opt_rootdir . '/' . $MANIFEST, $jsonString);
744
 
745
    #
746
    #   Create Terraform data
747
    #       Note: Terraform variable cannot have a '.' in them
748
    #   
749
    my @tfData2;
750
 
751
    push @tfData2, "// Terraform variable definitions to map clean package name to full file name";
752
    push @tfData2, "variable vixFileName {";
753
    push @tfData2, "    type = \"map\"";
754
    push @tfData2, "    default = {" ;
755
 
7300 dpurdie 756
    foreach my $item ( sort keys %{$bomInfo->{files}} )
6133 dpurdie 757
    {
758
        push @tfData2, "        \"". $item  ."\" = \"" .$bomInfo->{files}{$item}{fullname} ."\"";
759
    }
760
 
761
    push @tfData2, "    }" ;
762
    push @tfData2, "}" ;
763
 
764
    FileCreate ($opt_rootdir . '/' . $TFVARS, @tfData2);
765
 
766
 
767
}
768
 
769
#-------------------------------------------------------------------------------
770
# Function        : GetReleaseInfo 
771
#
772
# Description     : Get Release Meta Data
773
#
774
# Inputs          : 
775
#
776
# Returns         : Will exit on error 
777
#
778
sub GetReleaseInfo
779
{
780
    my $m_sqlstr = "SELECT p.PROJ_ID, rt.rtag_id, p.PROJ_NAME, rt.RTAG_NAME" .
781
                    " FROM release_tags rt, PROJECTS p" .
782
                    " WHERE p.PROJ_ID = rt.PROJ_ID" .
783
                    " and rt.RTAG_ID = " . $opt_rtagid;
784
 
785
    my $sth = $DM_DB->prepare($m_sqlstr);
786
    if ( defined($sth) )
787
    {
788
        if ( $sth->execute( ) )
789
        {
790
            if ( $sth->rows )
791
            {
792
                while ( my ( $proj_id, $xx, $pname, $rname ) = $sth->fetchrow_array )
793
                {
794
                    my $data;
795
                    $data->{product_id} = $proj_id;
796
                    $data->{product_name} = $pname;
797
                    $data->{release_rtagid} = $opt_rtagid;
798
                    $data->{release_name} = $rname;
799
                    push @{$bomInfo->{release}}, $data;
800
                }
801
            }
802
            else
803
            {
804
                Error("GetReleaseInfo: No rtagid found for " . $opt_rtagid);
805
            }
806
            $sth->finish();
807
        }
808
        else
809
        {
810
            Error("GetReleaseInfo: Execute failure", $sth->errstr(), $m_sqlstr );
811
        }
812
    }
813
    else
814
    {
815
        Error("GetReleaseInfo: Prepare failure", $sth->errstr(), $m_sqlstr );
816
    }
817
}
818
 
819
#-------------------------------------------------------------------------------
820
# Function        : GetPackageData 
821
#
822
# Description     : Extract data from RM based on the provided rtag_id
823
#
824
# Inputs          : 
825
#
826
# Returns         : 
827
#
828
sub GetPackageData
829
{
7304 dpurdie 830
    my $m_sqlstr =  "SELECT p.PKG_NAME, " .
831
                    " pv.PKG_VERSION, " .
832
                    " l.name " .
833
                    "FROM package_versions pv, " .
834
                    " RELEASE_MANAGER.RELEASE_CONTENT rc, " .
835
                    " RELEASE_MANAGER.PACKAGES p, " .
836
                    " RELEASE_MANAGER.LICENCING pl, " .
837
                    " RELEASE_MANAGER.LICENCES l " .
838
                    "WHERE rc.rtag_id     = $opt_rtagid " .
839
                    " AND rc.pv_id         = pv.pv_id " .
840
                    " AND p.PKG_ID         = pv.pkg_id " .
841
                    " AND pv.IS_DEPLOYABLE = 'Y' " .
842
                    " AND pl.PV_ID(+)      = pv.pv_id " .
843
                    " AND pl.LICENCE       = l.LICENCE(+)" ;
6133 dpurdie 844
#                    " and ( pv.IS_DEPLOYABLE = 'Y' or upper( p.PKG_NAME) like 'ERG%' or upper( p.PKG_NAME) like 'VIX%' )";
845
 
7304 dpurdie 846
 
847
 
848
    my ( $PKG_NAME, $PKG_VERSION, $LICENSE );
6133 dpurdie 849
    my $sth = $DM_DB->prepare($m_sqlstr);
850
    if ( defined($sth) )
851
    {
852
        if ( $sth->execute( ) )
853
        {
854
            if ( $sth->rows )
855
            {
7304 dpurdie 856
                while ( ( $PKG_NAME, $PKG_VERSION, $LICENSE ) = $sth->fetchrow_array )
6133 dpurdie 857
                {
858
                    Verbose ("Deployable: $PKG_NAME, $PKG_VERSION");
859
                    my $pkgDir = "$ENV{GBE_DPKG}/$PKG_NAME";
860
                    my $srcDir = "$ENV{GBE_DPKG}/$PKG_NAME/$PKG_VERSION";
861
                    my $dstDir = $opt_rootdir;
862
 
863
                    if ( -d "$srcDir" )
864
                    {
865
                        my $foundFiltered = 0;
866
 
867
                        # for each of the filter rules we glob the rule in the src pkg/version dir
868
                        # and if any of the globbed files dont exist in the dst dir add it to the 
869
                        # the filelist array of files to copy
870
                        foreach my $filter ( @confFilters )
871
                        {
7307 dpurdie 872
                            next if ( $filter =~ m~^--~ );
6133 dpurdie 873
                            foreach my $srcPath ( glob("$srcDir/$filter") )
874
                            {
875
                                next unless ( -f $srcPath );
7307 dpurdie 876
                                next unless testExcluded ($srcPath);
6133 dpurdie 877
                                $foundFiltered = 1;
878
                                $filtersUsed{$filter} = 1;
879
                                my $dstFile = basename($srcPath);
880
                                my $srcFile = $srcPath;
7304 dpurdie 881
                                ReportError("File provided by multiple packages: $dstFile") if exists ($bomList{$dstFile});
882
                                $bomList{$dstFile}{path} = $srcPath;
883
                                $bomList{$dstFile}{package} = $PKG_NAME;
884
                                $bomList{$dstFile}{version} = $PKG_VERSION;
885
                                $bomList{$dstFile}{license} = $LICENSE || '';
6133 dpurdie 886
                            }
887
                        }
888
 
889
                        # if no files found using filters then issue warning
890
                        Warning("No Files found for Package Version $PKG_NAME/$PKG_VERSION using supplied filters") 
891
                            unless ( $foundFiltered );
892
 
893
                        if ($foundFiltered)
894
                        {
895
                            $bomInfo->{packages}{$PKG_NAME} = $PKG_VERSION;
896
                        }
897
                    }
898
                    elsif ( ! -d "$pkgDir" )
899
                    {
900
                        # if srcDir and pkgDir dont exist then package is not in dpkg_archive so display message
901
                        Warning("Skipping Package $PKG_NAME/$PKG_VERSION as it does not exist in dpkg_archive");
902
                    }
903
                    else
904
                    {
905
                        # However if srcDir does not exist but pkgDir does exist then the package version is missing which maybe an issue
906
                        Warning("Missing Version $PKG_VERSION for Package $PKG_NAME in dpkg_archive");
907
                    }
908
                }
909
 
910
                #
911
                #   Report filter elements that where not used.
912
                #
913
                my @notUsed;
914
                foreach my $filter ( @confFilters )
915
                {
916
                    next if ( exists $filtersUsed{$filter} );
917
                    push @notUsed, $filter
918
                }
919
                Warning ("Unused filter rules:", @notUsed )
920
                    if ( @notUsed );
921
 
922
            }
923
            else
924
            {
925
                Error("No Packages for rtagid: $opt_rtagid");
926
            }
927
            $sth->finish();
928
        }
929
        else
930
        {
931
            Error("Execute failure", $sth->errstr(), $m_sqlstr );
932
        }
933
    }
934
    else
935
    {
936
        Error("Prepare failure", $sth->errstr(), $m_sqlstr );
937
    }
7304 dpurdie 938
 
939
    #
940
    #   Report Commercial packages
941
    #   Write out a file to contain the list of COTS files
942
    #
943
    my @Commercial;
944
    foreach my $file (sort keys %bomList)
945
    {
946
        next unless ($bomList{$file}{license} =~ m ~^Commercial~);
947
        $bomList{$file}{cots} = 1;
948
        push @Commercial, $file;
949
    }
950
    Message ("Commercial software packages:", @Commercial);
951
    my $data;
952
    $data->{COTS} = \@Commercial;
953
    WriteJsonFile ("$opt_rootdir/$BOMFILE", $data);
954
 
955
    ErrorDoExit();
6133 dpurdie 956
}
957
 
958
#-------------------------------------------------------------------------------
7307 dpurdie 959
# Function        : testExcluded 
960
#
961
# Description     : Test a filename against the list of exclusion list 
962
#
963
# Inputs          : filename 
964
#
965
# Returns         : true - not excluded
966
#
967
sub testExcluded
968
{
969
    my ($filename) = @_;
970
    foreach my $ufilter ( @confFilters )
971
    {
972
        next unless ( $ufilter =~ m~^--(.*)~ );
973
        my $filter = glob2pat($1);
974
        if ( $filename =~ m~$filter~)
975
        {
976
            $filtersUsed{$ufilter} = 1;
977
            return 0;
978
        }
979
    }
980
 
981
    return 1;
982
}
983
 
984
#-------------------------------------------------------------------------------
985
# Function        : glob2pat
986
#
987
# Description     : Convert four shell wildcard characters into their equivalent
988
#                   regular expression; all other characters are quoted to
989
#                   render them literals.
990
#
991
# Inputs          : Shell style wildcard pattern
992
#
993
# Returns         : Perl RE
994
#
995
 
996
sub glob2pat
997
{
998
    my $globstr = shift;
999
    $globstr =~ s~^/~~;
1000
    my %patmap = (
1001
        '*' => '[^/]*',
1002
        '?' => '[^/]',
1003
        '[' => '[',
1004
        ']' => ']',
1005
        '-' => '-',
1006
    );
1007
    $globstr =~ s{(.)} { $patmap{$1} || "\Q$1" }ge;
1008
    return '/' . $globstr . '$';
1009
}
1010
 
1011
 
1012
#-------------------------------------------------------------------------------
6133 dpurdie 1013
#   Documentation
1014
#
1015
 
1016
=pod
1017
 
1018
=for htmltoc    DEPLOY::generate_deployable
1019
 
1020
=head1 NAME
1021
 
1022
jats_generate_deployable - Extracts current package version list from Release Manager RtagId
1023
                and copy resultant packages to a specific directory.
1024
 
1025
=head1 SYNOPSIS
1026
 
1027
  jats generate_deployable [options]
1028
 
1029
 Options:
1030
    -help                   - Brief help message
1031
    -help -help             - Detailed help message
1032
    -man                    - Full documentation
1033
    -rtagid=xxx             - Specify the Release Manager RtagId to process
1034
    -rootdir=xxx            - Specifies the root of the releases directory
7307 dpurdie 1035
    -[no]symlinks           - Use symlinks to dpkg_archive
6133 dpurdie 1036
 
1037
    -showfilters            - Display current filter set and exit
1038
    -addfilter=xxx[,yyy]    - Add a new filter to the existing filter set
1039
    -delfilter=xxx[,yyy]    - Delete a filter from the existing filter set
1040
 
1041
    -showfiles              - Display current kept file set and exit
1042
    -addfiles=xxx[,yyy]     - Add a new file to the kept file set
1043
    -delfiles=xxx[,yyy]     - Delete a file from the kept file set
1044
 
7319 dpurdie 1045
    -ignoreManifest         - Ignore manifest generation errrors
6133 dpurdie 1046
    -test                   - Just log actions without copying files.
1047
    -verbose                - Enable verbose output
1048
 
1049
=head1 OPTIONS
1050
 
1051
=over 8
1052
 
1053
=item B<-help>
1054
 
1055
Print a brief help message and exits.
1056
 
1057
=item B<-help -help>
1058
 
1059
Print a detailed help message with an explanation for each option.
1060
 
1061
=item B<-man>
1062
 
1063
Prints the manual page and exits.
1064
 
1065
=item B<-rtagid=xxx>
1066
 
1067
This option specifies one or more RTAG_ID's to use as the source of packages that will be copied.
1068
The ID will be used to get a unique list of package/versions that can be copied from dpkg_archive.
1069
 
1070
This option is Mandatory, for non-filter command.
1071
 
1072
=item B<-rootdir=xxx>
1073
 
1074
This option specifies the root directory where the packages will be copied to.
1075
 
1076
The specified directory must exist.
1077
 
1078
The default value is the current directory.
1079
 
7307 dpurdie 1080
=item B<[no]symlinks>
1081
 
1082
This option will cause symlinks to packages to be used in place of actual packages
1083
 
6133 dpurdie 1084
=item B<-showfilters>
1085
 
1086
This option will display the current filter set. If it is combined with another filter operation 
1087
then the other operations will be performed before the display.
1088
 
1089
=item B<-addFilter=xxx[,yyy]>
1090
 
1091
This option allows new filters to be added to the set of filters. This
1092
option can be specified multiple times.
1093
 
1094
This option specifies a comma separated list of shell wildcard filter rule that
1095
will be used to determine which files are copied from package version directory in
1096
GBE_DPKG to the release directory. This can be supplied multiple times to
1097
specify rules for copying.
1098
 
1099
Filters must be added the first time this command is run against a release 
1100
and packages are copied to the project/release directory.  These values are then written to a 
1101
config file in the release directory so the same values can be used on subsequent runs.  
1102
In these subsequent runs this option need not be specified as the config items will be used, however
1103
they can be changed by specifying them again on the command line and the config will be re-written.
1104
 
7307 dpurdie 1105
A filter that begins with a '--' will exclude files. The filter may contain shell wildcards. It is not a reguar expression.
1106
 
6133 dpurdie 1107
The values of these will depend on what builds are required for each project.  Some examples are
7307 dpurdie 1108
    --filter='*-WIN32.exe,*.deb,--*COBRA.deb'
6133 dpurdie 1109
 
1110
=item B<-delFilter=xxx[,yyy]>
1111
 
1112
This option deletes one or more filter rules from an existing set of filters. This
1113
option can be specified multiple times.
1114
 
1115
=item B<-showfiles>
1116
 
1117
This option will display the current file set. If it is combined with another file operations
1118
then the other operations will be performed before the display.
1119
 
1120
=item B<-addFile=xxx[,yyy]>
1121
 
1122
This option allows new files to be added to the set of kept files. This
1123
option can be specified multiple times.
1124
 
1125
This option specifies a comma separated list of file names (No wild cards) that
1126
will be used to specify a list of files that shold be kept in the directory. These
1127
files do not form a part of the manifest, but are not deleted by the tool.
1128
 
1129
=item B<-delFile=xxx[,yyy]>
1130
 
1131
This option deletes one or more files from the set of kept files. This
1132
option can be specified multiple times.
1133
 
1134
=item B<-test>
1135
 
1136
This option will display what would be copied without actually copying anything
1137
 
1138
=item B<-verbose>
1139
 
1140
This option will display progress information as the program executes.
1141
 
1142
=back
1143
 
1144
=head1 DESCRIPTION
1145
 
1146
This program is used to update a Distribution 'bin' directory with the versions of
1147
packages as indicated by the specified Deployment Manager SBoms.
1148
 
1149
There are two modes of operation: Filter modification operations and BOM creation.
1150
 
1151
In 'Filter modification' mode the current filter set will be updated and the program will
1152
exit.
1153
 
1154
In BOM creation mode an sbomid must be provided.
1155
 
1156
The rtagid is used to get all the required information from Release Manager about
1157
which package version are required, as well as the project name and release name.
1158
 
1159
 
1160
In addition to using Release Manager information to determine which Package/Versions are
1161
required to be copied this script also uses a set of shell wildcard filters that are
1162
used to determine which files are actually copied when invoked.
1163
 
1164
The filter rules can be supplied on the command line if available read from a 
1165
configuration file saved in the output diretory the last time the script was run
1166
on this release directory.
1167
 
1168
One or more filter rules must be specified on the command line the first time this command 
1169
is run against a project release directory.  These filter values are then written to a config
1170
file in the output directory so the same values can be used on subsequent runs.  
1171
In subsequent runs the filter rules will be loaded from the config file and need not be specified 
1172
on the command line, however the filter rules in the config file can be changed by specifying 
1173
them again on the command line and the config will be re-written.
1174
 
1175
=cut
1176