File Coverage

blib/lib/App/GitHooks/Hook/PreCommit.pm
Criterion Covered Total %
statement 24 55 43.6
branch 0 22 4.5
condition 0 3 0.0
subroutine 8 10 80.0
pod 2 2 100.0
total 34 92 38.0


line stmt bran cond sub pod time code
1             package App::GitHooks::Hook::PreCommit;
2              
3 1     1   18352 use strict;
  1         2  
  1         34  
4 1     1   3 use warnings;
  1         2  
  1         24  
5              
6             # Inherit from the base Hook class.
7 1     1   3 use base 'App::GitHooks::Hook';
  1         2  
  1         351  
8              
9             # External dependencies.
10 1     1   7 use Carp;
  1         1  
  1         44  
11 1     1   645 use Data::Dumper;
  1         7222  
  1         62  
12 1     1   491 use Data::Validate::Type;
  1         6227  
  1         50  
13 1     1   795 use Path::Tiny qw();
  1         11001  
  1         33  
14              
15             # Internal dependencies.
16 1     1   6 use App::GitHooks::Constants qw( :HOOK_EXIT_CODES :PLUGIN_RETURN_CODES );
  1         2  
  1         470  
17              
18              
19             =head1 NAME
20              
21             App::GitHooks::Hook::PreCommit - Handler for pre-commit hook.
22              
23              
24             =head1 VERSION
25              
26             Version 1.7.3
27              
28             =cut
29              
30             our $VERSION = '1.7.3';
31              
32              
33             =head1 METHODS
34              
35             =head2 run()
36              
37             Run the hook handler and return an exit status to pass to git.
38              
39             my $exit_status = App::GitHooks::Hook::PreCommit->run(
40             app => $app,
41             );
42              
43             Arguments:
44              
45             =over 4
46              
47             =item * app I<(mandatory)>
48              
49             An App::GitHooks object.
50              
51             =back
52              
53             =cut
54              
55             sub run
56             {
57 0     0 1   my ( $class, %args ) = @_;
58 0           my $app = delete( $args{'app'} );
59 0 0         croak 'Unknown argument(s): ' . join( ', ', keys %args )
60             if scalar( keys %args ) != 0;
61              
62             # Check parameters.
63 0 0         croak "The 'app' argument is mandatory"
64             if !Data::Validate::Type::is_instance( $app, class => 'App::GitHooks' );
65              
66             # Remove the file that we use to indicate that pre-commit checks have been
67             # run for the set of staged files, in case it exists.
68 0           unlink( '.git/COMMIT-MSG-CHECKS' );
69              
70             # If the terminal isn't interactive, we won't have a human available to fix the
71             # problems so we just let the commit go as is.
72 0           my $config = $app->get_config();
73 0           my $force_interactive = $config->get( 'testing', 'force_interactive' );
74 0 0 0       return $HOOK_EXIT_SUCCESS
75             if !$app->get_terminal()->is_interactive() && !$force_interactive;
76              
77             # Run the checks on the staged changes.
78 0           my $checks_pass = run_all_tests( $app );
79              
80             # If the checks passed, write a file for the prepare-commit-msg hook to know
81             # that we've already run the checks and there's no need to do it a second time.
82             # This is what allows it to detect when --no-verify was used.
83 0 0         if ( $checks_pass )
84             {
85 0           Path::Tiny::path( '.git', 'COMMIT-MSG-CHECKS' )
86             ->spew( $checks_pass );
87             }
88              
89             # Indicate if we should allow continuing to the commit message or not.
90 0 0         return $checks_pass
91             ? $HOOK_EXIT_SUCCESS
92             : $HOOK_EXIT_FAILURE;
93             }
94              
95              
96             =head2 run_all_tests()
97              
98             Run all the tests available for the pre-commit hook and return whether issues
99             were detected.
100              
101             my $tests_success = run_all_tests( $app );
102              
103             This is a two step operation:
104              
105             =over 4
106              
107             =item 1. We load all the plugins that support "pre-commit", and run them to
108             analyze the overall pre-commit operation.
109              
110             =item 2. Each staged file is loaded and we run plugins that support
111             "pre-commit-file" on each one.
112              
113             =back
114              
115             =cut
116              
117             sub run_all_tests
118             {
119 0     0 1   my ( $app ) = @_;
120              
121             # Find all the plugins that support the pre-commit hook.
122 0           my $plugins = $app->get_hook_plugins( 'pre-commit' );
123              
124             # Run the plugins.
125 0           my $tests_success = 1;
126 0           my $has_warnings = 0;
127 0           foreach my $plugin ( @$plugins )
128             {
129 0           my $check_result = $plugin->run_pre_commit(
130             app => $app,
131             );
132              
133 0 0         $tests_success = 0
134             if $check_result == $PLUGIN_RETURN_FAILED;
135              
136 0 0         $has_warnings = 1
137             if $check_result == $PLUGIN_RETURN_WARNED;
138             }
139              
140             # Check the changed files individually with plugins that support
141             # "pre-commit-file".
142             {
143 0           my ( $file_checks, $file_warnings ) = $app
  0            
144             ->get_staged_changes()
145             ->verify();
146 0 0         $tests_success = 0
147             if !$file_checks;
148 0 0         $has_warnings = 1
149             if $file_warnings;
150             }
151              
152             # If warnings were found, notify users.
153 0 0         if ( $has_warnings )
154             {
155             # If we have a user, stop and ask if we should continue with the commit.
156             # uncoverable branch true
157 0 0         if ( $app->get_terminal()->is_interactive() )
158             {
159 0           print "Some warnings were found. Press to continue committing or Ctrl-C to abort the commit.\n";
160 0           my $input = ; ## no critic (InputOutput::ProhibitExplicitStdin)
161 0           print "\n";
162             }
163             # If we don't have a user, just warn and continue.
164             else
165             {
166 0           print "Some warnings were found, please review.\n";
167             }
168             }
169              
170 0           return $tests_success;
171             }
172              
173              
174             =head1 BUGS
175              
176             Please report any bugs or feature requests through the web interface at
177             L.
178             I will be notified, and then you'll automatically be notified of progress on
179             your bug as I make changes.
180              
181              
182             =head1 SUPPORT
183              
184             You can find documentation for this module with the perldoc command.
185              
186             perldoc App::GitHooks::Hook::PreCommit
187              
188              
189             You can also look for information at:
190              
191             =over
192              
193             =item * GitHub's request tracker
194              
195             L
196              
197             =item * AnnoCPAN: Annotated CPAN documentation
198              
199             L
200              
201             =item * CPAN Ratings
202              
203             L
204              
205             =item * MetaCPAN
206              
207             L
208              
209             =back
210              
211              
212             =head1 AUTHOR
213              
214             L,
215             C<< >>.
216              
217              
218             =head1 COPYRIGHT & LICENSE
219              
220             Copyright 2013-2015 Guillaume Aubert.
221              
222             This program is free software: you can redistribute it and/or modify it under
223             the terms of the GNU General Public License version 3 as published by the Free
224             Software Foundation.
225              
226             This program is distributed in the hope that it will be useful, but WITHOUT ANY
227             WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
228             PARTICULAR PURPOSE. See the GNU General Public License for more details.
229              
230             You should have received a copy of the GNU General Public License along with
231             this program. If not, see http://www.gnu.org/licenses/
232              
233             =cut
234              
235             1;