File Coverage

blib/lib/SVN/Hooks.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package SVN::Hooks;
2             # ABSTRACT: Framework for implementing Subversion hooks
3             $SVN::Hooks::VERSION = '1.34';
4 1     1   6 use strict;
  1         2  
  1         39  
5 1     1   6 use warnings;
  1         2  
  1         43  
6              
7 1     1   7 use File::Basename;
  1         2  
  1         117  
8 1     1   911 use File::Spec::Functions;
  1         1461  
  1         120  
9 1     1   8 use Data::Util qw(:check);
  1         1  
  1         209  
10 1     1   3642 use SVN::Look;
  0            
  0            
11              
12             use Exporter qw/import/;
13              
14             our @EXPORT = qw/run_hook POST_COMMIT POST_LOCK POST_REVPROP_CHANGE
15             POST_UNLOCK PRE_COMMIT PRE_LOCK PRE_REVPROP_CHANGE
16             PRE_UNLOCK START_COMMIT/;
17              
18             our @Conf_Files = (catfile('conf', 'svn-hooks.conf'));
19             our $Repo = undef;
20             our %Hooks = ();
21              
22             sub run_hook {
23             my ($hook_name, $repo_path, @args) = @_;
24              
25             $hook_name = basename $hook_name;
26              
27             -d $repo_path or die "not a directory ($repo_path): $_\n";
28              
29             $Repo = $repo_path;
30              
31             # Allow all hooks assume they execute on the repository's root directory
32             chdir $repo_path or die "cannot chdir to $repo_path: $!\n";
33              
34             # Reload all configuration files
35             foreach my $conf (@Conf_Files) {
36             my $conffile = file_name_is_absolute($conf) ? $conf : catfile($Repo, $conf);
37             next unless -e $conffile; # Configuration files are optional
38              
39             # The configuration file must be evaluated in the main:: namespace
40             package main;
41             $main::VERSION = '1.34';
42             unless (my $return = do $conffile) {
43             die "couldn't parse '$conffile': $@\n" if $@;
44             die "couldn't do '$conffile': $!\n" unless defined $return;
45             die "couldn't run '$conffile'\n" unless $return;
46             }
47             }
48              
49             # Substitute a SVN::Look object for the first argument
50             # in the hooks where this makes sense.
51             if ($hook_name eq 'pre-commit') {
52             # The next arg is a transaction number
53             $repo_path = SVN::Look->new($repo_path, '-t' => $args[0]);
54             } elsif ($hook_name =~ /^(?:post-commit|(?:pre|post)-revprop-change)$/) {
55             # The next arg is a revision number
56             $repo_path = SVN::Look->new($repo_path, '-r' => $args[0]);
57             }
58              
59             foreach my $hook (@{$Hooks{$hook_name}{list}}) {
60             if (is_code_ref($hook)) {
61             $hook->($repo_path, @args);
62             } elsif (is_array_ref($hook)) {
63             foreach my $h (@$hook) {
64             $h->($repo_path, @args);
65             }
66             } else {
67             die "SVN::Hooks: internal error!\n";
68             }
69             }
70              
71             return;
72             }
73              
74             ## no critic (Subroutines::ProhibitSubroutinePrototypes)
75              
76             # post-commit(SVN::Look, revision, txn-name)
77              
78             sub POST_COMMIT (&) {
79             my ($hook) = @_;
80             unless (exists $Hooks{'post-commit'}{set}{$hook}) {
81             push @{$Hooks{'post-commit'}{list}},
82             ($Hooks{'post-commit'}{set}{$hook} = sub { $hook->(@_); });
83             }
84             return;
85             }
86              
87             # post-lock(repos-path, username)
88              
89             sub POST_LOCK (&) {
90             my ($hook) = @_;
91             unless (exists $Hooks{'post-lock'}{set}{$hook}) {
92             push @{$Hooks{'post-lock'}{list}},
93             ($Hooks{'post-lock'}{set}{$hook} = sub { $hook->(@_); });
94             }
95             return;
96             }
97              
98             # post-revprop-change(SVN::Look, revision, username, property-name, action)
99              
100             sub POST_REVPROP_CHANGE (&) {
101             my ($hook) = @_;
102             unless (exists $Hooks{'post-revprop-change'}{set}{$hook}) {
103             push @{$Hooks{'post-revprop-change'}{list}},
104             ($Hooks{'post-revprop-change'}{set}{$hook} = sub { $hook->(@_); });
105             }
106             return;
107             }
108              
109             # post-unlock(repos-path, username)
110              
111             sub POST_UNLOCK (&) {
112             my ($hook) = @_;
113             unless (exists $Hooks{'post-unlock'}{set}{$hook}) {
114             push @{$Hooks{'post-unlock'}{list}},
115             ($Hooks{'post-unlock'}{set}{$hook} = sub { $hook->(@_); });
116             }
117             return;
118             }
119              
120             # pre-commit(SVN::Look, txn-name)
121              
122             sub PRE_COMMIT (&) {
123             my ($hook) = @_;
124             unless (exists $Hooks{'pre-commit'}{set}{$hook}) {
125             push @{$Hooks{'pre-commit'}{list}},
126             ($Hooks{'pre-commit'}{set}{$hook} = sub { $hook->(@_); });
127             }
128             return;
129             }
130              
131             # pre-lock(repos-path, path, username, comment, steal-lock-flag)
132              
133             sub PRE_LOCK (&) {
134             my ($hook) = @_;
135             unless (exists $Hooks{'pre-lock'}{set}{$hook}) {
136             push @{$Hooks{'pre-lock'}{list}},
137             ($Hooks{'pre-lock'}{set}{$hook} = sub { $hook->(@_); });
138             }
139             return;
140             }
141              
142             # pre-revprop-change(SVN::Look, revision, username, property-name, action)
143              
144             sub PRE_REVPROP_CHANGE (&) {
145             my ($hook) = @_;
146             unless (exists $Hooks{'pre-revprop-change'}{set}{$hook}) {
147             push @{$Hooks{'pre-revprop-change'}{list}},
148             ($Hooks{'pre-revprop-change'}{set}{$hook} = sub { $hook->(@_); });
149             }
150             return;
151             }
152              
153             # pre-unlock(repos-path, path, username, lock-token, break-unlock-flag)
154              
155             sub PRE_UNLOCK (&) {
156             my ($hook) = @_;
157             unless (exists $Hooks{'pre-unlock'}{set}{$hook}) {
158             push @{$Hooks{'pre-unlock'}{list}},
159             ($Hooks{'pre-unlock'}{set}{$hook} = sub { $hook->(@_); });
160             }
161             return;
162             }
163              
164             # < 1.8: start-commit(repos-path, username, capabilities)
165             # >= 1.8: start-commit(repos-path, username, capabilities, txn-name)
166              
167             # Subversion 1.8 added a txn-name argument to the start-commit. However it's
168             # only good to get at the commit properties but not to know about the files
169             # being changed by the commit, which would allow us to use the start-commit
170             # to perform many of the checks that we perform currently in the pre-commit
171             # hook. So, for now I'm not going to use the new argument to construct a
172             # SVN::Look object, since it is mostly useless anyway.
173              
174             sub START_COMMIT (&) {
175             my ($hook) = @_;
176             unless (exists $Hooks{'start-commit'}{set}{$hook}) {
177             push @{$Hooks{'start-commit'}{list}},
178             ($Hooks{'start-commit'}{set}{$hook} = sub { $hook->(@_); });
179             }
180             return;
181             }
182              
183             ## use critic
184              
185             1; # End of SVN::Hooks
186              
187             __END__