line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
|
2
|
|
|
|
|
|
|
=head1 NAME |
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
Slaughter::Transport::revisionControl - Transport base-class. |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
=head1 SYNOPSIS |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
This is a base-class for a generic revision control based transport. |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
=cut |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=head1 DESCRIPTION |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
This module implements the primitives which our transport API demands, but |
15
|
|
|
|
|
|
|
it does so in an abstract fashion with the intention that sub-classes |
16
|
|
|
|
|
|
|
will provide the missing configuration to allow it to be used. |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
This module may be used by any revision-control system, or other tool, |
19
|
|
|
|
|
|
|
that allows a fetch of a remote repository to be carried out by a simple |
20
|
|
|
|
|
|
|
command such as: |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
=for example begin |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
$cmd repository-location destination-path |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
=for example end |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
In our derived Mercurical class we set the command to "C<hg clone>", similarly |
29
|
|
|
|
|
|
|
in the GIT class we use "C<git clone>". Finally although it isn't a revision |
30
|
|
|
|
|
|
|
control system our rsync implementation works via a subclass precisely |
31
|
|
|
|
|
|
|
because it is possible to fetch a remote tree using a simple command, |
32
|
|
|
|
|
|
|
in that case it is: |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
=for example begin |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
rsync -qazr repository-location destination-path |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
=for example end |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
B<NOTE>: A full checkout of the remote repository is always inititated by |
41
|
|
|
|
|
|
|
this module. |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
It is possible that a future extension to this module will allow an existing |
44
|
|
|
|
|
|
|
repository to be uploaded in-place. |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=cut |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
=head1 SUBCLASSING |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
If you wish to write your own transport for a revision control tool, |
51
|
|
|
|
|
|
|
or similar command that will fetch a remote repository, you must |
52
|
|
|
|
|
|
|
subclass this class and implement the C<_init> method. |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
The following parameters should be populated in your derived class: |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
=over 8 |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
=item C<cmd_clone> |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
The command to clone the repository. This will have the repository location, as specified by "C<--prefix>", and the destination directory appended to it. |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
The command will have with the strings "C<#SRC#>" and "C<#DST#>" replaced with the source of the fetch and the destination into which to fetch it repectively. |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
The following, taken from C<Slaughter::Transport::hg>, demonstrates this: |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
=for example begin |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
$self->{ 'cmd_clone' } = "hg clone #SRC# #DST#"; |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
=for example end |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=item C<cmd_update> |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
A command to call to update an I<existing> repository. Currently each time slaughter runs it will pull the remote repository from scratch to a brand new temporary directory, it is possible in the future we will work with a local directory that persists - at that point having the ability to both checkout and update a remote repository will be useful. |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
=item C<cmd_version> |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
A command to call which will output the version of the revision control system. This may be any command which outputs text, as the output is discarded. The purposes is to ensure that the binary required for cloning is present on the system. |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=item C<name> |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
The name of the transport. |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=back |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
For a sample implementation please consult C<Slaughter::Transport::hg>. |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=cut |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
=head1 METHODS |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
Now follows documentation on the available methods. |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
=cut |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
|
99
|
1
|
|
|
1
|
|
250
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
23
|
|
100
|
1
|
|
|
1
|
|
2
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
535
|
|
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
package Slaughter::Transport::revisionControl; |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
our $VERSION = "3.0.5"; |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
=head2 new |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
Create a new instance of this object. |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
This constructor calls the "C<_init>" method of any derived class, if present, |
119
|
|
|
|
|
|
|
which is where we'll expect the setup mentioned in L</SUBCLASSING> to take place. |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
=cut |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
sub new |
124
|
|
|
|
|
|
|
{ |
125
|
5
|
|
|
5
|
1
|
1028
|
my ( $proto, %supplied ) = (@_); |
126
|
5
|
|
33
|
|
|
20
|
my $class = ref($proto) || $proto; |
127
|
|
|
|
|
|
|
|
128
|
5
|
|
|
|
|
6
|
my $self = {}; |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
|
133
|
5
|
|
|
|
|
10
|
foreach my $key ( keys %supplied ) |
134
|
|
|
|
|
|
|
{ |
135
|
0
|
|
|
|
|
0
|
$self->{ lc $key } = $supplied{ $key }; |
136
|
|
|
|
|
|
|
} |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
|
142
|
5
|
|
|
|
|
9
|
$self->{ 'error' } = undef; |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
|
147
|
5
|
|
|
|
|
6
|
$self->{ 'name' } = "revisionControl"; |
148
|
|
|
|
|
|
|
|
149
|
5
|
|
|
|
|
7
|
bless( $self, $class ); |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
|
158
|
5
|
100
|
|
|
|
17
|
if ( UNIVERSAL::can( $self, '_init' ) ) |
159
|
|
|
|
|
|
|
{ |
160
|
4
|
|
|
|
|
5
|
$self->_init(); |
161
|
4
|
|
|
|
|
6
|
$self->{ 'setup' } = 1; |
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
|
164
|
5
|
|
|
|
|
8
|
return $self; |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
=head2 isAvailable |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
Is this module available? This uses the details from the derived class |
172
|
|
|
|
|
|
|
to determine whether I<that> transport is available. |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
We regard the transport as available if the execution of the command |
175
|
|
|
|
|
|
|
stored in L</cmd_version> succeeds. |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
=cut |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
sub isAvailable |
180
|
|
|
|
|
|
|
{ |
181
|
0
|
|
|
0
|
1
|
0
|
my ($self) = (@_); |
182
|
|
|
|
|
|
|
|
183
|
0
|
|
|
|
|
0
|
$self->{ 'error' } = ""; |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
|
189
|
0
|
0
|
|
|
|
0
|
if ( !$self->{ 'setup' } ) |
190
|
|
|
|
|
|
|
{ |
191
|
0
|
|
|
|
|
0
|
$self->{ 'error' } = |
192
|
|
|
|
|
|
|
"This is a base-class, and should not be used directly\n"; |
193
|
0
|
|
|
|
|
0
|
return 0; |
194
|
|
|
|
|
|
|
} |
195
|
|
|
|
|
|
|
|
196
|
0
|
0
|
|
|
|
0
|
if ( !-d $self->{ 'transportdir' } ) |
197
|
|
|
|
|
|
|
{ |
198
|
0
|
|
|
|
|
0
|
$self->{ 'error' } = |
199
|
|
|
|
|
|
|
"Transport directory went away: $self->{'transportdir'}\n"; |
200
|
0
|
|
|
|
|
0
|
return 0; |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
|
203
|
0
|
0
|
|
|
|
0
|
if ( system("$self->{'cmd_version'} >/dev/null 2>/dev/null") != 0 ) |
204
|
|
|
|
|
|
|
{ |
205
|
0
|
|
|
|
|
0
|
$self->{ 'error' } = |
206
|
|
|
|
|
|
|
"Failed to execute '$self->{'cmd_version'}', is $self->{'name'} installed?\n"; |
207
|
0
|
|
|
|
|
0
|
return 0; |
208
|
|
|
|
|
|
|
} |
209
|
|
|
|
|
|
|
|
210
|
0
|
|
|
|
|
0
|
return 1; |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
=head2 error |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
Return the last error from the transport, this is set in L</isAvailable>. |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
=cut |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
sub error |
222
|
|
|
|
|
|
|
{ |
223
|
0
|
|
|
0
|
1
|
0
|
my ($self) = (@_); |
224
|
0
|
|
|
|
|
0
|
return ( $self->{ 'error' } ); |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
=head2 name |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
Return the name of this transport. This will be setup in the derived class, |
232
|
|
|
|
|
|
|
via the L</name> parameter. |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=cut |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
sub name |
237
|
|
|
|
|
|
|
{ |
238
|
5
|
|
|
5
|
1
|
5902
|
my ($self) = (@_); |
239
|
5
|
|
|
|
|
8
|
return ( $self->{ 'name' } ); |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
=begin doc |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
Clone the repository. |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
This is called only once, when the object is constructed. It will be |
249
|
|
|
|
|
|
|
called by slaughter, for example, to clone the repository prior to |
250
|
|
|
|
|
|
|
processing policies. |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
=end doc |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
=cut |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
sub setup |
257
|
|
|
|
|
|
|
{ |
258
|
0
|
|
|
0
|
0
|
|
my ($self) = (@_); |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
|
263
|
0
|
|
|
|
|
|
my $repo = $self->{ 'prefix' }; |
264
|
0
|
|
|
|
|
|
my $dst = $self->{ 'transportdir' }; |
265
|
|
|
|
|
|
|
|
266
|
0
|
0
|
|
|
|
|
$self->{ 'verbose' } && print "Fetching $repo into $dst\n"; |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
|
272
|
0
|
|
|
|
|
|
my $cmd = $self->{ 'cmd_clone' }; |
273
|
0
|
|
|
|
|
|
$cmd =~ s/#SRC#/$repo/g; |
274
|
0
|
|
|
|
|
|
$cmd =~ s/#DST#/$dst/g; |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
|
279
|
0
|
0
|
|
|
|
|
$cmd .= " >/dev/null 2>/dev/null" unless ( $self->{ 'verbose' } ); |
280
|
|
|
|
|
|
|
|
281
|
0
|
0
|
|
|
|
|
if ( system("$cmd") != 0 ) |
282
|
|
|
|
|
|
|
{ |
283
|
0
|
0
|
|
|
|
|
$self->{ 'verbose' } && |
284
|
|
|
|
|
|
|
print "WARNING: Failed to clone repository, command failed: $cmd"; |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
} |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
=begin doc |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
This is an internal/private method that merely returns the contents of the |
293
|
|
|
|
|
|
|
named file - or undef on error. |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
=end doc |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
=cut |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
sub _readFile |
300
|
|
|
|
|
|
|
{ |
301
|
0
|
|
|
0
|
|
|
my ( $self, $file ) = (@_); |
302
|
|
|
|
|
|
|
|
303
|
0
|
|
|
|
|
|
my $txt = undef; |
304
|
|
|
|
|
|
|
|
305
|
0
|
0
|
|
|
|
|
open( my $handle, "<", $file ) or return ($txt); |
306
|
|
|
|
|
|
|
|
307
|
0
|
|
|
|
|
|
while ( my $line = <$handle> ) |
308
|
|
|
|
|
|
|
{ |
309
|
0
|
|
|
|
|
|
$txt .= $line; |
310
|
|
|
|
|
|
|
} |
311
|
0
|
|
|
|
|
|
close($handle); |
312
|
|
|
|
|
|
|
|
313
|
0
|
|
|
|
|
|
return $txt; |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
=head2 fetchContents |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
Fetch a file from within the checked-out repository. |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
Given a root repository of /path/to/repo/ the file is looked for beneath |
323
|
|
|
|
|
|
|
/path/to/repo/files. |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
=cut |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
sub fetchContents |
328
|
|
|
|
|
|
|
{ |
329
|
0
|
|
|
0
|
1
|
|
my ( $self, %args ) = (@_); |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
|
334
|
0
|
|
|
|
|
|
my $prefix = $args{ 'prefix' }; |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
|
339
|
0
|
|
|
|
|
|
my $file = $args{ 'file' }; |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
|
344
|
0
|
|
|
|
|
|
my $complete = $self->{ 'transportdir' } . $prefix . $file; |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
|
349
|
0
|
|
|
|
|
|
return ( $self->_readFile($complete) ); |
350
|
|
|
|
|
|
|
} |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
1; |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
=head1 AUTHOR |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
Steve Kemp <steve@steve.org.uk> |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
=cut |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
=head1 LICENSE |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
Copyright (c) 2010-2015 by Steve Kemp. All rights reserved. |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
This module is free software; |
368
|
|
|
|
|
|
|
you can redistribute it and/or modify it under |
369
|
|
|
|
|
|
|
the same terms as Perl itself. |
370
|
|
|
|
|
|
|
The LICENSE file contains the full text of the license. |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
=cut |
373
|
|
|
|
|
|
|
|