Subversion Repositories DevTools

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
4455 dpurdie 1
########################################################################
2
# Copyright (c) VIX TECHNOLOGY (AUST) LTD
3
#
4
# Module name   : jats_gen_releasenote.pl
5
# Module type   : Makefile system
6
# Compiler(s)   : Perl
7
# Environment(s): jats
8
#
9
# Description   : Generate the package Release Note
10
#                 Requires
11
#                   1) Release Note data generated by jats_get_releasenote_data.pl
12
#                   2) Target package in dpkg_archive
13
#                   3) One or more built.files.xxx.xml
14
#
15
#                 Will:
16
#                   Merge built.files..xml files into the release note data
17
#                   Insert build meta data
18
#                   Save the Release Note MetaData within the package
19
#                   Generate the HTML release note - save within the package
20
#
21
# Usage:        See POD
22
#
23
#......................................................................#
24
 
25
require 5.008_002;
26
use strict;
27
use warnings;
28
 
29
use Pod::Usage;
30
use Getopt::Long;
31
use XML::Simple;
32
use File::Path;
33
use File::Copy;
34
use XML::Simple;
4546 dpurdie 35
use Digest::MD5;
36
use File::Find;
4455 dpurdie 37
 
38
use JatsError;
39
use JatsSystem;
40
use Getopt::Long;
41
use Pod::Usage;
42
use FileUtils;
43
use JatsEnv;
4550 dpurdie 44
use JatsRmApi;
4455 dpurdie 45
 
4550 dpurdie 46
use DBI;
47
 
4455 dpurdie 48
my $VERSION = "1.0.0";                                      # Update this. Inserted into meta data field
49
 
50
# User options
4612 dpurdie 51
my $opt_verbose = 0;
4455 dpurdie 52
my $opt_help = 0;
53
my $opt_archive;
54
my $archive_tag;
55
my $opt_pname;
56
my $opt_pversion;
57
my $opt_release_note;
58
my $opt_pvid;
59
my $opt_outname;
4550 dpurdie 60
my $opt_updateRmFiles;
4612 dpurdie 61
my $opt_workingDir;
4455 dpurdie 62
 
63
#   Global vars
4546 dpurdie 64
our $GBE_HOSTNAME;
4455 dpurdie 65
our $GBE_TOOLS;
66
my $DPKG_ROOT;
67
my $DPKG_DOC;
68
my $JATS_RN;
4612 dpurdie 69
my $OUT_ROOT;
4455 dpurdie 70
 
71
 
72
#
73
#   Structure to translate -archive=xxx option to archive variable
74
#   These are the various dpkg_archives known to JATS
75
#
76
my %Archive2Var =( 'main'       =>  'GBE_DPKG',
77
                   'store'      =>  'GBE_DPKG_STORE',
78
                   'cache'      =>  'GBE_DPKG_CACHE',
79
                   'local'      =>  'GBE_DPKG_LOCAL',
80
                   'sandbox'    =>  'GBE_DPKG_SBOX',
81
                   'deploy'     =>  'GBE_DPLY',
82
                   );
83
 
