File Coverage

blib/lib/Git/MoreHooks/GitRepoAdmin.pm
Criterion Covered Total %
statement 54 134 40.3
branch 4 20 20.0
condition 4 6 66.6
subroutine 14 18 77.7
pod 2 2 100.0
total 78 180 43.3


line stmt bran cond sub pod time code
1             package Git::MoreHooks::GitRepoAdmin;
2              
3             ## no critic (InputOutput::RequireCheckedSyscalls)
4             ## no critic (ValuesAndExpressions::ProhibitMagicNumbers)
5             ## no critic (ValuesAndExpressions::RequireInterpolationOfMetachars)
6             ## no critic (ControlStructures::ProhibitPostfixControls)
7              
8 2     2   445981 use strict;
  2         27  
  2         47  
9 2     2   8 use warnings;
  2         6  
  2         52  
10 2     2   9 use utf8;
  2         6  
  2         22  
11 2     2   48 use feature qw( say );
  2         3  
  2         266  
12              
13             # ABSTRACT: Integrate with .git-repo-admin
14              
15             our $VERSION = '0.016'; # VERSION: generated by DZP::OurPkgVersion
16              
17 2     2   932 use Git::Hooks 3.000000;
  2         70475  
  2         222  
18 2     2   783 use English qw( -no_match_vars );
  2         3954  
  2         9  
19 2     2   508 use Path::Tiny;
  2         4  
  2         65  
20 2     2   8 use Cwd;
  2         4  
  2         86  
21 2     2   9 use File::Spec;
  2         4  
  2         37  
22 2     2   510 use File::Temp qw/ tempfile tempdir /;
  2         7097  
  2         149  
23 2     2   11 use Log::Any qw{$log};
  2         4  
  2         14  
