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   10546844 use strict;
  4         25  
  4         176  
10 4     4   28 use warnings;
  4         10  
  4         366  
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.048';
16              
17 4     4   772 use Dist::Zilla 4 ();
  4         1873916  
  4         160  
18 4     4   29 use version 0.80 ();
  4         96  
  4         101  
19              
20 4     4   25 use Moose;
  4         8  
  4         36  
21 4     4   29129 use namespace::autoclean 0.09;
  4         118  
  4         37  
22 4     4   357 use Path::Tiny;
  4         13  
  4         329  
23 4     4   31 use Try::Tiny;
  4         11  
  4         260  
24 4     4   3226 use Types::Standard qw(Str RegexpRef Bool ArrayRef);
  4         339131  
  4         54  
25 4     4   7680 use Type::Utils qw(coerce from as via subtype);
  4         20846  
  4         55  
26              
27 4     4   3593 use constant _cache_fn => '.gitnxtver_cache';
  4         11  
  4         655  
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         19 use constant _CoercedRegexp => do {
38 4         18 my $tc = subtype as RegexpRef;
39 4         786 coerce $tc, from Str, via { qr/$_/ };
  4         335  
40 4         8146 $tc;
41 4     4   31 };
  4         11  
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   121276 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   1769 return [ sort map /$regexp/ ? try { version->parse("$1") } : (), @$tags ];
  64         4210  
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   422 my $versions = shift; # arrayref of versions sorted in ascending order
74              
75 30 100       515 return $versions->[-1]->stringify if @$versions;
76              
77 11         101 return undef;
78             } # end _max_version
79              
80             sub _last_version {
81 20     20   80 my ($self) = @_;
82              
83 20         58 my $last_ver;
84 20         1032 my $by_branch = $self->version_by_branch;
85 20         334 my $git = $self->git;
86              
87 20         1205 local $/ = "\n"; # Force record separator to be single newline
88              
89 20 100       93 if ($by_branch) {
90 12         56 my $head;
91 12         468 my $cachefile = path($self->zilla->root)->child(_cache_fn);
92 12 100       1592 if (-f $cachefile) {
93 3         148 ($head) = $git->rev_parse('HEAD');
94 3 100       26600 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   403 my @tags;
99 10         134 for ($git->rev_list(qw(--simplify-by-decoration --pretty=%d HEAD))) {
100 48 100       104059 /^\s*\((.+)\)/ or next;
101 24         374 push @tags, split /,\s*/, $1;
102             } # end for lines from git log
103 9         190 s/^tag:\s+// for @tags; # Git 1.8.3 says "tag: X" instead of "X"
104 9         1339 my $versions = _versions_from_tags($self->version_regexp, \@tags);
105 9 50       834 if ($self->logger->get_debug) {
106 0         0 $self->log_debug("Found version $_ on branch") for @$versions;
107             }
108 9         870 $last_ver = _max_version($versions);
109 10         1025 };
110 10 100       10704 if (defined $last_ver) {
111 7 100       200 ($head) = $git->rev_parse('HEAD') unless $head;
112 7         60033 print { $cachefile->openw } "$head $last_ver\n";
  7         235  
113 7         3476 return $last_ver;
114             }
115             } # end if version_by_branch
116              
117             # Consider versions from all branches:
118 11         678 $last_ver = _max_version($self->_all_versions);
119              
120 11 50 66     284 $self->log("WARNING: Unable to find version on current branch")
121             if defined($last_ver) and $by_branch;
122              
123 11         396 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 156148 my $self = shift;
146              
147             # Make sure we're not duplicating a version:
148 1         74 my $version = version->parse( $self->zilla->version );
149              
150             $self->log_fatal("version $version has already been tagged")
151 1 50       87 if grep +($_ == $version), @{ $self->_all_versions };
  1         63  
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 564410 my ($self) = @_;
163              
164             # override (or maybe needed to initialize)
165 23 100       224 return $ENV{V} if exists $ENV{V};
166              
167 20         129 my $last_ver = $self->_last_version;
168              
169 20 100       1631 return $self->first_version
170             unless defined $last_ver;
171              
172 13         3127 require Version::Next;
173 13         7527 my $new_ver = Version::Next::next_version($last_ver);
174 13         2989 $self->log("Bumping version from $last_ver to $new_ver");
175              
176 13         12280 return "$new_ver";
177             }
178              
179             sub prune_files {
180 1     1 0 13189 my $self = shift;
181              
182 1         9 for my $file (@{ $self->zilla->files }) {
  1         48  
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         127 return;
193             } # end prune_files
194              
195             __PACKAGE__->meta->make_immutable;
196 4     4   42 no Moose;
  4         11  
  4         39  
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.048
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