File Coverage

blib/lib/App/GitHooks/Plugin/ForceRegularUpdate.pm
Criterion Covered Total %
statement 49 52 94.2
branch 12 18 66.6
condition 7 14 50.0
subroutine 7 7 100.0
pod 1 1 100.0
total 76 92 82.6


line stmt bran cond sub pod time code
1             package App::GitHooks::Plugin::ForceRegularUpdate;
2              
3 5     5   1612566 use strict;
  5         14  
  5         180  
4 5     5   26 use warnings;
  5         10  
  5         144  
5              
6 5     5   162 use base 'App::GitHooks::Plugin';
  5         13  
  5         1726  
7              
8             # External dependencies.
9 5     5   589 use Carp;
  5         10  
  5         298  
10 5     5   1621 use File::Slurp ();
  5         25100  
  5         168  
11              
12             # Internal dependencies.
13 5     5   7054 use App::GitHooks::Constants qw( :PLUGIN_RETURN_CODES );
  5         439  
  5         4545  
14              
15              
16             =head1 NAME
17              
18             App::GitHooks::Plugin::ForceRegularUpdate - Force running a specific tool at regular intervals.
19              
20              
21             =head1 DESCRIPTION
22              
23             # TODO: description of how to write a tool that generates the timestamp file.
24              
25              
26             =head1 VERSION
27              
28             Version 1.0.3
29              
30             =cut
31              
32             our $VERSION = '1.0.3';
33              
34              
35             =head1 CONFIGURATION OPTIONS
36              
37             This plugin supports the following options in the C<[BlockProductionCommits]>
38             section of your C<.githooksrc> file.
39              
40             [BlockProductionCommits]
41             max_update_age = 2 * 24 * 3600 # 2 days
42             description = ./my_updater.sh
43             env_variable = my_environment
44             env_safe_regex = /^development$/
45             update_file = /var/local/.last_update.txt
46              
47             =head2 max_update_age
48              
49             This indicates the maximum amount of time that may have elapsed since the last
50             update, before commits are blocked.
51              
52             max_update_age = 2 * 24 * 3600 # 2 days
53              
54             Note that this configuration option supports comments at the end, for
55             readability.
56              
57              
58             =head2 description
59              
60             The name of the tool to run to perform an update that will reset the time
61             counter.
62              
63             description = ./my_updater.sh
64              
65              
66             =head2 env_variable
67              
68             Optional, the name of the environment variable to use to determine the
69             environment (production, staging, development, etc).
70              
71             env_variable = my_environment
72              
73              
74             =head2 env_regex
75              
76             Optional, but required if C is used.
77              
78             A regular expression that indicates that this plugin should be run when it is
79             matched.
80              
81             env_safe_regex = /^development$/
82              
83             The example above only checks for regular updates when
84             C<$ENV{'my_environment'} =~ /^development$/>.
85              
86              
87             =head2 update_file
88              
89             The path to the file that stores the unix time of the last upgrade. This is the
90             file your update tool should write the current unix time to upon successful
91             completion.
92              
93             update_file = /var/local/.last_update.txt
94              
95             Note that you can use an absolute path, or a relative path. If relative, the
96             path will be relative to the root of the current git repository.
97              
98              
99             =head1 METHODS
100              
101             =head2 run_pre_commit()
102              
103             Code to execute as part of the pre-commit hook.
104              
105             my $success = App::GitHooks::Plugin::ForceRegularUpdate->run_pre_commit();
106              
107             =cut
108              
109             sub run_pre_commit
110             {
111 4     4 1 31014 my ( $class, %args ) = @_;
112 4         16 my $app = delete( $args{'app'} );
113 4         26 my $repository = $app->get_repository();
114 4         686045 my $config = $app->get_config();
115              
116             # Verify we have the max update age configured.
117 4         140 my $max_update_age = $config->get( 'ForceRegularUpdate', 'max_update_age' );
118 4 50       123 croak "'max_update_age' must be defined in the [ForceRegularUpdate] section of your .githooksrc file"
119             if !defined $max_update_age;
120 4         45 $max_update_age =~ s/^\s+//;
121 4         252 $max_update_age =~ s/\s*(?:#.*)$//;
122 4 50       46 croak "'max_update_age' in the [ForceRegularUpdate] section must be an integer expressing seconds"
123             if $max_update_age !~ /^\d+$/;
124              
125             # Verify we have a description.
126 4         25 my $description = $config->get( 'ForceRegularUpdate', 'description' );
127 4 50 33     196 croak "'description' must be defined in the [ForceRegularUpdate] section of your .githooksrc file"
128             if !defined( $description ) || ( $description !~ /\w/ );
129              
130             # Check if we have environment restrictions.
131 4         23 my $env_variable = $config->get( 'ForceRegularUpdate', 'env_variable' );
132 4         80 my $env_regex = $config->get_regex( 'ForceRegularUpdate', 'env_regex' );
133 4 50       211 if ( defined( $env_variable ) )
134             {
135 4 50       25 croak "You defined an environment variable to check against, but not a regex to use, in the [ForceRegularUpdate] section"
136             if !defined( $env_regex );
137              
138 4 100 50     165 return $PLUGIN_RETURN_SKIPPED
139             if ( $ENV{ $env_variable } // '' ) !~ $env_regex;
140             }
141              
142             # Retrieve the file that specifies the time of last update.
143 3         16 my $update_file = $config->get( 'ForceRegularUpdate', 'update_file' );
144 3 50       55 croak "'update_file' must be defined in the [ForceRegularUpdate] section of your .githooksrc file"
145             if !defined( $update_file );
146 3         10 $update_file =~ s/\$ENV{'([^']+)'}/$ENV{$1}/xeg;
  0         0  
147 3         9 $update_file =~ s/\$ENV{"([^"]+)"}/$ENV{$1}/xeg;
  0         0  
148 3         9 $update_file =~ s/\$ENV{([^\}]+)}/$ENV{$1}/xeg;
  0         0  