84
#-------------------------------------------------------------------------------
85
# Function        : Main Entry
86
#
87
# Description     :
88
#
89
# Inputs          :
90
#
91
# Returns         :
92
#
93
{
94
    my $result = GetOptions (
4550 dpurdie 95
                    "help+"         => \$opt_help,              # flag, multiple use allowed
96
                    "manual:3"      => \$opt_help,              # flag, set value
97
                    "verbose:+"     => \$opt_verbose,           # flag, increment
98
                    "archive=s"     => \$opt_archive,           # string
99
                    "releasenote=s" => \$opt_release_note,      # string
4612 dpurdie 100
                    "UpdateRmFiles" => \$opt_updateRmFiles,     # flag
101
                    "WorkDir=s"     => \$opt_workingDir,        # String
4455 dpurdie 102
                    );
103
 
104
    #
105
    #   Process help and manual options
106
    #
107
    pod2usage(-verbose => 0, -message => "Version: $VERSION")  if ($opt_help == 1  || ! $result);
108
    pod2usage(-verbose => 1)  if ($opt_help == 2 );
109
    pod2usage(-verbose => 2)  if ($opt_help > 2);
110
 
111
    ErrorConfig( 'name'    =>'GenRn', 'verbose' => $opt_verbose );
4612 dpurdie 112
    InitFileUtils();
4455 dpurdie 113
 
114
    #
115
    #   Needed EnvVars
116
    #
4546 dpurdie 117
    EnvImport ('GBE_HOSTNAME');
4455 dpurdie 118
    EnvImport ('GBE_TOOLS');
119
    $JATS_RN = catdir($GBE_TOOLS, 'RELEASENOTES');
120
    Error("Release Note tools not found", $JATS_RN)
121
        unless (-d $JATS_RN);
122
 
123
 
124
    #
125
    #   Sanity Check
126
    #
127
    Error("Release Note data not provided") unless $opt_release_note;
128
    Error("Release Note data not found") unless -f $opt_release_note;
4612 dpurdie 129
    if (defined $opt_workingDir)
130
    {
131
        $opt_workingDir = FullPath($opt_workingDir);
132
        Error("Working directory is not a directory") unless -d $opt_workingDir;
133
        Error("Working directory is writable") unless -w $opt_workingDir;
134
    }
4455 dpurdie 135
 
136
 
137
    #
138
    #   Determine the target archive
139
    #   The default archive is GBE_DPKG, but this may be changed
140
    #
141
    $opt_archive = 'main' unless ( $opt_archive );
142
    my $archive_tag = $Archive2Var{$opt_archive};
143
    Error("Unknown archive specified: $opt_archive")
144
        unless ( $archive_tag );
145
    $DPKG_ROOT = $ENV{$archive_tag} || '';
146
    Verbose ("Archive Variable: $archive_tag" );
147
    Verbose2 ("Archive Path: $DPKG_ROOT" );
148
 
149
    #
150
    #   Process the release note and extract essential information
151
    #
152
    processReleaseNote();
153
 
154
    #
155
    #   Locate the target package
156
    #
157
    $DPKG_ROOT = catdir($DPKG_ROOT, $opt_pname, $opt_pversion);
4546 dpurdie 158
    Verbose ("Package Path: ", DisplayPath($DPKG_ROOT) );
4455 dpurdie 159
    Error ("Package not found in archive", $DPKG_ROOT) unless -d $DPKG_ROOT;
160
 
4612 dpurdie 161
    #   OUT_ROOT is really only used for testing
162
    #   Needs to mimic $DPKG_ROOT
163
    $OUT_ROOT = defined($opt_workingDir) ? $opt_workingDir : $DPKG_ROOT; 
164
    Verbose2 ("Output Path: $OUT_ROOT" );
165
 
4455 dpurdie 166
    #
167
    #   Locate the release note data file
168
    #   This was created as a prerequisite to the build
169
    #   Note: windows requires '/' in the file list
170
    #
171
    my @filesList;
172
    foreach my $item ( glob(catdir($DPKG_ROOT, 'built.files.*.xml'))) {
173
        $item =~ s~\\~/~g;
174
        push @filesList, $item;
175
    }
4546 dpurdie 176
    unless (scalar @filesList > 0)
177
    {
178
        #
179
        #   No filelist found in the package
180
        #   This may occur for packages that did not go though the build system
181
        #   Create a package-list
182
        #
183
        my $item = generateFileList();
184
        $item =~ s~\\~/~g;
185
        push @filesList, $item;
186
    }
4455 dpurdie 187
    Error("No file list found within the package") unless (scalar @filesList > 0);
188
 
189
    #
190
    #   Generate the name of the release note
191
    #       RELEASE_NOTES_PVID_PKGNAME_CLEANV.html
192
    #
193
    my $cleanv = $opt_pversion;
194
    $cleanv =~ s~\.~_~g;
195
    $opt_outname = join('_',
196
                        'RELEASE_NOTES',
197
                        $opt_pvid,
198
                        $opt_pname,
199
                        $cleanv,    
200
                        ) . '.html';
201
    Verbose("Note Name: $opt_outname");
202
 
203
    #
204
    #   Create output directory within the package
205
    #
4612 dpurdie 206
    $DPKG_DOC = catdir($OUT_ROOT, 'doc');
4455 dpurdie 207
    mkpath($DPKG_DOC, 0, 0775);
208
 
209
    #
210
    #   Merge the Release Note and the various file lists into a single
211
    #   XML file. Keep the file local
212
    #
213
    System('--NoShell', '--Exit', 'java', '-jar', 
214
           catdir($JATS_RN, 'saxon9he.jar'), 
215
           '-xsl:' . catdir($JATS_RN, 'merge.xslt'),
216
           "-s:$opt_release_note",
217
           'fileList=' . join(',', @filesList),
218
           '-o:' . 'built.files.releasenote.xml'
219
            );
220
 
221
    #
222
    #   Generate the HTML Release Note
223
    #   Output directly to the body of the package
224
    #
225
    System('--NoShell', '--Exit', 'java', '-jar', 
226
           catdir($JATS_RN, 'saxon9he.jar'), 
227
           '-xsl:' . catdir($JATS_RN, 'releaseNote2html.xslt'),
228
           '-s:' . 'built.files.releasenote.xml',
229
           "-o:" . catdir($DPKG_DOC, $opt_outname)
230
            );
231
 
232
    #
233
    #   Transfer the XML database directly into the package
234
    #
235
    unless( File::Copy::copy('built.files.releasenote.xml', catdir($DPKG_DOC, 'release_note.xml')) )
236
    {
237
        Error("Cannot transfer XML release note", $!);
238
    }
239
 
240
    #
4550 dpurdie 241
    #   Update the Release Manager entry - File Data
242
    #
243
    if ($opt_updateRmFiles)
244
    {
245
        updateRmFiles();
246
        updateRmNoteInfo();
247
    }
248
 
249
    #
4455 dpurdie 250
    #   All done
251
    #
4546 dpurdie 252
    Verbose ("Release Note:", DisplayPath(catdir($DPKG_DOC, $opt_outname)));
4455 dpurdie 253
    exit 0;
254
}
255
 
