File Coverage

blib/lib/Dist/Zilla/Plugin/Git/NextVersion.pm
Criterion Covered Total %
statement 93 99 93.9
branch 23 28 82.1
condition 2 3 66.6
subroutine 21 22 95.4
pod 0 4 0.0
total 139 156 89.1


line stmt bran cond sub pod time code
1             #
2             # This file is part of Dist-Zilla-Plugin-Git
3             #
4             # This software is copyright (c) 2009 by Jerome Quelin.
5             #
6             # This is free software; you can redistribute it and/or modify it under
7             # the same terms as the Perl 5 programming language system itself.
8             #
9 4     4   9116835 use strict;
  4         27  
  4         159  
10 4     4   25 use warnings;
  4         11  
  4         328  
11              
12             package Dist::Zilla::Plugin::Git::NextVersion;
13             # ABSTRACT: Provide a version number by bumping the last git release tag
14              
15             our $VERSION = '2.047';
16              
17 4     4   636 use Dist::Zilla 4 ();
  4         1676055  
  4         153  
18 4     4   31 use version 0.80 ();
  4         100  
  4         92  
19              
20 4     4   27 use Moose;
  4         14  
  4         37  
21 4     4   26772 use namespace::autoclean 0.09;
  4         85  
  4         35  
22 4     4   309 use Path::Tiny;
  4         12  
  4         288  
23 4     4   25 use Try::Tiny;
  4         11  
  4         256  
24 4     4   2496 use Types::Standard qw(Str RegexpRef Bool ArrayRef);
  4         311419  
  4         77  
25 4     4   6533 use Type::Utils qw(coerce from as via subtype);
  4         19072  
  4         43  
26              
27 4     4   3162 use constant _cache_fn => '.gitnxtver_cache';
  4         9  
  4         627  
28              
29             with 'Dist::Zilla::Role::BeforeRelease',
30             'Dist::Zilla::Role::AfterRelease',
31             'Dist::Zilla::Role::FilePruner',
32             'Dist::Zilla::Role::VersionProvider',
33             'Dist::Zilla::Role::Git::Repo';
34              
35             # -- attributes
36              
37 4         9 use constant _CoercedRegexp => do {
38 4         19 my $tc = subtype as RegexpRef;
39 4         658 coerce $tc, from Str, via { qr/$_/ };
  4         305  
40 4         7391 $tc;
41 4     4   30 };
  4         9  
