File Coverage

blib/lib/Module/DevAid.pm
Criterion Covered Total %
statement 15 297 5.0
branch 0 134 0.0
condition 0 78 0.0
subroutine 5 21 23.8
pod 16 16 100.0
total 36 546 6.5


line stmt bran cond sub pod time code
1             package Module::DevAid;
2 1     1   24587 use strict;
  1         2  
  1         38  
3 1     1   6 use warnings;
  1         2  
  1         75  
4              
5             =head1 NAME
6              
7             Module::DevAid - tools to aid perl module developers
8              
9             =head1 VERSION
10              
11             This describes version B<0.24> of Module::DevAid.
12              
13             =cut
14              
15             our $VERSION = '0.24';
16              
17             =head1 SYNOPSIS
18              
19             use Module::DevAid;
20              
21             my $da = Module::DevAid->new(
22             dist_name => 'My::Module',
23             modules => qw(lib/My/Module.pm lib/My/Module/Other.pm),
24             scripts => qw(scripts/myscript),
25             gen_readme => 1,
26             gen_todo => 1,
27             );
28              
29             $da->generate_readme_file();
30              
31             =head1 DESCRIPTION
32              
33             Module (and script) to aid with development, by helping (and testing)
34             auto-building of certain files, and with the steps needed in building and
35             committing a release.
36              
37             At this point this only uses the darcs or svk revision systems.
38              
39             Takes a project description, either through the command line options, or
40             via a project config file, which defaults to 'mod_devaid.conf' in
41             the current directory.
42              
43             Features:
44              
45             =over
46              
47             =item *
48              
49             generates a README file from POD
50              
51             =item *
52              
53             generates a TODO file from a devtodo .todo file
54              
55             =item *
56              
57             auto-updates a Changes file from the revision-control system's change log.
58              
59             =item *
60              
61             auto-changes version-id in module and script files
62              
63             =item *
64              
65             does all of the above and tags commits for a release
66              
67             =back
68              
69             =head1 METHODS
70              
71             =head2 new
72            
73             my $da = new(%args)
74              
75             Create a new object. This first reads from the default config file
76             (see L) and the defaults from there can be overridden
77             with the following arguments:
78              
79             =over
80              
81             =item changes_file => I
82              
83             Name of the Changes file to be generated. (default: Changes)
84              
85             =item commit_todo => 1
86              
87             Should we commit the TODO file we generated? If true, then will
88             attempt to do a darcs commit on the generated TODO file. This needs
89             to be an option because some setups need the TODO file (as well as the
90             .todo file) to be under revision control, and others don't.
91             (see L) (default: false)
92              
93             =item dist_name => I
94              
95             The distribution name of the project, such as the name of the module
96             My::Module.
97              
98             =item gen_readme => 1
99              
100             Should we generate a README file? (see L)
101              
102             =item gen_todo => 1
103              
104             Should we generate a TODO file? If true, the TODO file will be
105             generated from a .todo file of the kind created by the devtodo program.
106             (see L)
107              
108             =item modules => qw(lib/My/Module.pm)
109              
110             The module files in the project. Must have their path relative
111             to the top directory of the project.
112              
113             =item old_version_file => I
114              
115             The file which will hold the previous version (gets updated on version_bump).
116             (see L) (default: old_version.txt)
117              
118             =item pod_file => I
119              
120             The file which contains the POD from which the README file should
121             be generated. If not defined, defaults to the first module
122             in the B list.
123              
124             If B is true, the README file will be generated from
125             I
126             NAME, DESCRIPTION, INSTALLATION, REQUIRES and AUTHOR.
127              
128             =item readme_file => I
129              
130             Name of the README file to be generated. (default: README)
131              
132             =item version_bump_code => I
133              
134             my $vsub = sub {
135             my $version = shift;
136              
137             # code to update files
138             ...
139             };
140              
141             version_bump_code => $vsub
142              
143             Reference to a function which will perform custom actions to
144             automatically change the version-id. The default actions go through the
145             B and B and update anything matching a standard
146             VERSION-setting string, and which matches a 'This describes version'
147             string. This subroutine is for doing anything additional or different.
148              
149             This is given one argument, the version string.
150              
151             =item version_bump_files
152              
153             The list of files altered by the version_bump_code, so that all the
154             version changes can be committed at the same time. This is needed because
155             some tests require the test files to have the version-id in them,
156             and therefore all version commits should be done at the same time,
157             otherwise the tests will fail, and the commits won't work.
158              
159             =item scripts => qw(scripts/myscript)
160              
161             The script files in the project. Must have their path relative
162             to the top directory of the project.
163              
164             =item todo_file => I
165              
166             Name of the TODO file to be generated. (default: TODO)
167              
168             =item version_file => I
169              
170             The file from which to take the version. The version should be in the form
171             of a standard VERSION id: I.I on a line by itself.
172             Optionally, it can be I.I followed by a general id,
173             for example 2.03_rc1
174             (default: version.txt)
175              
176             =item version_control => I
177              
178             Which version-control system is being used. The options are 'darcs'
179             and 'svk'. (default: darcs)
180             The version control system is used for listing and committing changes.
181              
182             =back
183              
184             =cut
185 1     1   983 use Pod::Select;
  1         2161  
  1         99  