256
#-------------------------------------------------------------------------------
257
# Function        : processReleaseNote 
258
#
4546 dpurdie 259
# Description     : Extract essential information from the Release Note Data
4455 dpurdie 260
#
261
# Inputs          : 
262
#
4546 dpurdie 263
# Returns         : Populates global variables 
4455 dpurdie 264
#
265
sub processReleaseNote
266
{
267
    my $xml = XMLin($opt_release_note);
268
 
269
    foreach my $item ( qw (name version pvid) ) {
270
        Error("Unexpected ReleaseNote format: package $item")
271
            unless (exists $xml->{package}{$item});
272
    }
273
 
274
    $opt_pname = $xml->{package}{name};
275
    $opt_pversion = $xml->{package}{version};
276
    $opt_pvid = $xml->{package}{pvid};
277
 
278
    Verbose("Package Name: $opt_pname") ;
279
    Verbose("Package Version: $opt_pversion") ;
280
    Verbose("Package Pvid: $opt_pvid") ;
281
}
282
 
4546 dpurdie 283
#-------------------------------------------------------------------------------
284
# Function        : generateFileList
285
#                   generateFileListProc 
286
#
287
# Description     : A fileList has not been found in the target package
288
#                   Perhaps the package was not created by the build system
289
#                   
290
#                   Generate a fileList        
291
#
292
# Inputs          : None
293
#
294
# Returns         : Path to the generated file list
295
#
296
my @generateFileListData;
297
my $baseLength;
298
sub generateFileList
299
{
300
    my $outXmlFile;
301
    Verbose("Generate internal filelist - none found in package");
4455 dpurdie 302
 
4546 dpurdie 303
    #
304
    #   Scan the package and process each file
305
    #
306
    $baseLength = length($DPKG_ROOT);
307
    File::Find::find( \&generateFileListProc, $DPKG_ROOT );
308
 
309
    #
310
    #   Write out XML of the Generated file list
311
    #
312
    my $data;
313
    $data->{file} = \@generateFileListData;
314
 
315
    #
316
    #   Write out sections of XML
317
    #       Want control over the output order
318
    #       Use lots of attributes and only elements for arrays
319
    #       Save as one attribute per line - for readability
320
    #
4612 dpurdie 321
    $outXmlFile = catdir($OUT_ROOT,"built.files.$GBE_HOSTNAME.xml");
4546 dpurdie 322
 
323
    Verbose2('Meta File', $outXmlFile);
324
    my $xs = XML::Simple->new( NoAttr =>0, AttrIndent => 1 );
325
 
326
    open (my $XML, '>', $outXmlFile) || Error ("Cannot create output file: $outXmlFile", $!);
327
    $xs->XMLout($data, 
328
                'RootName' => 'files', 
329
                'XMLDecl'  => '<?xml version="1.0" encoding="UTF-8"?>',
330
                'OutputFile' => $XML);
331
    close $XML;
332
 
333
    return $outXmlFile;
334
}
335
 
