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});
4618 dpurdie 433
    #DebugDumpData("XML DATA", \$xml);
4550 dpurdie 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
4618 dpurdie 442
    #   The {files}{file} may be either an array or a hash XMLin appears to
443
    #   make some decision as to which it will be, and I can't control it
4550 dpurdie 444
    #
4618 dpurdie 445
    #   When it does happen the hash key is the 'name' element
446
    #
447
    if (ref($xml->{files}{file}) eq 'HASH')
448
    {
449
        Verbose("Convert file hash to an array");
450
        my @nowArray;
451
        foreach my $name ( keys %{$xml->{files}{file}})
452
        {
453
            my $entry = $xml->{files}{file}{$name};
454
            $entry->{name} = $name;
455
            push @nowArray, $entry;
456
        }
457
        $xml->{files}{file} = \@nowArray
458
    }
459
    Error("Sanity Check Failure. File Section not an ARRAY") unless(ref($xml->{files}{file}) eq 'ARRAY');
460
 
461
    #
462
    #   Release Manager Database will barf if there are duplicate entries
463
    #   Maintain a hash of items processed, and only process each once
464
    #
465
    my %fullNameSeen;
466
 
4550 dpurdie 467
    $updateRmFilesData = ""; 
468
    foreach my $entry (@{$xml->{files}{file}})
469
    {
470
        #
471
        #   Clean up the data
472
        #
473
        my $fname = $entry->{name} || '';
474
        my $fpath  = $entry->{path} || '';
475
        my $fsize  = $entry->{size} || 0;
476
        my $fmd5  = $entry->{md5sum} || '';
477
        $fpath =~ s~/$~~ unless $fname;
478
 
4618 dpurdie 479
        unless( $fullNameSeen{$fpath}{$fname} )
480
        {
481
            $fullNameSeen{$fpath}{$fname} = 1;
4550 dpurdie 482
 
4618 dpurdie 483
            #
484
            #   Create SQL fragment for the insert
485
            #
486
            $eCount++;
487
            my $entry = " INTO release_manager.release_components ( pv_id, file_name, file_path, byte_size, crc_cksum, crc_modcrc ) " .
488
                        " VALUES ( $opt_pvid, '$fname', '$fpath',$fsize , '$fmd5', '')";
489
            $updateRmFilesData .= $entry;
490
 
491
            # The size of the aggregation is key to performance
492
            # Too big is as bad as too small
493
            if (length($updateRmFilesData) > 10000)
494
            {
495
                updateRmFilesInsert();
496
            }
497
        }
498
        else
4550 dpurdie 499
        {
4618 dpurdie 500
            Warning("Multiple file entries for: $fpath $fname");
4550 dpurdie 501
        }
502
    }
503
    updateRmFilesInsert();
504
    Verbose ("Inserted $eCount entries into Release_Components");
505
}
506
 
4455 dpurdie 507
#-------------------------------------------------------------------------------
4550 dpurdie 508
# Function        : updateRmFilesInsert 
509
#
510
# Description     : Insert entries using the partial SQL statement 
511
#                   Must be called when the partial SQL buffer get large
512
#                   as well as at the end to fluch any outstanding inserts
513
#
514
# Inputs          : 
515
#
516
# Returns         : 
517
#
518
sub updateRmFilesInsert
519
{
520
    if (length($updateRmFilesData) > 0)
521
    {
522
        executeRmQuery(
523
            'updateRmFilesInsert',
524
            'INSERT ALL' .$updateRmFilesData . ' SELECT 1 FROM DUAL'
525
            );
526
 
527
        $updateRmFilesData = ""; 
528
    }
529
}
530
#-------------------------------------------------------------------------------
531
# Function        : updateRmFilesDelete 
532
#
533
# Description     : Delete ALL entires in the 'release_components' table associated
534
#                   with the current pvid
535
#                   
536
#                   Needs to be done before new entries are added.    
537
#
538
# Inputs          : 
539
#
540
# Returns         : 
541
#
542
sub updateRmFilesDelete
543
{
544
    executeRmQuery(
545
        'updateRmFilesDelete',
4612 dpurdie 546
        'delete from release_manager.release_components where pv_id=' . $opt_pvid
4550 dpurdie 547
        );
548
}
549
 
