File Coverage

blib/lib/Module/Release/Git.pm
Criterion Covered Total %
statement 41 62 66.1
branch 7 12 58.3
condition 2 6 33.3
subroutine 9 12 75.0
pod 6 6 100.0
total 65 98 66.3


line stmt bran cond sub pod time code
1 3     3   583474 use v5.10;
  3         18  
2              
3             package Module::Release::Git;
4              
5 3     3   25 use strict;
  3         11  
  3         106  
6 3     3   23 use warnings;
  3         12  
  3         145  
7 3     3   21 use Exporter qw(import);
  3         12  
  3         405  
8              
9             our @EXPORT = qw(
10             check_vcs vcs_tag vcs_exit make_vcs_tag get_vcs_tag_format
11             get_recent_contributors
12             );
13              
14             our $VERSION = '1.015';
15              
16             =encoding utf8
17              
18             =head1 NAME
19              
20             Module::Release::Git - Use Git with Module::Release
21              
22             =head1 SYNOPSIS
23              
24             The release script automatically loads this module if it sees a
25             F<.git> directory. The module exports C<check_vcs>, C<vcs_tag>, and
26             C<make_vcs_tag>.
27              
28             =head1 DESCRIPTION
29              
30             Module::Release::Git subclasses Module::Release, and provides
31             its own implementations of the C<check_vcs()> and C<vcs_tag()> methods
32             that are suitable for use with a Git repository.
33              
34             These methods are B<automatically> exported in to the callers namespace
35             using Exporter.
36              
37             This module depends on the external git binary (so far).
38              
39             =over 4
40              
41             =item check_vcs()
42              
43             Check the state of the Git repository.
44              
45             =cut
46              
47             sub check_vcs {
48 6     6 1 6555 my $self = shift;
49              
50 6         19 $self->_print( "Checking state of Git... " );
51              
52 6         20 my $git_status = $self->run('git status -s 2>&1');
53              
54 3     3   22 no warnings 'uninitialized';
  3         5  
  3         2276  
55              
56 6         22 my( $branch ) = $self->run('git rev-parse --abbrev-ref HEAD');
57              
58 6         19 my $up_to_date = ($git_status eq '');
59              
60 6 100       31 $self->_die( "\nERROR: Git is not up-to-date: Can't release files\n\n$git_status\n" )
61             unless $up_to_date;
62              
63 2         8 $self->_print( "Git up-to-date on branch $branch\n" );
64              
65 2         7 return 1;
66             }
67              
68             =item vcs_tag(TAG)
69              
70             Tag the release in local Git.
71              
72             =cut
73              
74             sub vcs_tag {
75 5     5 1 50 my( $self, $tag ) = @_;
76              
77 5   66     22 $tag ||= $self->make_vcs_tag;
78              
79 5         28 $self->_print( "Tagging release with $tag\n" );
80              
81 5 50       19 return 0 unless defined $tag;
82              
83 5         21 $self->run( "git tag $tag" );
84              
85 5         53 return 1;
86             }
87              
88             =item make_vcs_tag
89              
90             By default, examines the name of the remote file
91             (i.e. F<Foo-Bar-0.04.tar.gz>) and constructs a tag string like
92             C<release-0.04> from it. Override this method if you want to use a
93             different tagging scheme, or don't even call it.
94              
95             =cut
96              
97             sub make_vcs_tag {
98 9     9 1 8018 my( $self, $tag_format ) = @_;
99 9 50       46 $tag_format = defined $tag_format ? $tag_format : $self->get_vcs_tag_format;
100              
101 9         101 my $version = eval { $self->dist_version };
  9         22  
102 9         36 my $err = $@;
103 9 100       34 unless( defined $version ) {
104 3         29 $self->_warn( "Could not get version [$err]" );
105 3         17 $version = $self->_get_time;
106             }
107              
108 9         53 $tag_format =~ s/%v/$version/e;
  9         33  
109              
110 9         51 return $tag_format;
111             }
112              
113             sub _get_time {
114 0     0   0 my( $self ) = @_;
115 0         0 require POSIX;
116 0         0 POSIX::strftime( '%Y%m%d%H%M%S', localtime );
117             }
118              
119             =item get_vcs_tag_format
120              
121             Return the tag format. It's a sprintf-like syntax, but with one format:
122              
123             %v replace with the full version
124              
125             If you've set C<> in the configuration, it uses that. Otherwise it
126             returns C<release-%v>.
127              
128             =cut
129              
130             sub get_vcs_tag_format {
131 9     9 1 26 my( $self ) = @_;
132              
133 9 50       38 $self->config->get( 'git_default_tag' ) ||
134             'release-%v'
135             }
136              
137             =item vcs_exit
138              
139             Perform repo tasks post-release. This one pushes origin to master
140             and pushes tags.
141              
142             =cut
143              
144             sub vcs_exit {
145 0     0 1   my( $self, $tag ) = @_;
146              
147 0   0       $tag ||= $self->make_vcs_tag;
148              
149 0           $self->_print( "Cleaning up git\n" );
150              
151 0 0         return 0 unless defined $tag;
152              
153 0           $self->_print( "Pushing to origin\n" );
154 0           $self->run( "git push origin master" );
155              
156 0           $self->_print( "Pushing tags\n" );
157 0           $self->run( "git push --tags" );
158              
159 0           return 1;
160             }
161              
162             =item get_recent_contributors()
163              
164             Return a list of contributors since last release.
165              
166             =cut
167              
168             sub get_recent_contributors {
169 0     0 1   my $self = shift;
170              
171 0           chomp( my $last_tagged_commit = $self->run("git rev-list --tags --max-count=1") );
172 0           chomp( my @commits_from_last_tag = split /\R/, $self->run("git rev-list $last_tagged_commit..HEAD") );
173              
174             my @authors_since_last_tag =
175 0           map { qx{git show --no-patch --pretty=format:'%an <%ae>' $_} }
  0            
176             @commits_from_last_tag;
177 0           my %authors = map { $_, 1 } @authors_since_last_tag;
  0            
178 0           my @authors = sort keys %authors;
179              
180 0           return @authors;
181             }
182              
183             =back
184              
185             =head1 TO DO
186              
187             =over 4
188              
189             =item Use Gitlib.pm whenever it exists
190              
191             =item More options for tagging
192              
193             =back
194              
195             =head1 SEE ALSO
196              
197             L<Module::Release::Subversion>, L<Module::Release>
198              
199             =head1 SOURCE AVAILABILITY
200              
201             This module is in Github:
202              
203             https://github.com/briandfoy/module-release-git
204              
205             =head1 AUTHOR
206              
207             brian d foy, <bdfoy@cpan.org>
208              
209             =head1 COPYRIGHT AND LICENSE
210              
211             Copyright © 2007-2021, brian d foy <bdfoy@cpan.org>. All rights reserved.
212              
213             You may redistribute this under the same terms as the Artistic License 2.0.
214              
215             =cut
216              
217             1;