336
sub generateFileListProc
337
{
338
    my %data;
339
    my $md5sum;
340
    my $source = $File::Find::name;
341
    my $type = 'dir';
342
 
343
    my $target = substr($source, $baseLength);
344
    $target =~ s~^/~~;
345
    $target =~ s~/$~~;
346
    Verbose2("GenFileList: $source, $target");
347
    return if (length ($target) == 0);
348
 
349
    if (-l $source)
350
    {
351
        $type = 'link';
352
    }
353
    elsif ( -f $source)
354
    {
355
        $type = 'file';
356
        Verbose2("Calculate MD5 Digest: $source");
357
        open(my $fh , $source) or Error ("Can't open '$source': $!");
358
        binmode $fh, ':crlf';
359
        $md5sum = Digest::MD5->new->addfile($fh)->hexdigest;
360
        close $fh;
361
    }
362
 
363
 
364
    if (-d $source)
365
    {
366
        $data{path} = $target;
367
    }
368
    else
369
    {
370
        $data{path} = StripFileExt($target);
371
        $data{name} = StripDir($target);
372
        if (defined $md5sum)
373
        {
374
            $data{size} = (stat($source))[7];
375
            $data{md5sum} = $md5sum;
376
        }
377
    }
378
 
379
    $data{fullname} = $target;
380
    $data{type} = $type;
381
    $data{machtype} = 'unknown';
382
    $data{host} = $GBE_HOSTNAME;
383
 
384
    # Put a nice '/' on the end of the patch elements
385
    $data{path} .= '/'
386
        if ( exists ($data{path}) && length($data{path}) > 0);
387
 
388
    push @generateFileListData, \%data;
389
}
390
 
4550 dpurdie 391
#-------------------------------------------------------------------------------
392
# Function        : updateRmFiles 
393
#
394
# Description     : 
395
#
396
# Inputs          : 
397
#
398
# Returns         : 
399
#
400
my $RM_DB;
401
my  $updateRmFilesData;
402
sub updateRmFiles
403
{
404
    my $eCount = 0;
405
    #
406
    #   If in use the the autobuilder - which it should be, then
4612 dpurdie 407
    #   modify the user name to access RM with a proxy user. This is the
408
    #   same method used in the 'buildtool'
4550 dpurdie 409
    #
4612 dpurdie 410
    if ($ENV{GBE_ABT} && $ENV{GBE_RM_USERNAME} && $ENV{GBE_RM_USERNAME} !~ m~]$~ )
4550 dpurdie 411
    {
4612 dpurdie 412
        Verbose("Access RM database as proxy user");
4550 dpurdie 413
        $ENV{GBE_RM_USERNAME} = $ENV{GBE_RM_USERNAME} . '[release_manager]'; 
414
    }
415
 
416
    #
417
    #   Read in the release note data base
418
    #
419
    my $rnDataBase = catdir($DPKG_DOC, 'release_note.xml');
420
    if (! -f $rnDataBase )
421
    {
422
        Error("Release Note XML database not found: $!", $rnDataBase);
423
    }
424
    my $xml = XMLin($rnDataBase);
425
 
426
    #
427
    #   Sanity Test the data
428
    #
429
    Error("Sanity Check Failure. PVID") unless($opt_pvid eq $xml->{package}{pvid});