42              
43             has version_regexp => ( is => 'ro', isa => _CoercedRegexp, coerce => 1,
44             default => sub { qr/^v(.+)$/ } );
45              
46             has first_version => ( is => 'ro', isa => Str, default => '0.001' );
47              
48             has version_by_branch => ( is => 'ro', isa => Bool, default => 0 );
49              
50             sub _versions_from_tags {
51 31     31   107154 my ($regexp, $tags) = @_;
52              
53             # WARNING: The quotes in "$1" are necessary, because version doesn't
54             # call get magic properly.
55 31 100   64   1463 return [ sort map /$regexp/ ? try { version->parse("$1") } : (), @$tags ];
  64         3971  
56             } # end _versions_from_tags
57              
58             has _all_versions => (
59             is => 'ro', isa => ArrayRef,
60             init_arg => undef,
61             lazy => 1,
62             default => sub {
63             my $self = shift;
64             my $v = _versions_from_tags($self->version_regexp, [ $self->git->tag ]);
65             if ($self->logger->get_debug) {
66             $self->log_debug("Found version $_") for @$v;
67             }
68             $v;
69             }
70             );
71              
72             sub _max_version {
73 30     30   337 my $versions = shift; # arrayref of versions sorted in ascending order
74              
75 30 100       476 return $versions->[-1]->stringify if @$versions;
76              
77 11         59 return undef;
78             } # end _max_version
79              
80             sub _last_version {
81 20     20   119 my ($self) = @_;
82              
83 20         45 my $last_ver;
84 20         854 my $by_branch = $self->version_by_branch;
85 20         300 my $git = $self->git;
86              
87 20         995 local $/ = "\n"; # Force record separator to be single newline
88              
89 20 100       89 if ($by_branch) {
90 12         97 my $head;
91 12         382 my $cachefile = path($self->zilla->root)->child(_cache_fn);
92 12 100       1501 if (-f $cachefile) {
93 3         147 ($head) = $git->rev_parse('HEAD');
94 3 100       29426 return $1 if $cachefile->slurp =~ /^\Q$head\E (.+)/;
95             }
96             try {
97             # Note: git < 1.6.1 doesn't understand --simplify-by-decoration or %d
98 10     10   346 my @tags;
99 10         118 for ($git->rev_list(qw(--simplify-by-decoration --pretty=%d HEAD))) {
100 48 100       87944 /^\s*\((.+)\)/ or next;
101 24         316 push @tags, split /,\s*/, $1;
102             } # end for lines from git log
103 9         202 s/^tag:\s+// for @tags; # Git 1.8.3 says "tag: X" instead of "X"
104 9         1141 my $versions = _versions_from_tags($self->version_regexp, \@tags);
105 9 50       740 if ($self->logger->get_debug) {
106 0         0 $self->log_debug("Found version $_ on branch") for @$versions;
107             }
108 9         710 $last_ver = _max_version($versions);
109 10         975 };
110 10 100       8045 if (defined $last_ver) {
111 7 100       242 ($head) = $git->rev_parse('HEAD') unless $head;
112 7         44837 print { $cachefile->openw } "$head $last_ver\n";
  7         183  
113 7         3974 return $last_ver;
114             }
115             } # end if version_by_branch
116              
117             # Consider versions from all branches:
118 11         584 $last_ver = _max_version($self->_all_versions);
119              
120 11 50 66     239 $self->log("WARNING: Unable to find version on current branch")
121             if defined($last_ver) and $by_branch;
122              
123 11         328 return $last_ver;
124             }
125              
126             # -- role implementation
127              
128             around dump_config => sub
129             {
130             my $orig = shift;
131             my $self = shift;
132              
133             my $config = $self->$orig;
134              
135             $config->{+__PACKAGE__} = {
136             (map +($_ => $self->$_), qw(version_regexp first_version)),
137             version_by_branch => $self->version_by_branch ? 1 : 0,
138             blessed($self) ne __PACKAGE__ ? ( version => $VERSION ) : (),
139             };
140              
141             return $config;
142             };
143              
144             sub before_release {
145 1     1 0 152563 my $self = shift;
146              
147             # Make sure we're not duplicating a version:
148 1         70 my $version = version->parse( $self->zilla->version );
149              
150             $self->log_fatal("version $version has already been tagged")
151 1 50       85 if grep +($_ == $version), @{ $self->_all_versions };
  1         70  
152             }
153              
154             sub after_release {
155 0     0 0 0 my $self = shift;
156              
157             # Remove the cache file, just in case:
158 0         0 path($self->zilla->root)->child(_cache_fn)->remove;
159             }
160              
161             sub provide_version {
162 23     23 0 500796 my ($self) = @_;
163              
164             # override (or maybe needed to initialize)
165 23 100       142 return $ENV{V} if exists $ENV{V};
166              
167 20         195 my $last_ver = $self->_last_version;
168              
169 20 100       1542 return $self->first_version
170             unless defined $last_ver;
171              
172 13         2936 require Version::Next;
173 13         6189 my $new_ver = Version::Next::next_version($last_ver);
174 13         2721 $self->log("Bumping version from $last_ver to $new_ver");
175              
176 13         11543 return "$new_ver";
177             }
178              
179             sub prune_files {
180 1     1 0 13317 my $self = shift;
181              
182 1         15 for my $file (@{ $self->zilla->files }) {
  1         54  
183              
184             # Ensure we don't distribute .gitnxtver_cache:
185 0 0       0 next unless $file->name eq _cache_fn;
186              
187 0         0 $self->log_debug([ 'pruning %s', $file->name ]);
188              
189 0         0 $self->zilla->prune_file($file);
190             }
191              
192 1         139 return;
193             } # end prune_files
194              
195             __PACKAGE__->meta->make_immutable;
196 4     4   33 no Moose;
  4         8  
  4         44  