186 1     1   1040 use Pod::Text;
  1         90552  
  1         371  
187 1     1   1199 use IO::String;
  1         6592  
  1         3993  
188              
189             sub new {
190 0     0 1   my $class = shift;
191 0           my %parameters = (@_);
192 0   0       my $self = bless({}, ref ($class) || $class);
193 0           $self->config_read();
194              
195             # set the parameters, which override the config
196 0 0         if (%parameters) {
197 0           while (my ($key, $value) = each (%parameters))
198             {
199 0           $self->{$key} = $value;
200             }
201             }
202              
203             # set the defaults if not already set
204 0   0       $self->{version_control} ||= 'darcs';
205 0   0       $self->{pod_file} ||= $self->{modules}->[0];
206 0   0       $self->{commit_todo} ||= 0;
207 0   0       $self->{gen_readme} ||= 0;
208 0   0       $self->{gen_todo} ||= 0;
209 0   0       $self->{todo_file} ||= 'TODO';
210 0   0       $self->{changes_file} ||= 'Changes';
211 0   0       $self->{readme_file} ||= 'README';
212 0   0       $self->{version_file} ||= 'version.txt';
213 0   0       $self->{old_version_file} ||= 'old_version.txt';
214 0   0       $self->{version_bump_files} ||= [];
215 0   0       $self->{scripts} ||= [];
216 0           $self->{pod_sel} = new Pod::Select();
217 0           $self->{pod_text} = Pod::Text->new(alt=>1,indent=>0);
218 0           return $self;
219             } # new
220              
221             =head2 config_read
222              
223             Set the configuration from a config file. This is called
224             for the default config when "new" is invoked, so there's no
225             need to call this unless you want to use an additional config file.
226              
227             The information about a project can be given in a config file, which
228             defaults to 'mod_devaid.conf' in the current directory. If you want
229             to use a different file, set the MOD_DEVAID_CONF environment variable,
230             and the module will use that.
231              
232             The options which can be set in the config file are exactly the same
233             as those which can be set in B.
234              
235             The options are set with a 'option = value' setup. Blank lines are
236             ignored.
237              
238             For example:
239              
240             dist_name = My::Module
241             modules = lib/My/Module.pm lib/My/Module/Other.pm
242             gen_readme = 1
243              
244             =head3 version_bump_code
245              
246             Use with CAUTION.
247              
248             This defines additional code which can be used for the automatic
249             update of version numbers in files. It has to be defined all on one
250             line, and basically be a subroutine definition, like so:
251              
252             version_bump_code = sub { my $version = shift; # code to update files ... };
253              
254             =head3 version_bump_files
255              
256             The list of files altered by the version_bump_code, so that all the
257             version changes can be committed at the same time. This is needed because
258             some tests require the test files to have the version-id in them,
259             and therefore all version commits should be done at the same time,
260             otherwise the tests will fail, and the commits won't work.
261              
262             =cut
263              
264             sub config_read {
265 0     0 1   my $self = shift;
266 0 0 0       my $filename = (@_ ? shift :
267             ($ENV{MOD_DEVAID_CONF} || 'mod_devaid.conf'));
268              
269 0 0         return unless -e $filename;
270              
271 0 0         open my $config_file, '<', $filename
272             or die "couldn't open config file $filename: $!";
273              
274 0           while (<$config_file>) {
275 0           chomp;
276 0 0         next if /\A\s*\Z/sm;
277 0 0         if (/\A(\w+)\s*=\s*(.+)\Z/sm)
278             {
279 0           my $key = $1;
280 0           my $value = $2;
281 0 0         if ($key =~ /^(modules|scripts|version_bump_files)$/) # plural thing
    0          
282             {
283 0           $self->{$key} = [split(/\s+/, $value)];
284             }
285             elsif ($key eq 'version_bump_code')
286             {
287 0 0         if ($value =~ /^sub/)
288             {
289 0           $self->{$key} = eval "$value";
290             }
291             else
292             {
293 0           warn "$key: $value is not code\n";
294             }
295             }
296             else
297             {
298 0           $self->{$key} = $value;
299             }
300             }
301             }
302 0           return 1;
303             }
304              
305             =head2 do_release
306              
307             Do a release, using darcs as the revision control system.
308              
309             =cut
310              
311             sub do_release {
312 0     0 1   my $self = shift;
313              
314 0           my $old_version = $self->get_old_version();
315 0           my $version = $self->get_new_version();
316              
317             # release notes
318             # note that we update the changes file first,
319             # so as not to have the automatic changes included in the list
320 0           $self->update_changes_file($old_version, $version);
321              
322 0           $self->generate_todo_file();
323              
324             # version
325 0           $self->version_bump($version, 1);
326              
327             # readme has to be generated after the version_bump
328             # because it may contain version info
329 0           $self->generate_readme_file(1);
330              
331             # release tag
332 0           $self->tag_release($version);
333              
334 0 0         if (-f 'Build.PL') # we are using Module::Build
    0          
335             {
336             # Rebuild the Build file and make the dist file.
337             # Note that this has to be done as a shell command
338             # because it needs the new Build script to get
339             # the correct version
340 0           my $command = "perl Build.PL && Build dist";
341 0           system($command);
342             }
343             elsif (-f 'Makefile.PL') # we are using ExtUtils::MakeMaker
344             {
345             # Rebuild the Makefile and make the dist file.
346             # Note that this has to be done as a shell command
347             # because it needs the new Makefile to get
348             # the correct version
349 0           my $command = "perl Makefile.PL && make dist";
350 0           system($command);
351             }
352             else # make a darcs dist
353             {
354 0           my $dist_rel_name = $self->{dist_name};
355 0           $dist_rel_name =~ s/::/-/g;
356 0           my $command = "darcs dist -d $dist_rel_name-$version";
357 0           system($command);
358             }
359             } # do_release
360              
361             =head2 version_bump
362              
363             Automate the update of the version, taken from B
364             and B
365              
366             =cut
367              
368             sub version_bump {
369 0     0 1   my $self = shift;
370 0           my $version = shift;
371 0 0         my $do_commit = (@_ ? shift : 0);
372              
373 0           my $old_version_file = $self->{old_version_file};
374 0           my $version_file = $self->{version_file};
375              
376 0           print STDERR "\$VERSION = '$version'\n";
377              
378             #================================================================
379             # change the version in various files
380             #
381 0           my @files = @{$self->{modules}};
  0            
382 0 0         push @files, @{$self->{scripts}} if @{$self->{scripts}};
  0            
  0            
383              
384 0           my $command;
385 0 0         if (@files)
386             {
387 0           $command = 'perl -pi -e "/VERSION\s+=\s+\'\d/ && s/VERSION\s+=\s+\'\d+\.\d+\w*\''
388             . "/VERSION = '${version}'/\" " .
389             join(' ', @files);
390 0           system($command);
391              
392 0           $command = 'perl -pi -e \'/^This describes version/ && s/B<\d+[.]\d+\w*>'
393             . "/B<$version>/' " .
394             join(' ', @files);
395 0           system($command);
396             }
397              
398             # call the user custom code
399 0 0 0       if (exists $self->{version_bump_code}
      0        
400             && defined $self->{version_bump_code}
401             && ref($self->{version_bump_code}) eq 'CODE')
402             {
403 0           $self->{version_bump_code}->($version);
404             }
405              
406             #================================================================
407              
408             # copy the current version to old_version_file
409 0 0 0       if (-f $old_version_file
410             && open(OVFILE, ">$old_version_file"))
411             {
412 0           print OVFILE "$version\n";
413 0           close(OVFILE);
414             }
415              
416 0 0 0       if ($do_commit && $self->{version_control} eq 'darcs')
    0 0        
417             {
418 0           $command = "darcs record -am 'bump version to $version' $old_version_file $version_file "
419 0           . join(' ', @files, @{$self->{version_bump_files}});
420 0           system($command);
421             }
422             elsif ($do_commit && $self->{version_control} eq 'svk')
423             {
424 0           $command = "svk commit -m 'bump version to $version' $old_version_file $version_file "
425 0           . join(' ', @files, @{$self->{version_bump_files}});
426 0           system($command);
427             }
428              
429             } # version_bump
430              
431             =head2 get_todo_content
432              
433             Get the content which would be put in a TODO file
434             generated using devtodo .todo file in project directory.
435              
436             Returns a string.
437              
438             =cut
439              
440             sub get_todo_content {
441 0     0 1   my $self = shift;
442              
443 0           my $product = $self->{dist_name};
444 0           my $todo_file = $self->{todo_file};
445              
446 0           my $title_str = "TODO list for $product";
447 0           my $format = 'todo=%i%[info]%f%2n. %[priority]%+1T\n%+1i%[info]Added:%[normal]%c %[info]Priority: %[normal]%p\n\n';
448 0           my $todo_cmd = "todo --format '$format' --use-format display=todo";
449 0           my $todo_str = `$todo_cmd`;
450 0 0         if (!$todo_str)
451             {
452 0           $todo_str = "\t** nothing to do! **\n";
453             }
454 0           my $ret_str = join("\n", $title_str, '=' x length($title_str), $todo_str);
455             }
456              
457             =head2 generate_todo_file
458              
459             Generate TODO file using devtodo .todo file in project directory.
460             Uses get_todo_content().
461              
462             =cut
463              
464             sub generate_todo_file {
465 0     0 1   my $self = shift;
466              
467 0 0         if ($self->{gen_todo})
468             {
469 0           my $product = $self->{dist_name};
470 0           my $todo_file = $self->{todo_file};
471 0           my $do_commit = $self->{commit_todo};
472              
473 0           my $todo_str = $self->get_todo_content();
474 0 0         if (open(OTFILE, ">${todo_file}"))
475             {
476 0           print OTFILE $todo_str, "\n";
477 0           close(OTFILE);
478 0           print "generated $todo_file\n";
479             }
480 0 0 0       if ($do_commit && $self->{version_control} eq 'darcs')
    0 0        
481             {
482 0           my $command = "darcs record -am 'generate TODO file' $todo_file";
483 0           system($command);
484             }
485             elsif ($do_commit && $self->{version_control} eq 'svk')
486             {
487 0           my $command = "svk commit -m 'generate TODO file' $todo_file";
488 0           system($command);
489             }
490             }
491             }
492              
493             =head2 get_readme_content
494              
495             Generate README content from PoD in module.
496             Only uses selected sections, rather than the whole thing.
497             Returns a string.
498              
499             =cut
500              
501             sub get_readme_content {
502 0     0 1   my $self = shift;
503              
504 0           my $pod_file = $self->{pod_file};
505 0           $self->{pod_sel}->select('NAME','VERSION','DESCRIPTION',
506             'INSTALLATION','CONTENTS','REQUIRES|PREREQUISITES',
507             'AUTHOR','SUPPORT','COPYRIGHT.*|LICENCE|LICENSE');
508 0           my $readme_pod;
509 0           my $io_pod = IO::String->new($readme_pod);
510 0           my $pod_file_fh;
511 0 0         open($pod_file_fh, $pod_file) or die "Could not open $pod_file";
512 0           $self->{pod_sel}->parse_from_filehandle($pod_file_fh, $io_pod);
513              
514             # reset the handle to zero so it can be read from
515 0           $io_pod->setpos(0);
516              
517 0           my $readme_txt;
518 0           my $io_txt = IO::String->new($readme_txt);
519              
520 0           $self->{pod_text}->parse_from_filehandle($io_pod, $io_txt);
521              
522 0           return $readme_txt;
523             }
524              
525             =head2 generate_readme_file
526              
527             Generate README file from PoD in module.
528             (uses get_readme_content)
529              
530             =cut
531              
532             sub generate_readme_file {
533 0     0 1   my $self = shift;
534 0 0         my $do_commit = (@_ ? shift : 0);
535              
536 0 0         if ($self->{gen_readme})
537             {
538 0           my $pod_file = $self->{pod_file};
539 0           my $readme_file = $self->{readme_file};
540 0           my $readme_str = $self->get_readme_content();
541 0 0         open RMFILE, ">$readme_file"
542             or die "Cannot write to $readme_file";
543 0           print RMFILE $readme_str;
544 0           close (RMFILE);
545 0           print "generated $readme_file\n";
546 0 0 0       if ($do_commit && $self->{version_control} eq 'darcs')
    0 0        
547             {
548 0           my $command = "darcs record -am 'generate README file' $readme_file";
549 0           system($command);
550             }
551             elsif ($do_commit && $self->{version_control} eq 'svk')
552             {
553 0           my $command = "svk commit -m 'generate README file' $readme_file";
554 0           system($command);
555             }
556             }
557             }
558              
559             =head2 get_new_changes
560              
561             Get the changes committed since the last release.
562             Generate a more compact format than the default.
563              
564             =cut
565              
566             sub get_new_changes {
567 0     0 1   my $self = shift;
568 0           my $old_version = shift;
569              
570 0           my $new_changes;
571 0 0         if ($self->{version_control} eq 'darcs')
    0          
572             {
573 0           $new_changes = $self->get_new_darcs_changes($old_version);
574             }
575             elsif ($self->{version_control} eq 'svk')
576             {
577 0           $new_changes = $self->get_new_svk_changes($old_version);
578             }
579 0           return $new_changes;
580             }
581              
582             =head2 get_changes_content
583              
584             Get the contents of what the new changes file should be.
585             Takes version and old_version id strings as arguments.
586             (uses get_new_changes)
587             Returns a string.
588              
589             =cut
590              
591             sub get_changes_content {
592 0     0 1   my $self = shift;
593 0           my $old_version = shift;
594 0           my $version = shift;
595              
596 0           my $product = $self->{dist_name};
597 0           my $new_changes = $self->get_new_changes($old_version);
598 0           chomp $new_changes;
599 0           my $date_str = `date "+%a %d %B %Y"`;
600 0           chomp $date_str;
601 0           my $changes_file = $self->{changes_file};
602 0           my $existing_changes = '';
603 0 0 0       if (-f $changes_file
604             && open(CFILE, $changes_file))
605             {
606 0           my $count = 0;
607 0           while (my $line = )
608             {
609             # skip the header part -- first three lines
610 0 0 0       if (!($line =~ /^Revision history/
      0        
      0        
      0        
611             || ($line =~ /^======/ && $count < 3)
612             || ($line =~ /^\s*$/ && $count < 3)))
613             {
614 0           $existing_changes .= $line;
615             }
616 0           $count++;
617             }
618 0           close (CFILE);
619             }
620 0           my $title_str = "Revision history for $product";
621 0           my $version_title_str = "$version $date_str";
622 0           my $ret_str = join("\n",
623             $title_str,
624             '=' x length($title_str),
625             '',
626             $version_title_str,
627             '-' x length($version_title_str),
628             '',
629             $new_changes,
630             $existing_changes
631             );
632 0           return $ret_str;
633             }
634              
635             =head2 get_old_version
636              
637             Get the version-id of the previous release from B
638              
639             =cut
640              
641             sub get_old_version {
642 0     0 1   my $self = shift;
643              
644 0           my $old_version_file = $self->{old_version_file};
645 0           my $old_version = '';
646              
647             # read the old version
648 0 0 0       if (-f $old_version_file
649             && open(OVFILE, $old_version_file))
650             {
651 0           while (my $line = )
652             {
653 0 0         if ($line =~ /^([0-9]+\.[0-9]+)$/)
654             {
655 0           eval "\$old_version = '$1';";
656 0           last;
657             }
658             }
659 0           close(OVFILE);
660             }
661 0           return $old_version;
662             } # get_old_version
663              
664             =head2 get_new_version
665              
666             Get the version-id of the up-and-coming release from B
667              
668             =cut
669              
670             sub get_new_version {
671 0     0 1   my $self = shift;
672              
673 0           my $new_version_file = $self->{version_file};
674 0           my $version = '';
675              
676             # read the old version
677 0 0 0       if (-f $new_version_file
678             && open(NVFILE, $new_version_file))
679             {
680 0           while (my $line = )
681             {
682 0 0         if ($line =~ /^(\d+\.\d+\w*)$/)
683             {
684 0           eval "\$version = '$1';";
685 0           last;
686             }
687             }
688 0           close(NVFILE);
689             }
690 0           return $version;
691             } # get_new_version
692              
693             =head1 INTERNAL METHODS
694              
695             These are documented for the developer only, and are not meant
696             to be used by the outside.
697              
698             =head2 get_new_darcs_changes
699              
700             Get the changes committed to darcs since the last release.
701             Generate a more compact format than the darcs changes default.
702              
703             =cut
704              
705             sub get_new_darcs_changes {
706 0     0 1   my $self = shift;
707 0           my $old_version = shift;
708              
709 0           my $command = "darcs changes --from-patch release-$old_version";
710 0           my $new_changes = '';
711 0 0         if (!`$command`) # check the command works
712             {
713 0           $command = 'darcs changes';
714             }
715 0 0         if (open(CFILE, "$command |"))
716             {
717 0           my $cdate = '';
718 0           while (my $line = )
719             {
720             # filter out the tagged release bit
721 0 0         if ($line =~ /^\s*tagged\s+release/)
    0          
    0          
722             {
723             }
724             # grab the date parts
725             elsif ($line =~ /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s+([a-zA-Z]+)\s+(\d+)\s+\d\d:\d\d:\d\d\s+\w+\s+(\d+)/)
726             {
727 0           my $month = $2;
728 0           my $day = $3;
729 0           my $year = $4;
730 0           $cdate = "$day $month $year";
731             }
732             elsif ($line =~ /\s+\*\s+/) # item start
733             {
734             # stick the date in
735 0           $line =~ s/(\s+\*\s+)/$1\($cdate\) /;
736 0           $new_changes .= $line;
737             }
738             else
739             {
740 0           $new_changes .= $line;
741             }
742             }
743 0           close CFILE;
744             }
745 0 0         if (!$new_changes) # get ALL the changes if that failed
746             {
747 0           $new_changes = `darcs changes`;
748             }
749 0           return $new_changes;
750             } # get_new_darcs_changes
751              
752             =head2 get_new_svk_changes
753              
754             Get the changes committed to svk since the last release.
755             Generate a more compact format than the svk changes default.
756              
757             =cut
758              
759             sub get_new_svk_changes {
760 0     0 1   my $self = shift;
761 0           my $old_version = shift;
762              
763             # find out the version of the most recent tag
764 0           my $info_cmd = "svk info";
765 0           my $fh;
766 0           my $depot_path = '';
767 0           my $local_last_rev = '';
768 0 0         if (open($fh, "$info_cmd |"))
769             {
770 0           while (my $line = <$fh>)
771             {
772 0 0         if ($line =~ /Depot Path:\s+(.*)/)
    0          
773             {
774 0           $depot_path = $1;
775             }
776             elsif ($line =~ /Last Changed Rev.:\s*(\d+)/)
777             {
778 0           $local_last_rev = $1;
779             }
780             }
781 0           close $fh;
782             }
783 0           my $tags_path = $depot_path;
784 0           $tags_path =~ s/trunk/tags/;
785              
786 0           $info_cmd = "svk info $tags_path";
787 0           my $tags_last_rev = '';
788 0 0         if (open($fh, "$info_cmd |"))
789             {
790 0           while (my $line = <$fh>)
791             {
792 0 0         if ($line =~ /Last Changed Rev.:\s*(\d+)/)
793             {
794 0           $tags_last_rev = $1;
795             }
796             }
797 0           close $fh;
798             }
799 0           my $new_changes = '';
800              
801 0           my $command = "svk log -r HEAD:$tags_last_rev";
802             # if the local change is older than the tag change,
803             # there are no changes; fall back to all changes.
804 0 0         if ($local_last_rev < $tags_last_rev)
805             {
806 0           $command = 'svk log';
807             }
808 0 0         if (!`$command`) # check the command works
809             {
810 0           $command = 'svk log';
811             }
812 0 0         if (open(CFILE, "$command |"))
813             {
814 0           my $cdate = '';
815 0           my $item = '';
816 0           while (my $line = )
817             {
818             # filter out the tagged release bit
819 0 0         if ($line =~ /^\s*tagged\s+release/)
    0          
    0          
    0          
820             {
821             }
822             # grab the date parts
823             elsif ($line =~ /^r\d+:\s+\w+\s+\|\s+(\d\d\d\d-\d+-\d+)/)
824             {
825 0           $cdate = $1;
826 0           $item = '';
827             }
828             elsif ($line =~ /----------/) # item start or end
829             {
830 0 0         if ($item) # end
831             {
832 0           $new_changes .= " * ($cdate) $item";
833             }
834             }
835             elsif ($line =~ /^$/) # blank
836             {
837 0 0         $item .= $line if $item;
838             }
839             else
840             {
841 0 0         if ($item)
842             {
843 0           $item .= " $line"; # alignment
844             }
845             else
846             {
847 0           $item .= $line;
848             }
849             }
850             }
851 0           close CFILE;
852 0 0         $new_changes .= "\n" if $new_changes;
853             }
854 0 0         if (!$new_changes) # get ALL the changes if that failed
855             {
856 0           $new_changes = `svk log`;
857             }
858 0           return $new_changes;
859             } # get_new_svk_changes
860              
861             =head2 update_changes_file
862              
863             Called by do_release. Overwrites the changes file and commits
864             the change. (uses get_changes_content)
865              
866             =cut
867              
868             sub update_changes_file {
869 0     0 1   my $self = shift;
870 0           my $old_version = shift;
871 0           my $version = shift;
872              
873 0           my $changes_str = $self->get_changes_content($old_version, $version);
874 0           my $changes_file = $self->{changes_file};
875 0 0         if (open(OCFILE, ">${changes_file}"))
876             {
877 0           print OCFILE $changes_str;
878 0           close(OCFILE);
879             }
880 0 0         if ($self->{version_control} eq 'darcs')
    0          
881             {
882 0           my $command = "darcs record -am 'update release notes' $changes_file";
883 0           system($command);
884             }
885             elsif ($self->{version_control} eq 'svk')
886             {
887 0           my $command = "svk commit -m 'update release notes' $changes_file";
888 0           system($command);
889             }
890             }
891              
892             =head2 tag_release
893              
894             Called by do_release. Tags the release.
895              
896             =cut
897              
898             sub tag_release {
899 0     0 1   my $self = shift;
900 0           my $version = shift;
901              
902 0 0         if ($self->{version_control} eq 'darcs')
    0          
903             {
904 0           my $command = "darcs tag -m release-$version --checkpoint";
905 0           system($command);
906             }
907             elsif ($self->{version_control} eq 'svk')
908             {
909             # find the tag path
910 0           my $info_cmd = "svk info";
911 0           my $fh;
912 0           my $depot_path = '';
913 0 0         if (open($fh, "$info_cmd |"))
914             {
915 0           while (my $line = <$fh>)
916             {
917 0 0         if ($line =~ /Depot Path:\s+(.*)/)
918             {
919 0           $depot_path = $1;
920             }
921             }
922 0           close $fh;
923             }
924 0           my $tags_path = $depot_path;
925 0           $tags_path =~ s/trunk/tags/;
926 0           my $command = "svk copy -p -m release-$version $depot_path $tags_path/v$version";
927 0           system($command);
928             }
929             } # tag_release
930              
931             =head1 REQUIRES
932              
933             Getopt::Long
934             Pod::Usage
935             Data::Dumper
936             Test::More
937              
938             =head1 SEE ALSO
939              
940             perl(1).
941              
942             =head1 INSTALLATION
943              
944             To install this module, run the following commands:
945              
946             perl Build.PL
947             ./Build
948             ./Build test
949             ./Build install
950              
951             =head1 BUGS
952              
953             Please report any bugs or feature requests to the author.
954              
955             =head1 AUTHOR
956              
957             Kathryn Andersen (RUBYKAT)
958             perlkat AT katspace dot com
959             http://www.katspace.com
960              
961             =head1 COPYRIGHT AND LICENCE
962              
963             Copyright (c) 2004-2007 by Kathryn Andersen
964              
965             This program is free software; you can redistribute it and/or modify it
966             under the same terms as Perl itself.
967              
968             =cut
969              
970             1; # End of Module::DevAid
971             __END__