430
    Error("Sanity Check Failure. Name") unless($opt_pname eq $xml->{package}{name});
431
    Error("Sanity Check Failure. Version") unless($opt_pversion eq $xml->{package}{version});
432
    Error("Sanity Check Failure. File Section") unless(exists $xml->{files}{file});
433
#    DebugDumpData("XML DATA", \$xml);
434
 
435
    #
436
    #   Delete any existing entry(s)
437
    #
438
    updateRmFilesDelete();
439
 
440
    #
441
    #   Scan each entry and pump required data into the RM Database
442
    #
443
    $updateRmFilesData = ""; 
444
    foreach my $entry (@{$xml->{files}{file}})
445
    {
446
        #DebugDumpData("Entry", $entry);
447
 
448
        #
449
        #   Clean up the data
450
        #
451
        my $fname = $entry->{name} || '';
452
        my $fpath  = $entry->{path} || '';
453
        my $fsize  = $entry->{size} || 0;
454
        my $fmd5  = $entry->{md5sum} || '';
455
 
456
        $fpath =~ s~/$~~ unless $fname;
457
 
458
        #
459
        #   Create SQL fragment for the insert
460
        #
461
        $eCount++;
462
        my $entry = " INTO release_manager.release_components ( pv_id, file_name, file_path, byte_size, crc_cksum, crc_modcrc ) " .
463
                    " VALUES ( $opt_pvid, '$fname', '$fpath',$fsize , '$fmd5', '')";
464
        $updateRmFilesData .= $entry;
465
 
466
        # The size of the aggregation is key to performance
467
        # Too big is as bad as too small
468
        if (length($updateRmFilesData) > 10000)
469
        {
470
            updateRmFilesInsert();
471
        }
472
    }
473
    updateRmFilesInsert();
474
    Verbose ("Inserted $eCount entries into Release_Components");
475
}
476
 
4455 dpurdie 477
#-------------------------------------------------------------------------------
4550 dpurdie 478
# Function        : updateRmFilesInsert 
479
#
480
# Description     : Insert entries using the partial SQL statement 
481
#                   Must be called when the partial SQL buffer get large
482
#                   as well as at the end to fluch any outstanding inserts
483
#
484
# Inputs          : 
485
#
486
# Returns         : 
487
#
488
sub updateRmFilesInsert
489
{
490
    if (length($updateRmFilesData) > 0)
491
    {
492
        executeRmQuery(
493
            'updateRmFilesInsert',
494
            'INSERT ALL' .$updateRmFilesData . ' SELECT 1 FROM DUAL'
495
            );
496
 
497
        $updateRmFilesData = ""; 
498
    }
499
}
500
#-------------------------------------------------------------------------------
501
# Function        : updateRmFilesDelete 
502
#
503
# Description     : Delete ALL entires in the 'release_components' table associated
504
#                   with the current pvid
505
#                   
506
#                   Needs to be done before new entries are added.    
507
#
508
# Inputs          : 
509
#
510
# Returns         : 
511
#
512
sub updateRmFilesDelete
513
{
514
    executeRmQuery(
515
        'updateRmFilesDelete',
4612 dpurdie 516
        'delete from release_manager.release_components where pv_id=' . $opt_pvid
4550 dpurdie 517
        );
518
}
519
 
520
#-------------------------------------------------------------------------------
521
# Function        : updateRmNoteInfo 
522
#
523
# Description     : Insert Release Note Info into the Release Manager Database
524
#                   This has the side effect that RM will see the Release Note
525
#                   as being fully generated.
526
#
527
# Inputs          : 
528
#
529
# Returns         : 
530
#
531
sub updateRmNoteInfo
532
{
533
    #
534
    #   Determine the path to the Release Note in a form that is suitable for the Release Manager
535
    #   Database. This expects: /dpkg_archive/PkgName/PkgVer/doc/pkgFileName
536
    #
537
    my $rnPath = join('/', '', 'dpkg_archive', $opt_pname, $opt_pversion, 'doc', $opt_outname);
538
    Verbose("RN_INFO:", $rnPath);
539
 
540
    # Into the database
541
    executeRmQuery (
542
        'updateRmNoteInfo', 
543
        'update release_manager.package_versions set release_notes_info=\'' . $rnPath . '\' where pv_id=' . $opt_pvid
544
        );
545
}
546
 