197             1;
198              
199             __END__
200              
201             =pod
202              
203             =encoding UTF-8
204              
205             =head1 NAME
206              
207             Dist::Zilla::Plugin::Git::NextVersion - Provide a version number by bumping the last git release tag
208              
209             =head1 VERSION
210              
211             version 2.047
212              
213             =head1 SYNOPSIS
214              
215             In your F<dist.ini>:
216              
217             [Git::NextVersion]
218             first_version = 0.001 ; this is the default
219             version_by_branch = 0 ; this is the default
220             version_regexp = ^v(.+)$ ; this is the default
221              
222             =head1 DESCRIPTION
223              
224             This does the L<VersionProvider|Dist::Zilla::Role::VersionProvider> role.
225             It finds the last version number from your Git tags, increments it
226             using L<Version::Next>, and uses the result as the C<version> parameter
227             for your distribution.
228              
229             In addition, when making a release, it ensures that the version being
230             released has not already been tagged. (The
231             L<Git::Tag|Dist::Zilla::Plugin::Git::Tag> plugin has a similar check,
232             but Git::Tag only checks for an exact match on the tag. Since
233             Git::NextVersion knows how to extract version numbers from tags, it
234             can find duplicates that Git::Tag would miss.)
235              
236             The plugin accepts the following options:
237              
238             =over
239              
240             =item *
241              
242             C<first_version> - if the repository has no tags at all, this version
243             is used as the first version for the distribution. It defaults to "0.001".
244              
245             =item *
246              
247             C<version_by_branch> - if true, consider only tags on the current
248             branch when looking for the previous version. If you have a
249             maintenance branch for stable releases and a development branch for
250             trial releases, you should set this to 1. (You'll also need git
251             version 1.6.1 or later.) The default is to look at all tags, because
252             finding the tags reachable from a branch is a more expensive operation
253             than simply listing all tags.
254              
255             =item *
256              
257             C<version_regexp> - regular expression that matches a tag containing
258             a version. It must capture the version into $1. Defaults to ^v(.+)$
259             which matches the default C<tag_format> from the
260             L<Git::Tag|Dist::Zilla::Plugin::Git::Tag> plugin.
261             If you change C<tag_format>, you B<must> set a corresponding C<version_regexp>.
262              
263             =back
264              
265             You can also set the C<V> environment variable to override the new version.
266             This is useful if you need to bump to a specific version. For example, if
267             the last tag is 0.005 and you want to jump to 1.000 you can set V = 1.000.
268              
269             $ V=1.000 dzil release
270              
271             Because tracing history takes time, if you use the
272             C<version_by_branch> option, Git::NextVersion will create a
273             F<.gitnxtver_cache> file in your repository root to track the highest
274             version number that is an ancestor of the HEAD revision. You should
275             add F<.gitnxtver_cache> to your F<.gitignore> file. It will
276             automatically be pruned from the distribution.
277              
278             =for Pod::Coverage provide_version
279             prune_files
280             before_release
281             after_release
282              
283             =head1 SUPPORT
284              
285             Bugs may be submitted through L<the RT bug tracker|https://rt.cpan.org/Public/Dist/Display.html?Name=Dist-Zilla-Plugin-Git>
286             (or L<bug-Dist-Zilla-Plugin-Git@rt.cpan.org|mailto:bug-Dist-Zilla-Plugin-Git@rt.cpan.org>).
287              
288             There is also a mailing list available for users of this distribution, at
289             L<http://dzil.org/#mailing-list>.
290              
291             There is also an irc channel available for users of this distribution, at
292             L<C<#distzilla> on C<irc.perl.org>|irc://irc.perl.org/#distzilla>.
293              
294             =head1 AUTHOR
295              
296             Jerome Quelin
297              
298             =head1 COPYRIGHT AND LICENCE
299              
300             This software is copyright (c) 2009 by Jerome Quelin.
301              
302             This is free software; you can redistribute it and/or modify it under
303             the same terms as the Perl 5 programming language system itself.
304              
305             =cut