Subversion Repositories DevTools

Rev

Rev 4625 | Rev 6116 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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