File Coverage

blib/lib/App/GitHooks/Plugin/PerlCritic.pm
Criterion Covered Total %
statement 41 45 91.1
branch 7 12 58.3
condition n/a
subroutine 11 12 100.0
pod 3 3 100.0
total 62 72 87.5


line stmt bran cond sub pod time code
1             package App::GitHooks::Plugin::PerlCritic;
2              
3 8     8   1241729 use strict;
  8         15  
  8         223  
4 8     8   34 use warnings;
  8         8  
  8         237  
5              
6 8     8   33 use base 'App::GitHooks::Plugin';
  8         13  
  8         2345  
7              
8             # External dependencies.
9 8     8   6204 use Perl::Critic;
  8         6083740  
  8         427  
10 8     8   4143 use Perl::Critic::Git;
  8         167076  
  8         232  
11 8     8   52 use Try::Tiny;
  8         13  
  8         465  
12              
13             # Internal dependencies.
14 8     8   1306 use App::GitHooks::Constants qw( :PLUGIN_RETURN_CODES );
  8         1092  
  8         3666  
15              
16              
17             =head1 NAME
18              
19             App::GitHooks::Plugin::PerlCritic - Verify that all changes and addition to the Perl files pass PerlCritic checks.
20              
21              
22             =head1 DESCRIPTION
23              
24             PerlCritic is a static source code analysis tool. This plugin calls PerlCritic
25             and verifies that the staged modifications do not trigger any violations.
26              
27             Note that if you stage half of a file for committing, this plugin will
28             correctly only check for code violations in the staged half.
29              
30              
31             =head1 VERSION
32              
33             Version 1.1.0
34              
35             =cut
36              
37             our $VERSION = '1.1.0';
38              
39             # Tweak the format of the violations reported by PerlCritic.
40             # Note: the PerlCritic package unfortunately doesn't honor .perlcriticrc,
41             # unlike the perlcritic executable.
42             Perl::Critic::Violation::set_format( "%m at line %l column %c. %e. (Severity: %s, %p)\n" );
43              
44              
45             =head1 METHODS
46              
47             =head2 get_file_pattern()
48              
49             Return a pattern to filter the files this plugin should analyze.
50              
51             my $file_pattern = App::GitHooks::Plugin::PerlCritic->get_file_pattern(
52             app => $app,
53             );
54              
55             =cut
56              
57             sub get_file_pattern
58             {
59 6     6 1 676776 return qr/\.(?:pl|pm|t|cgi)$/x;
60             }
61              
62              
63             =head2 get_file_check_description()
64              
65             Return a description of the check performed on files by the plugin and that
66             will be displayed to the user, if applicable, along with an indication of the
67             success or failure of the plugin.
68              
69             my $description = App::GitHooks::Plugin::PerlCritic->get_file_check_description();
70              
71             =cut
72              
73             sub get_file_check_description
74             {
75 5     5 1 5272 return "The file passes Perl::Critic's review.";
76             }
77              
78              
79             =head2 run_pre_commit_file()
80              
81             Code to execute for each file as part of the pre-commit hook.
82              
83             my $success = App::GitHooks::Plugin::PerlCritic->run_pre_commit_file();
84              
85             =cut
86              
87             sub run_pre_commit_file
88             {
89 2     2 1 9018 my ( $class, %args ) = @_;
90 2         235 my $file = delete( $args{'file'} );
91 2         18 my $git_action = delete( $args{'git_action'} );
92 2         12 my $app = delete( $args{'app'} );
93 2         88 my $staged_changes = $app->get_staged_changes();
94 2         63 my $repository = $app->get_repository();
95              
96             # Ignore deleted files.
97 2 50       57 return $PLUGIN_RETURN_SKIPPED
98             if $git_action eq 'D';
99              
100             # Ignore merges, since they correspond mostly to code written by other people.
101 2 50       55 return $PLUGIN_RETURN_SKIPPED
102             if $staged_changes->is_merge();
103              
104             # Ignore revert commits.
105 2 50       43 return $PLUGIN_RETURN_SKIPPED
106             if $staged_changes->is_revert();
107              
108             # Get PerlCritic violations for uncommitted files only.
109 2         38 my $error = undef;
110             my $violations = try
111             {
112 2 50   2   166 if ( $git_action eq 'A' )
113             {
114 2         85 my $critic = Perl::Critic->new();
115 2         834005 return [ $critic->critique( $file ) ];
116             }
117             else
118             {
119 0         0 my $git_critic = Perl::Critic::Git->new(
120             file => $repository->work_tree() . '/' . $file,
121             );
122              
123 0         0 return $git_critic->report_violations(
124             author => 'not.committed.yet',
125             );
126             }
127             }
128             catch
129             {
130             # Unless PPI dies, we should never get into this catch() part.
131             # uncoverable subroutine
132 0     0   0 $error = $_;
133 0         0 return [];
134 2         72 };
135              
136 2 50       12500 die "Failed to run PerlCritic: $error.\n"
137             if defined( $error );
138              
139 2 100       14 die "Violations found:\n" . join( '', map { $_->to_string() } @$violations ) . "\n"
  1         6  
140             if scalar( @$violations ) != 0;
141              
142 1         5 return $PLUGIN_RETURN_PASSED;
143             }
144              
145              
146             =head1 BUGS
147              
148             Please report any bugs or feature requests through the web interface at
149             L<https://github.com/guillaumeaubert/App-GitHooks-Plugin-PerlCritic/issues/new>.
150             I will be notified, and then you'll automatically be notified of progress on
151             your bug as I make changes.
152              
153              
154             =head1 SUPPORT
155              
156             You can find documentation for this module with the perldoc command.
157              
158             perldoc App::GitHooks::Plugin::PerlCritic
159              
160              
161             You can also look for information at:
162              
163             =over
164              
165             =item * GitHub's request tracker
166              
167             L<https://github.com/guillaumeaubert/App-GitHooks-Plugin-PerlCritic/issues>
168              
169             =item * AnnoCPAN: Annotated CPAN documentation
170              
171             L<http://annocpan.org/dist/app-githooks-plugin-perlcritic>
172              
173             =item * CPAN Ratings
174              
175             L<http://cpanratings.perl.org/d/app-githooks-plugin-perlcritic>
176              
177             =item * MetaCPAN
178              
179             L<https://metacpan.org/release/App-GitHooks-Plugin-PerlCritic>
180              
181             =back
182              
183              
184             =head1 AUTHOR
185              
186             L<Guillaume Aubert|https://metacpan.org/author/AUBERTG>,
187             C<< <aubertg at cpan.org> >>.
188              
189              
190             =head1 COPYRIGHT & LICENSE
191              
192             Copyright 2013-2017 Guillaume Aubert.
193              
194             This code is free software; you can redistribute it and/or modify it under the
195             same terms as Perl 5 itself.
196              
197             This program is distributed in the hope that it will be useful, but WITHOUT ANY
198             WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
199             PARTICULAR PURPOSE. See the LICENSE file for more details.
200              
201             =cut
202              
203             1;