24              
25             my $PKG = __PACKAGE__;
26             my ($CFG) = __PACKAGE__ =~ /::([^:]+)$/msx;
27             $CFG = 'githooks.' . $CFG;
28              
29             #############
30             # Grok hook configuration, check it and set defaults.
31              
32             sub _setup_config {
33 2     2   8 my ($git) = @_;
34 2         11 $log->debugf( __PACKAGE__ . '::_setup_config(%s):', '$git' );
35              
36 2         205 my $config = $git->get_config();
37 2         27198 $log->tracef( __PACKAGE__ . '::_setup_config(): Current Git config:\n%s.', $config );
38              
39 2   50     73 $config->{ lc $CFG } //= {};
40              
41 2         6 my $default = $config->{ lc $CFG };
42 2   50     25 $default->{'ref'} //= [ '^refs/heads/main$', '^refs/heads/master$' ];
43              
44 2         12 return;
45             }
46              
47             sub _current_version {
48 6     6   2447 my ( $git, $dir ) = @_;
49 6         95 $log->debug( ( caller 0 )[3], '( ', $git, ', ', $dir, ')' );
50 6         549 my $ver_file_cnt = path( File::Spec->catdir( $dir, 'VERSION' ) )->slurp_utf8;
51 4         1234 $log->debug( ( caller 0 )[3], ': ver_file_cnt=', $ver_file_cnt );
52 4         278 my ($hooks_cfg_ver) = $ver_file_cnt =~ m/^([[:digit:]]+)$/msx;
53 4 100       17 return if !$hooks_cfg_ver;
54 2         14 return $hooks_cfg_ver;
55             }
56              
57             sub _new_version {
58 0     0   0 my ( $git, $ref, $dir ) = @_;
59 0         0 $log->debug( ( caller 0 )[3], '( ', $git, ', ', $ref, ', ', $dir, ')' );
60              
61 0         0 my $filepath = File::Spec->catdir( $dir, 'VERSION' );
62 0         0 my $object = "$ref:$filepath";
63 0         0 my $ver_file_cnt = $git->run(
64             'cat-file',
65             '-p', $object,
66             {
67             env => {
68              
69             # Eliminate the effects of system wide and global configuration.
70             GIT_CONFIG_NOSYSTEM => 1,
71             XDG_CONFIG_HOME => undef,
72             HOME => undef,
73             },
74             },
75             );
76              
77 0         0 $log->debug( ( caller 0 )[3], ': ver_file_cnt=', $ver_file_cnt );
78 0         0 my ($hooks_cfg_ver) = $ver_file_cnt =~ m/^([[:digit:]]+)$/msx;
79 0 0       0 return if !$hooks_cfg_ver;
80 0         0 return $hooks_cfg_ver;
81             }
82              
83             sub _ref_matches {
84 0     0   0 my ( $git, $our_refs, $branches ) = @_;
85 0         0 $log->debug( ( caller 0 )[3], '( ', ( join q{:}, @{$our_refs} ), ', ', ( join q{:}, @{$branches} ), ')' );
  0         0  
  0         0  
86              
87 0         0 my @matches;
88 0         0 foreach my $our_ref ( @{$our_refs} ) {
  0         0  
89 0         0 my @m = grep { m/$our_ref/msx } @{$branches};
  0         0  
  0         0  
90 0         0 push @matches, @m;
91             }
92              
93 0         0 return @matches;
94             }
95              
96             sub _update_server_side {
97 0     0   0 my ( $git, $ref, $dir ) = @_;
98 0         0 $log->debug( ( caller 0 )[3], '( ', $git, ', ', $ref, ', ', $dir, ')' );
99              
100 0         0 my $tmp = File::Temp->new();
101              
102             # Hook is always run with cdw as repo root dir.
103 0         0 my $filepath = File::Spec->catdir( $dir, 'initialize-server.sh' );
104 0         0 my $object = "$ref:$filepath";
105 0         0 my $exe_file_cnt = $git->run(
106             'cat-file',
107             '-p', $object,
108             {
109             env => {
110              
111             # Eliminate the effects of system wide and global configuration.
112             GIT_CONFIG_NOSYSTEM => 1,
113             XDG_CONFIG_HOME => undef,
114             HOME => undef,
115             },
116             },
117             );
118 0         0 $log->debug( ( caller 0 )[3], ': exe_file_cnt=', $exe_file_cnt );
119 0         0 print {$tmp} $exe_file_cnt;
  0         0  
120 0         0 my $tmp_filename = $tmp->filename;
121 0         0 $log->debug( ( caller 0 )[3], ': tmp_filename=', $tmp_filename );
122 0         0 local $OUTPUT_AUTOFLUSH = 1;
123 0         0 system 'bash', $tmp_filename, $ref;
124              
125 0         0 return;
126             }
127              
128             ##########
129              
130             sub check_affected_refs_client_side {
131 2     2 1 332393 my ( $git, $is_squash_merge ) = @_;
132 2   100     66 $log->debug( ( caller 0 )[3], '( ', $git, '( ', ( $is_squash_merge // 'undef' ), ')' );
133 2 100       283 $is_squash_merge = 0 if ( !defined $is_squash_merge );
134              
135 2         19 _setup_config($git);
136 2         6484 $log->debug( ( caller 0 )[3], ': cwd=', cwd );
137              
138             # Hook is always run with cdw as repo root dir.
139 2         298 my $curr_ver = _current_version( $git, '.git/.git-repo-admin' );
140 0           $log->debug( ( caller 0 )[3], ': curr_ver=', $curr_ver );
141              
142             # We cannot use $git->get_affected_refs() because post-merge hook
143             # does not get information about which branches are affected.
144             # Don't know why!!!
145             # So instead we just work with the only branch in the config.
146 0           my @our_refs = $git->get_config( $CFG => 'ref' );
147 0           $log->debug( ( caller 0 )[3], ': our_refs=', ( join q{:}, @our_refs ) );
148 0           my $branches_raw = $git->run(
149             'for-each-ref',
150             '--format',
151             '%(refname)',
152             {
153             env => {
154              
155             # Eliminate the effects of system wide and global configuration.
156             GIT_CONFIG_NOSYSTEM => 1,
157             XDG_CONFIG_HOME => undef,
158             HOME => undef,
159             },
160             },
161             );
162 0           my @branches = split qr{\n}msx, $branches_raw;
163 0           $log->debug( ( caller 0 )[3], ': branches=', ( join q{:}, @branches ) );
164              
165 0           my @matches = _ref_matches( $git, \@our_refs, \@branches );
166 0 0         if ( @matches > 1 ) {
    0          
167 0           $git->fault('Config variable \'ref\' matches with more than one branch.');
168 0           return scalar @matches;
169             }
170             elsif ( @matches == 0 ) {
171 0           $git->fault('Config variable \'ref\' does not match with any branch.');
172 0           return 1;
173             }
174              
175             # Okay, no errors in the config.
176             # We always proceed to read the VERSION from the hook config branch.
177 0           my $new_ver = _new_version( $git, $matches[0], '.git-repo-admin' );
178 0           $log->debug( ( caller 0 )[3], ': new_ver=', $new_ver );
179 0 0         if ( $new_ver > $curr_ver ) {
180 0           $log->debug( ( caller 0 )[3], 'Newer version detected. Update config.' );
181 0           say '********************************************************************************';
182 0           say '* UPDATE CLIEND SIDE HOOKS *';
183 0           say '* Run .git-repo-admin/install-client-hooks.sh *';
184 0           say '* ATTN. Switch to right branch first! *';
185 0           say '********************************************************************************';
186             }
187              
188 0           return 0;
189             }
190              
191             sub check_affected_refs_server_side {
192 0     0 1   my ($git) = @_;
193 0           $log->debug( ( caller 0 )[3], '( ', $git, ')' );
194              
195 0           _setup_config($git);
196 0           my $curr_ver = _current_version( $git, '.git-repo-admin' );
197 0           $log->debug( ( caller 0 )[3], ': curr_ver=', $curr_ver );
198              
199             # We're only interested in branches
200 0           my @refs = grep { m{^refs/heads/}msx } $git->get_affected_refs();
  0            
201 0 0         return 1 unless @refs;
202              
203 0           my @our_refs = $git->get_config( $CFG => 'ref' );
204 0           $log->debug( ( caller 0 )[3], ': our_refs=', ( join q{:}, @our_refs ) );
205              
206             # my ($r, $match) = _ref_matches( $git, \@our_refs, \@refs );
207 0           my @matches = _ref_matches( $git, \@our_refs, \@refs );
208 0           $log->debug( ( caller 0 )[3], ': matches=', ( join q{:}, @matches ) );
209 0 0         if ( @matches > 1 ) {
    0          
210 0           $git->fault('Config variable \'ref\' matches with more than one branch.');
211 0           $log->debug( ( caller 0 )[3], '(): ' . scalar @matches );
212 0           return scalar @matches;
213             }
214             elsif ( @matches == 1 ) {
215 0           my $new_ver = _new_version( $git, $matches[0], '.git-repo-admin' );
216 0           $log->debug( ( caller 0 )[3], ': new_ver=', $new_ver );
217 0 0         if ( $new_ver > $curr_ver ) {
218 0           $log->debug( ( caller 0 )[3], 'Newer version detected. Update config.' );
219 0           _update_server_side( $git, $matches[0], '.git-repo-admin' );
220             }
221 0           $log->debug( ( caller 0 )[3], '(): 0' );
222 0           return 0;
223             }
224              
225             # On server side we get the affected refs via the hook, and the main/master
226             # doesn't need to be one of them, if it wasn't updated.
227              
228             }
229              
230             # Install hooks
231             POST_MERGE \&check_affected_refs_client_side;
232             POST_RECEIVE \&check_affected_refs_server_side;
233              
234             1;
235              
236             __END__