File Coverage

blib/lib/App/GitHooks/Utils.pm
Criterion Covered Total %
statement 53 54 98.1
branch 12 16 75.0
condition 10 16 62.5
subroutine 10 11 90.9
pod 4 4 100.0
total 89 101 88.1


line stmt bran cond sub pod time code
1             package App::GitHooks::Utils;
2              
3 20     20   16104 use strict;
  20         19  
  20         452  
4 20     20   59 use warnings;
  20         22  
  20         364  
5              
6             # External dependencies.
7 20     20   57 use Carp;
  20         19  
  20         798  
8 20     20   62 use File::Spec;
  20         149  
  20         282  
9 20     20   4631 use Try::Tiny;
  20         10562  
  20         10914  
10              
11             # No internal dependencies - keep this as a leaf module in the graph so that we
12             # can include it everywhere.
13              
14              
15             =head1 NAME
16              
17             App::GitHooks::Utils - Support functions for App::GitHooks and its plugins.
18              
19              
20             =head1 VERSION
21              
22             Version 1.8.0
23              
24             =cut
25              
26             our $VERSION = '1.8.0';
27              
28              
29             =head1 FUNCTIONS
30              
31             =head2 get_project_prefixes()
32              
33             Get an arrayref of valid project prefixes.
34              
35             my $project_prefixes = App::GitHooks::Utils::get_project_prefixes( $app );
36              
37             Arguments:
38              
39             =over 4
40              
41             =item * $app
42              
43             An C instance.
44              
45             =back
46              
47             =cut
48              
49             sub get_project_prefixes
50             {
51 13     13 1 110 my ( $app ) = @_;
52 13   100     44 my $config_line = $app->get_config()->{'_'}->{'project_prefixes'} // '';
53              
54             # Strip leading/trailing whitespace.
55 13         44 $config_line =~ s/(?:^\s+|\s+$)//g;
56              
57 13         63 return [ split( /\s*[, ]\s*/, $config_line ) ];
58             }
59              
60              
61             =head2 get_project_prefix_regex()
62              
63             Return a non-capturing regex that will match all the valid project prefixes.
64              
65             my $project_prefix_regex = App::GitHooks::Utils::get_project_prefix_regex( $app );
66              
67             Arguments:
68              
69             =over 4
70              
71             =item * $app
72              
73             An C instance.
74              
75             =back
76              
77             =cut
78              
79             sub get_project_prefix_regex
80             {
81 9     9 1 128 my ( $app ) = @_;
82              
83 9         24 my $prefixes = get_project_prefixes( $app );
84              
85 9 100       37 if ( scalar( @$prefixes ) == 0 )
    100          
86             {
87 3         11 return '';
88             }
89             elsif ( scalar( @$prefixes ) == 1 )
90             {
91 1         7 return $prefixes->[0];
92             }
93             else
94             {
95 5         26 return '(?:' . join( '|', @$prefixes ) . ')';
96             }
97             }
98              
99              
100             =head2 get_ticket_id_from_commit_regex()
101              
102             Return a regex that will extract a ticket ID from a commit message, if it
103             exists.
104              
105             my $ticket_id_regex = App::GitHooks::Utils::get_ticket_id_from_commit_regex( $app );
106              
107             Arguments:
108              
109             =over 4
110              
111             =item * $app
112              
113             An C instance.
114              
115             =back
116              
117             =cut
118              
119             sub get_ticket_id_from_commit_regex
120             {
121 2     2 1 46 my ( $app ) = @_;
122 2         4 my $config = $app->get_config();
123              
124             # Retrieve the regular expression from the config or use a default.
125 2   100     4 my $ticket_regex = $app->get_config()->get_regex( '_', 'extract_ticket_id_from_commit' )
126             // '^($project_prefixes-\d+|--)\: ?';
127              
128             # Replace the list of project prefixes if it is mentioned in the regex.
129 2         7 my $project_prefix_regex = get_project_prefix_regex( $app );
130 2         7 $ticket_regex =~ s/\$project_prefixes/$project_prefix_regex/g;
131              
132 2         7 return $ticket_regex;
133             }
134              
135              
136             =head2 get_ticket_id_from_branch_name()
137              
138             Return the ticket ID derived from the name of the current branch for this
139             repository.
140              
141             my $ticket_id = App::GitHooks::Utils::get_ticket_id_from_branch_name( $app );
142              
143             Arguments:
144              
145             =over 4
146              
147             =item * $app
148              
149             An C instance.
150              
151             =back
152              
153             =cut
154              
155             sub get_ticket_id_from_branch_name
156             {
157 4     4 1 108 my ( $app ) = @_;
158 4         19 my $repository = $app->get_repository();
159 4         23 my $config = $app->get_config();
160              
161             # If the config doesn't specify a way to extract the ticket ID from the
162             # branch, there's nothing we can do here.
163 4         31 my $ticket_regex = $config->get_regex( '_', 'extract_ticket_id_from_branch' );
164             return undef
165 4 100       31 if !defined( $ticket_regex );
166              
167             # Check if we're in a rebase. During a rebase (regardless of whether it's
168             # interractive or not), the HEAD goes in a detached state, and we won't be
169             # able to call symbolic-ref on it to get a branch name.
170 3         16 my $git_directory = $repository->git_dir();
171             return undef
172 3 50 33     158 if ( -d File::Spec->catfile( $git_directory, 'rebase-merge' ) ) # detect rebase -i
173             || ( -d File::Spec->catfile( $git_directory, 'rebase-apply' ) ); # detect rebase
174              
175 3         7 my $ticket_id;
176             try
177             {
178              
179             # Retrieve the branch name.
180 3     3   125 my $branch_name = $repository->run('symbolic-ref', 'HEAD');
181 3         18578 my ( $branch_name_without_prefixes ) = $branch_name =~ /([^\/]+)$/;
182              
183             # Extract the ticket ID from the branch name.
184 3         18 my $project_prefix_regex = get_project_prefix_regex( $app );
185 3         9 $ticket_regex =~ s/\$project_prefixes/$project_prefix_regex/g;
186 3         71 ( $ticket_id ) = $branch_name_without_prefixes =~ /$ticket_regex/i;
187              
188 3         18 my $normalize = $config->get( '_', 'normalize_branch_ticket_id' );
189 3 100 66     28 if ( defined( $ticket_id ) && defined( $normalize ) && ( $normalize =~ /\S/ ) )
      66        
190             {
191 1         8 my ( $match, $replacement ) = $normalize =~ m|^\s*s/(.*?)(?
192 1 50 33     10 croak "Invalid format for 'normalize_branch_ticket_id' in configuration file."
193             if !defined( $match ) || !defined( $replacement );
194 1 50       4 croak "Unsafe matching pattern in 'normalize_branch_ticket_id', escape your slashes"
195             if $match =~ /(?
196 1 50       3 croak "Unsafe replacement pattern in 'normalize_branch_ticket_id', escape your slashes"
197             if $replacement =~ /(?
198 1         113 eval( "\$ticket_id =~ s/$match/$replacement/i" ); ## no critic (BuiltinFunctions::ProhibitStringyEval, ErrorHandling::RequireCheckingReturnValueOfEval)
199             }
200             }
201             catch
202             {
203 0     0   0 carp "ERROR: $_";
204 3         52 };
205              
206 3         109 return $ticket_id;
207             }
208              
209              
210             =head1 BUGS
211              
212             Please report any bugs or feature requests through the web interface at
213             L.
214             I will be notified, and then you'll automatically be notified of progress on
215             your bug as I make changes.
216              
217              
218             =head1 SUPPORT
219              
220             You can find documentation for this module with the perldoc command.
221              
222             perldoc App::GitHooks::Utils
223              
224              
225             You can also look for information at:
226              
227             =over
228              
229             =item * GitHub's request tracker
230              
231             L
232              
233             =item * AnnoCPAN: Annotated CPAN documentation
234              
235             L
236              
237             =item * CPAN Ratings
238              
239             L
240              
241             =item * MetaCPAN
242              
243             L
244              
245             =back
246              
247              
248             =head1 AUTHOR
249              
250             L,
251             C<< >>.
252              
253              
254             =head1 COPYRIGHT & LICENSE
255              
256             Copyright 2013-2016 Guillaume Aubert.
257              
258             This code is free software; you can redistribute it and/or modify it under the
259             same terms as Perl 5 itself.
260              
261             This program is distributed in the hope that it will be useful, but WITHOUT ANY
262             WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
263             PARTICULAR PURPOSE. See the LICENSE file for more details.
264              
265             =cut
266              
267             1;