per(l)version

Yanick on 2008-03-20T02:13:22

Updating the version number is all the files of a module distribution is a pain in the behind. It's repetitive, boring and ridiculously prone to errors -- in short, a task fit for a script, not for humans. So I decided to hack myself a little something, for the time being called perversion, to take care of it.

The idea is pretty straight-forward. In the root directory of my distribution, I have a config file named perversionrc that contains Perl code that return an hash of all files where the the module version is given, and regular expressions to capture the said version. For example, here's the perversionrc of WWW::Ohloh::API:

use File::Find::Rule;

my %file;

$file{README} = qr/WWW-Ohloh-API version (\S+)/;

for my $m ( File::Find::Rule->file->name( '*.pm' )->in( 'lib' ) ) {
   $file{$m} = [ qr/\$VERSION\s*=\s*'(.*?)';/,
                 qr/This document describes \S+ version (\S*)/ ];
}

%file;

Once that file is present, perversion is ready to rock. You want to verify that the version number is consistent in all given files?

$ perversion
distribution version is set to 0.0.9
lib/WWW/Ohloh/API/Enlistment.pm:12: 0.0.6
!!! does not match distribution version (0.0.9) !!!
lib/WWW/Ohloh/API/Repository.pm:10: 0.0.6
!!! does not match distribution version (0.0.9) !!!
lib/WWW/Ohloh/API/ContributorLanguageFact.pm:10: 0.0.6
!!! does not match distribution version (0.0.9) !!!
lib/WWW/Ohloh/API/ContributorFact.pm:10: 0.0.6
!!! does not match distribution version (0.0.9) !!!
lib/WWW/Ohloh/API/ContributorFact.pm:241: 0.0.6
!!! does not match distribution version (0.0.9) !!!
Want to set the version to a specific value?
$ perversion -set 1.2.3
changing all versions to 1.2.3..
        updating lib/WWW/Ohloh/API.pm..
        updating lib/WWW/Ohloh/API/Language.pm..
        updating lib/WWW/Ohloh/API/ActivityFacts.pm..
        updating lib/WWW/Ohloh/API/Account.pm..
        updating lib/WWW/Ohloh/API/Kudos.pm..
        updating lib/WWW/Ohloh/API/Project.pm..
        updating lib/WWW/Ohloh/API/Enlistment.pm..
        updating lib/WWW/Ohloh/API/KudoScore.pm..
        updating lib/WWW/Ohloh/API/Factoid.pm..
        updating lib/WWW/Ohloh/API/Repository.pm..
        updating lib/WWW/Ohloh/API/Projects.pm..
        updating lib/WWW/Ohloh/API/ContributorLanguageFact.pm..
        updating lib/WWW/Ohloh/API/Languages.pm..
        updating lib/WWW/Ohloh/API/ActivityFact.pm..
        updating lib/WWW/Ohloh/API/ContributorFact.pm..
        updating lib/WWW/Ohloh/API/Analysis.pm..
        updating lib/WWW/Ohloh/API/Kudo.pm..
        updating README..
done
Don't want to go through the mental challenge of computing the next version yourself? No problem!
$ perversion -inc revision
distribution version is set to 1.2.3
new distribution version is 1.2.4

$ perversion -inc minor
distribution version is set to 1.2.4
new distribution version is 1.3.0

$ perversion -inc major
distribution version is set to 1.3.0
new distribution version is 2.0.0

$ perversion -inc alpha
distribution version is set to 2.0.0
new distribution version is 2.0_1

The script is still raw and in need of polishing, but for those interested, it's available from its very own git repository.


Like yours, but done with PPI...

Alias on 2008-03-20T08:01:23

http://search.cpan.org/src/ADAMK/PPI-PowerToys-0.10/lib/PPI/App/Version.pm

I really should chuck some proper docs on this and release it properly, instead of just relying on word of mouth :(

Re:Like yours, but done with PPI...

jplindstrom on 2008-03-20T11:45:43

Shouldn't that be in the PPIx namespace?

Or was this written earlier than that?

Re:Like yours, but done with PPI...

Yanick on 2008-03-21T14:58:17

At first, I also thought of tackling the problem with PPI, but it has the disadvantage of not being able to much with the pod (I think) and, of course, non-Perl files like the README and friends. And those are the harder half of the problem, because in the code one could do

    use My::Module::Version; our $VERSION = $My::Module::Version::VERSION;

and get away with only changing the $VERSION in My::Module. Or, at least, I think one could do that. I've didn't dare to put that theory to the test with CPAN yet. :-)

another one

daxim on 2008-03-20T11:14:30

http://search.cpan.org/src/ANDYA/Perl-Version-1.004/examples/perl-reversion

Nearly everything...

barbie on 2008-03-20T11:38:00

The one place I regularly forget to update is the META.yml. However, version strings in a META.yml get a little tricky as you need to change them in context of the distribution and modules versions and nothing else. Been thinking I need to automated that somehow ... when I have time ;)

Re:Nearly everything...

Yanick on 2008-03-20T12:55:12

But then, META.yml is dealt with by './Build dist' or 'make dist', so one shouldn't have to dabble with it, no?

And this being said, based on a five second pre-morning coffee perusal of the way META.yml presents its stuff (i.e., I'm probably wrong but still too sleepy for it to stop me :-) ), I think that something looking like the following added to the perversionrc hash should do the trick:

$h{META.yml} = [
    qr/ ^ name:    \s \S+   $
          version: \s (.*?) $ /x,
    qr/ ^  \s{4} file:    \s \S+   $
           \s{4} version: \s (.*?) $ /x,
];

Of course, if the different files don't all share the distro version, then forget everything I'd said. :-)

Re:Nearly everything...

barbie on 2008-03-20T18:10:48

But then, META.yml is dealt with by './Build dist' or 'make dist', so one shouldn't have to dabble with it, no?

I don't use M::B and EU::MM doesn't build a fully documented META.yml. As such I write all my META.yml files by hand, as that way I can be guaranteed to include all the details I want.

Also the ordering is not guaranteed, so your regex may work in some cases but not all. That's why I said it was tricky ;) A good headstart mind :)

Re:Nearly everything...

Yanick on 2008-03-21T15:12:45

Something that could be done would be to pervert perversion a little bit more still. Instead of giving a regular expression as a value in the config hash, one could also pass references to a sub that would then be able to do whatever it wants with the file content... Including parsing its YAML, updating versions at the right places, and then spitting back the result. It shouldn't be much more than a few lines more. I might hack that in later on today, just for fun. :-)

Re:Nearly everything...

Yanick on 2008-03-21T19:15:45

And here we go. I've updated the code such that you can now hook a subroutine ref in the perversionrc hash. E.g.:

use YAML qw/ Load Dump /;

sub do_meta {
    my ( $text, $version ) = @_;

    my $yaml = Load( $text );

    my @old;

    push @old, $yaml->{version};
    $yaml->{version} = $version;

    for ( values %{ $yaml->{provides} } ) {
        push @old, $_->{version};
        $_->{version} = $version;
    }

    return Dump( $yaml ), @old;
}

(
    'META.yml'=> \&do_meta,
);

Passing the old versions is not mandatory, but useful if you want the version verification functionality to work.