550
#-------------------------------------------------------------------------------
551
# Function        : updateRmNoteInfo 
552
#
553
# Description     : Insert Release Note Info into the Release Manager Database
554
#                   This has the side effect that RM will see the Release Note
555
#                   as being fully generated.
556
#
557
# Inputs          : 
558
#
559
# Returns         : 
560
#
561
sub updateRmNoteInfo
562
{
563
    #
564
    #   Determine the path to the Release Note in a form that is suitable for the Release Manager
565
    #   Database. This expects: /dpkg_archive/PkgName/PkgVer/doc/pkgFileName
566
    #
567
    my $rnPath = join('/', '', 'dpkg_archive', $opt_pname, $opt_pversion, 'doc', $opt_outname);
568
    Verbose("RN_INFO:", $rnPath);
569
 
570
    # Into the database
571
    executeRmQuery (
572
        'updateRmNoteInfo', 
573
        'update release_manager.package_versions set release_notes_info=\'' . $rnPath . '\' where pv_id=' . $opt_pvid
574
        );
575
}
576
 
577
#-------------------------------------------------------------------------------
578
# Function        : executeRmQuery 
579
#
580
# Description     : Execute a simple RM query. One that does not expect any return data
581
#
582
# Inputs          : $fname           - OprName, for error reporting
583
#                   $m_sqlstr        - SQL String
584
#
585
# Returns         : Will exit on error
586
#
587
sub executeRmQuery
588
{
589
    my ($fname, $m_sqlstr) = @_;
590
 
591
    #
592
    #   Connect to the Database - once
593
    #
594
    connectRM(\$RM_DB, 0) unless $RM_DB;
595
 
4552 dpurdie 596
    Verbose2('ExecuteQuery:', $fname);
4550 dpurdie 597
    #
598
    #   Create the full SQL statement
599
    #
600
    my $sth = $RM_DB->prepare($m_sqlstr);
601
    if ( defined($sth) )
602
    {
603
        if ( $sth->execute() )
604
        {
605
            $sth->finish();
606
        }
607
        else
608
        {
609
            Error("$fname: Execute failure: $m_sqlstr", $sth->errstr() );
610
        }
611
    }
612
    else
613
    {
614
        Error("$fname: Prepare failure");
615
    }
616
}
617
 
618
 
619
#-------------------------------------------------------------------------------
4455 dpurdie 620
#   Documentation
621
#
622
 
623
=pod
624
 
625
=for htmltoc    SYSUTIL::
626
 
627
=head1 NAME
628
 
4550 dpurdie 629
jats_gen_releasenote - Generate a Release Note
4455 dpurdie 630
 
631
=head1 SYNOPSIS
632
 
4550 dpurdie 633
 jats jats_gen_releasenote [options]
4455 dpurdie 634
 
635
 Options:
636
    -help              - Brief help message
637
    -help -help        - Detailed help message
638
    -man               - Full documentation
639
    -verbose           - Display additional progress messages
640
    -outfile=name      - [Optional] Name of the output XML file
4468 dpurdie 641
    -archive=name      - [Optional] Name package archive
642
    -releasenote=path  - Path to the Release Note Data
4550 dpurdie 643
    -UpdateRmFiles     - Update the Files list in Release Manager
4455 dpurdie 644
 
645
 
646
=head1 OPTIONS
647
 
648
=over 8
649
 
650
=item B<-help>
651
 
652
Print a brief help message and exits.
653
 
654
=item B<-help -help>
655
 
656
Print a detailed help message with an explanation for each option.
657
 
658
=item B<-man>
659
 
660
Prints the manual page and exits.
661
 
662
=item B<-pvid=nn>
663
 
664
This option provides identifies the PackageVersion to be processed.
665
 
666
This option is mandatory.
667
 
668
=item B<-outfile=name>
669
 
670
This option specifies the output file name.
671
 
672
If not provided by the user the output filename will be created in the current directory
673
and it will be named after the package name and package version.
674
 
675
If the filename does not end in .xml, then .xml will be appended to the file name.
676
 
4550 dpurdie 677
=item B<-UpdateRmFiles>
678
 
679
This option will case the utility to ppdate the Files list in Release Manager.
680
 
681
The existinf file list will be replaced by the one within the package.
682
 
683
Note: Write Access to Release Manager is required.
684
 
4455 dpurdie 685
=back
686
 
687
=head1 DESCRIPTION
688
 
689
This utility program is used to extract sufficient information from Release Manager and other
690
associated databases so that a Release Note can be created.
691
 
692
The extracted data is stored in an XML format. The intent is that XSLT will be used to create
693
an HTML based release note.
694
 
695
 
696
=head1 EXAMPLE
697
 
698
=head2 jats get_releasenote_data -pvid=983058 -outfile=tmpdata.xml
699
 
700
This will locate a package-version with an id of 983058, extrat required information and create
701
an XML file called tmpdata.xml.
702
 
703
=cut