149              
150             # Check if the update was ever performed.
151 3         18 my $failure_character = $app->get_failure_character();
152 3 100       191 if ( ! -e $update_file )
153             {
154 1         10 print $app->wrap(
155             $app->color( 'red', "$failure_character It appears that you have never performed $description on this machine - please do that before committing.\n" ),
156             "",
157             );
158 1         86 return $PLUGIN_RETURN_FAILED;
159             }
160              
161             # Retrieve the value of the file and check whether it's within the bounds
162             # allowed.
163 2         36 my $last_update = File::Slurp::read_file( $update_file );
164 2         221 chomp( $last_update );
165 2 100 33     64 if ( !defined( $last_update ) # Invalid format.
      66        
      66        
166             || ( $last_update !~ /^\d+$/ ) # Invalid format.
167             || ( $last_update < time() - $max_update_age ) # Not updated in a long time.
168             || ( $last_update > time() + 10 ) # Nice try setting the timestamp in the future to not have to update.
169             )
170             {
171 1         10 print $app->wrap(
172             $app->color(
173             'red',
174             "$failure_character It appears that you haven't performed $description on this machine for a long time. Please do that and try to commit again.\n"
175             ),
176             "",
177             );
178 1         75 return $PLUGIN_RETURN_FAILED;
179             }
180              
181 1         20 return $PLUGIN_RETURN_PASSED;
182             }
183              
184              
185             =head1 BUGS
186              
187             Please report any bugs or feature requests through the web interface at
188             L.
189             I will be notified, and then you'll automatically be notified of progress on
190             your bug as I make changes.
191              
192              
193             =head1 SUPPORT
194              
195             You can find documentation for this module with the perldoc command.
196              
197             perldoc App::GitHooks::Plugin::ForceRegularUpdate
198              
199              
200             You can also look for information at:
201              
202             =over
203              
204             =item * GitHub's request tracker
205              
206             L
207              
208             =item * AnnoCPAN: Annotated CPAN documentation
209              
210             L
211              
212             =item * CPAN Ratings
213              
214             L
215              
216             =item * MetaCPAN
217              
218             L
219              
220             =back
221              
222              
223             =head1 AUTHOR
224              
225             L,
226             C<< >>.
227              
228              
229             =head1 COPYRIGHT & LICENSE
230              
231             Copyright 2013-2014 Guillaume Aubert.
232              
233             This program is free software: you can redistribute it and/or modify it under
234             the terms of the GNU General Public License version 3 as published by the Free
235             Software Foundation.
236              
237             This program is distributed in the hope that it will be useful, but WITHOUT ANY
238             WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
239             PARTICULAR PURPOSE. See the GNU General Public License for more details.
240              
241             You should have received a copy of the GNU General Public License along with
242             this program. If not, see http://www.gnu.org/licenses/
243              
244             =cut
245              
246             1;