File Coverage

blib/lib/Module/Release/Git.pm
Criterion Covered Total %
statement 60 86 69.7
branch 12 20 60.0
condition 3 11 27.2
subroutine 12 16 75.0
pod 10 10 100.0
total 97 143 67.8


line stmt bran cond sub pod time code
1 5     5   152977 use v5.10;
  5         38  
2              
3             package Module::Release::Git;
4              
5 5     5   45 use strict;
  5         29  
  5         188  
6 5     5   43 use warnings;
  5         11  
  5         283  
7 5     5   47 use Exporter qw(import);
  5         17  
  5         1148  
8              
9             our @EXPORT = qw(
10             check_vcs
11             get_recent_contributors
12             get_vcs_tag_format
13             is_allowed_branch
14             make_vcs_tag
15             vcs_branch
16             vcs_commit_message
17             vcs_commit_message_template
18             vcs_exit
19             vcs_tag
20             );
21              
22             our $VERSION = '1.016';
23              
24             =encoding utf8
25              
26             =head1 NAME
27              
28             Module::Release::Git - Use Git with Module::Release
29              
30             =head1 SYNOPSIS
31              
32             The release script automatically loads this module if it sees a
33             F<.git> directory. The module exports C, C, and
34             C.
35              
36             =head1 DESCRIPTION
37              
38             Module::Release::Git subclasses Module::Release, and provides
39             its own implementations of the C and C methods
40             that are suitable for use with a Git repository.
41              
42             These methods are B exported in to the callers namespace
43             using Exporter.
44              
45             This module depends on the external git binary (so far).
46              
47             =over 4
48              
49             =item check_vcs()
50              
51             Check the state of the Git repository.
52              
53             =cut
54              
55             sub _get_time {
56 0     0   0 my( $self ) = @_;
57 0         0 require POSIX;
58 0         0 POSIX::strftime( '%Y%m%d%H%M%S', localtime );
59             }
60              
61             sub check_vcs {
62 6     6 1 6760 my $self = shift;
63              
64 6         19 $self->_print( "Checking state of Git... " );
65              
66 6         22 my $git_status = $self->run('git status -s 2>&1');
67              
68 5     5   59 no warnings 'uninitialized';
  5         20  
  5         5341  
69              
70 6         26 my $branch = $self->vcs_branch;
71              
72 6         13 my $up_to_date = ($git_status eq '');
73              
74 6 100       28 $self->_die( "\nERROR: Git is not up-to-date: Can't release files\n\n$git_status\n" )
75             unless $up_to_date;
76              
77 2         9 $self->_print( "Git up-to-date on branch $branch\n" );
78              
79 2         8 return 1;
80             }
81              
82             =item get_recent_contributors()
83              
84             Return a list of contributors since last release.
85              
86             =cut
87              
88             sub get_recent_contributors {
89 0     0 1 0 my $self = shift;
90              
91 0         0 chomp( my $last_tagged_commit = $self->run("git rev-list --tags --max-count=1") );
92 0         0 chomp( my @commits_from_last_tag = split /\R/, $self->run("git rev-list $last_tagged_commit..HEAD") );
93              
94             my @authors_since_last_tag =
95 0         0 map { qx{git show --no-patch --pretty=format:'%an <%ae>' $_} }
  0         0  
96             @commits_from_last_tag;
97 0         0 my %authors = map { $_, 1 } @authors_since_last_tag;
  0         0  
98 0         0 my @authors = sort keys %authors;
99              
100 0         0 return @authors;
101             }
102              
103             =item is_allowed_branch
104              
105             Returns true if the current branch is allowed to release.
106              
107             1. Look at the config for C. That's a comma-separated
108             list of allowed branch names. If the current branch is exactly any of
109             those, return true. Or, keep trying.
110              
111             2. Look at the config for C. If the current
112             branch matches that Perl pattern, return true. Or, keep trying.
113              
114             3. If the current branch is exactly C or C
, return true.
115              
116             4. Or, return false.
117              
118             =cut
119              
120             sub is_allowed_branch {
121 17     17 1 39063 my( $self ) = @_;
122 17         64 my $branch = $self->vcs_branch;
123              
124 17         51 return do {
125 17 100 0     45 if( $self->config->allowed_branches ) {
    50          
    0          
126 9         142 my $s = $self->config->allowed_branches;
127 9         119 scalar grep { $_ eq $branch } split /\s*,\s*/, $s;
  27         89  
128             }
129             elsif( $self->config->allowed_branches_regex ) {
130 8         207 my $re = eval { my $r = $self->config->allowed_branches_regex; qr/$r/ };
  8         19  
  8         124  
131 8         77 $branch =~ m/$re/;
132             }
133 0         0 elsif( $branch eq 'master' or $branch eq 'main' ) { 1 }
134 0         0 else { 0 }
135             };
136             }
137              
138             =item get_vcs_tag_format
139              
140             Return the tag format. It's a sprintf-like syntax, but with one format:
141              
142             %v replace with the full version
143              
144             If you've set C<> in the configuration, it uses that. Otherwise it
145             returns C.
146              
147             =cut
148              
149             sub get_vcs_tag_format {
150 9     9 1 17 my( $self ) = @_;
151 9 50       22 $self->config->git_default_tag || 'release-%v'
152             }
153              
154             =item make_vcs_tag
155              
156             By default, examines the name of the remote file
157             (i.e. F) and constructs a tag string like
158             C from it. Override this method if you want to use a
159             different tagging scheme, or don't even call it.
160              
161             =cut
162              
163             sub make_vcs_tag {
164 9     9 1 6245 my( $self, $tag_format ) = @_;
165 9 50       26 $tag_format = defined $tag_format ? $tag_format : $self->get_vcs_tag_format;
166              
167 9         232 my $version = eval { $self->dist_version };
  9         25  
168 9         30 my $err = $@;
169 9 100       49 unless( defined $version ) {
170 3         29 $self->_warn( "Could not get version [$err]" );
171 3         18 $version = $self->_get_time;
172             }
173              
174 9         40 $tag_format =~ s/%v/$version/e;
  9         25  
175              
176 9         45 return $tag_format;
177             }
178              
179             =item vcs_branch()
180              
181             Return the current branch name.
182              
183             =cut
184              
185             sub vcs_branch {
186 8     8 1 8215 state $branch;
187 8 100       30 return $branch if $branch;
188              
189 5         11 my( $self ) = @_;
190 5         15 ( $branch ) = $self->run('git rev-parse --abbrev-ref HEAD');
191 5         42 chomp( $branch );
192 5         21 $branch;
193             }
194              
195             =item vcs_commit_message_template()
196              
197             Returns the config for C, or the default C<'* for version %s'>.
198             This is a C ready string. The first argument to C
199             is the release version.
200              
201             =cut
202              
203             sub vcs_commit_message_template {
204 1     1 1 5763 my( $self ) = @_;
205 1   50     6 $self->config->commit_message_format // '* for version %s'
206             }
207              
208             =item vcs_commit_message()
209              
210             Returns the commit message, using C as the
211             format.
212              
213             =cut
214              
215             sub vcs_commit_message {
216 0     0 1 0 my( $self, $args ) = @_;
217 0         0 my $template = $self->vcs_commit_message_template;
218 0         0 sprintf $template, $args->{version};
219             }
220              
221             =item vcs_exit
222              
223             Perform repo tasks post-release. This one pushes origin to master
224             and pushes tags.
225              
226             =cut
227              
228             sub vcs_exit {
229 0     0 1 0 my( $self, $tag ) = @_;
230              
231 0   0     0 $tag ||= $self->make_vcs_tag;
232              
233 0         0 $self->_print( "Cleaning up git\n" );
234              
235 0 0       0 return 0 unless defined $tag;
236              
237 0         0 $self->_print( "Pushing to origin\n" );
238 0         0 $self->run( "git push origin master" );
239              
240 0         0 $self->_print( "Pushing tags\n" );
241 0         0 $self->run( "git push --tags" );
242              
243 0         0 return 1;
244             }
245              
246             =item vcs_tag(TAG)
247              
248             Tag the release in local Git, using the value from C.
249              
250             =cut
251              
252             sub vcs_tag {
253 5     5 1 10 my( $self, $tag ) = @_;
254              
255 5   66     20 $tag ||= $self->make_vcs_tag;
256              
257 5         20 $self->_print( "Tagging release with $tag\n" );
258              
259 5 50       18 return 0 unless defined $tag;
260              
261 5         17 $self->run( "git tag $tag" );
262              
263 5         65 return 1;
264             }
265              
266             =back
267              
268             =head1 TO DO
269              
270             =over 4
271              
272             =item Use Gitlib.pm whenever it exists
273              
274             =item More options for tagging
275              
276             =back
277              
278             =head1 SEE ALSO
279              
280             L, L
281              
282             =head1 SOURCE AVAILABILITY
283              
284             This module is in Github:
285              
286             https://github.com/briandfoy/module-release-git
287              
288             =head1 AUTHOR
289              
290             brian d foy,
291              
292             =head1 COPYRIGHT AND LICENSE
293              
294             Copyright © 2007-2021, brian d foy . All rights reserved.
295              
296             You may redistribute this under the same terms as the Artistic License 2.0.
297              
298             =cut
299              
300             1;