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