547
#-------------------------------------------------------------------------------
548
# Function        : executeRmQuery 
549
#
550
# Description     : Execute a simple RM query. One that does not expect any return data
551
#
552
# Inputs          : $fname           - OprName, for error reporting
553
#                   $m_sqlstr        - SQL String
554
#
555
# Returns         : Will exit on error
556
#
557
sub executeRmQuery
558
{
559
    my ($fname, $m_sqlstr) = @_;
560
 
561
    #
562
    #   Connect to the Database - once
563
    #
564
    connectRM(\$RM_DB, 0) unless $RM_DB;
565
 
4552 dpurdie 566
    Verbose2('ExecuteQuery:', $fname);
4550 dpurdie 567
    #
568
    #   Create the full SQL statement
569
    #
570
    my $sth = $RM_DB->prepare($m_sqlstr);
571
    if ( defined($sth) )
572
    {
573
        if ( $sth->execute() )
574
        {
575
            $sth->finish();
576
        }
577
        else
578
        {
579
            Error("$fname: Execute failure: $m_sqlstr", $sth->errstr() );
580
        }
581
    }
582
    else
583
    {
584
        Error("$fname: Prepare failure");
585
    }
586
}
587
 
588
 
589
#-------------------------------------------------------------------------------
4455 dpurdie 590
#   Documentation
591
#
592
 
593
=pod
594
 
595
=for htmltoc    SYSUTIL::
596
 
597
=head1 NAME
598
 
4550 dpurdie 599
jats_gen_releasenote - Generate a Release Note
4455 dpurdie 600
 
601
=head1 SYNOPSIS
602
 
4550 dpurdie 603
 jats jats_gen_releasenote [options]
4455 dpurdie 604
 
605
 Options:
606
    -help              - Brief help message
607
    -help -help        - Detailed help message
608
    -man               - Full documentation
609
    -verbose           - Display additional progress messages
610
    -outfile=name      - [Optional] Name of the output XML file
4468 dpurdie 611
    -archive=name      - [Optional] Name package archive
612
    -releasenote=path  - Path to the Release Note Data
4550 dpurdie 613
    -UpdateRmFiles     - Update the Files list in Release Manager
4455 dpurdie 614
 
615
 
616
=head1 OPTIONS
617
 
618
=over 8
619
 
620
=item B<-help>
621
 
622
Print a brief help message and exits.
623
 
624
=item B<-help -help>
625
 
626
Print a detailed help message with an explanation for each option.
627
 
628
=item B<-man>
629
 
630
Prints the manual page and exits.
631
 
632
=item B<-pvid=nn>
633
 
634
This option provides identifies the PackageVersion to be processed.
635
 
636
This option is mandatory.
637
 
638
=item B<-outfile=name>
639
 
640
This option specifies the output file name.
641
 
642
If not provided by the user the output filename will be created in the current directory
643
and it will be named after the package name and package version.
644
 
645
If the filename does not end in .xml, then .xml will be appended to the file name.
646
 
4550 dpurdie 647
=item B<-UpdateRmFiles>
648
 
649
This option will case the utility to ppdate the Files list in Release Manager.
650
 
651
The existinf file list will be replaced by the one within the package.
652
 
653
Note: Write Access to Release Manager is required.
654
 
4455 dpurdie 655
=back
656
 
657
=head1 DESCRIPTION
658
 
659
This utility program is used to extract sufficient information from Release Manager and other
660
associated databases so that a Release Note can be created.
661
 
662
The extracted data is stored in an XML format. The intent is that XSLT will be used to create
663
an HTML based release note.
664
 
665
 
666
=head1 EXAMPLE
667
 
668
=head2 jats get_releasenote_data -pvid=983058 -outfile=tmpdata.xml
669
 
670
This will locate a package-version with an id of 983058, extrat required information and create
671
an XML file called tmpdata.xml.
672
 
673
=cut