File Coverage

blib/lib/Shipwright/Backend/Git.pm
Criterion Covered Total %
statement 32 154 20.7
branch 1 34 2.9
condition 0 12 0.0
subroutine 11 28 39.2
pod 8 8 100.0
total 52 236 22.0


line stmt bran cond sub pod time code
1             package Shipwright::Backend::Git;
2              
3 1     1   748 use warnings;
  1         2  
  1         28  
4 1     1   4 use strict;
  1         2  
  1         27  
5 1     1   3 use File::Spec::Functions qw/catfile catdir/;
  1         2  
  1         41  
6 1     1   4 use Shipwright::Util;
  1         2  
  1         77  
7 1     1   4 use File::Temp qw/tempdir/;
  1         2  
  1         40  
8 1     1   5 use File::Copy::Recursive qw/rcopy/;
  1         1  
  1         35  
9 1     1   4 use Cwd qw/getcwd/;
  1         4  
  1         37  
10 1     1   4 use Shipwright::Backend::FS;
  1         2  
  1         41  
11 1     1   4 use File::Path qw/remove_tree make_path/;
  1         1  
  1         56  
12              
13             our %REQUIRE_OPTIONS = ( import => [qw/source/] );
14              
15 1     1   4 use base qw/Shipwright::Backend::Base/;
  1         1  
  1         1389  
16              
17             =head1 NAME
18              
19             Shipwright::Backend::Git - Git repository backend
20              
21             =head1 SYNOPSIS
22              
23             shipwright create -r git:file:///home/me/shipwright/my_proj.git
24              
25             =head1 DESCRIPTION
26              
27             This module implements a Git based backend
28             for Shipwright L.
29              
30             =head1 ENVIRONMENT VARIABLES
31              
32             =over 4
33              
34             =item SHIPWRIGHT_GIT - path of F command, default value is F.
35              
36             =back
37              
38             =head1 METHODS
39              
40             =over 4
41              
42             =item build
43              
44             =cut
45              
46             sub build {
47 0     0 1 0 my $self = shift;
48 0         0 my %args = @_;
49 0         0 $self->SUPER::build(%args);
50 0 0       0 $self->strip_repository
51             if $self->repository =~ m{^git:[a-z]+(?:\+[a-z]+)?://};
52 0         0 my $repo_path = $self->repository;
53 0         0 $repo_path =~ s!.*?://!!;
54 0 0 0     0 $self->_sync_local_dir if -e $repo_path && -e $self->local_dir && !$args{no_sync_local_dir};
      0        
55 0         0 return $self;
56             }
57              
58             =item initialize
59              
60             initialize a project.
61              
62             =cut
63              
64             sub initialize {
65 0     0 1 0 my $self = shift;
66              
67 0         0 my $dir = $self->SUPER::initialize(@_);
68              
69 0         0 my $path = $self->repository;
70 0         0 $path =~ s!^file://!!;
71              
72 0     0   0 run_cmd( sub { remove_tree( $path ) } );
  0         0  
73 0     0   0 run_cmd( sub { make_path( $path ) } );
  0         0  
74              
75 0         0 $self->_init_new_git_repos( $path );
76 0         0 $self->_initialize_local_dir();
77              
78 0 0       0 rcopy( $dir, $self->local_dir )
79             or confess_or_die "can't copy $dir to $path: $!";
80 0         0 $self->commit( comment => 'create project' );
81             }
82              
83             sub _init_new_git_repos {
84 0     0   0 my $self = shift;
85 0         0 my $new_repos_dir = shift;
86              
87 0         0 my $cwd = getcwd;
88              
89             # make a new bare repos at the target path
90 0         0 chdir $new_repos_dir;
91 0         0 run_cmd( [ $ENV{'SHIPWRIGHT_GIT'}, '--bare', 'init' ] );
92              
93 0         0 my ($output) =
94             run_cmd( [ $ENV{'SHIPWRIGHT_GIT'}, '--version' ] );
95 0         0 my ($version) = $output =~ /(\d+\.\d+\.\d+)/;
96 0 0 0     0 if ( $version && $version lt '1.6.2' ) {
97              
98             ### git doesn't allow to clone an empty repo before 1.6.2
99             ### make a temporary non-bare repos to initialize the new bare
100             ### repos with, pushing from the regular repos to the bare one
101 0         0 my $dir =
102             tempdir( 'shipwright_backend_git_XXXXXX', CLEANUP => 1, TMPDIR => 1 );
103              
104 0         0 chdir $dir;
105 0         0 run_cmd( [ $ENV{'SHIPWRIGHT_GIT'}, 'init' ] );
106              
107             # touch a file in the non-bare repos
108 0         0 my $initial_file = '.shipwright_git_initial';
109             {
110 0 0       0 open my $f,
  0         0  
111             '>', $initial_file
112             or confess_or_die "$! writing $dir/$initial_file"
113             }
114              
115             run_cmd(
116 0         0 [ $ENV{'SHIPWRIGHT_GIT'}, 'add', $initial_file ] );
117 0         0 run_cmd(
118             [
119             $ENV{'SHIPWRIGHT_GIT'},
120             'commit',
121             -m => 'initial commit, shipwright creating new git repository'
122             ]
123             );
124 0         0 run_cmd(
125             [ $ENV{'SHIPWRIGHT_GIT'}, 'push', $new_repos_dir, 'master' ] );
126              
127             }
128              
129 0         0 chdir $cwd;
130 0         0 return $new_repos_dir;
131             }
132              
133             sub _initialize_local_dir {
134 0     0   0 my $self = shift;
135             # the 0 is very important, or it may results in recursion
136 0         0 my $target = $self->local_dir( 0 );
137 0 0       0 remove_tree( $target ) if -e $target;
138              
139 0         0 run_cmd(
140             [ $ENV{'SHIPWRIGHT_GIT'}, 'clone', $self->repository, $target ] );
141 0         0 my $cwd = getcwd;
142 0         0 chdir $target;
143             # git 1.6.3.3 will warn if we don't specify push.default
144 0         0 run_cmd(
145             [ $ENV{'SHIPWRIGHT_GIT'}, 'config', 'push.default', 'matching' ] );
146 0         0 chdir $cwd;
147 0         0 return $target;
148             }
149              
150             sub _sync_local_dir {
151 0     0   0 my $self = shift;
152 0         0 my $target = $self->local_dir;
153 0         0 my $cwd = getcwd;
154 0 0       0 chdir $target or return;
155              
156 0         0 run_cmd( [ $ENV{'SHIPWRIGHT_GIT'}, 'pull' ] );
157 0         0 chdir $cwd;
158             }
159              
160              
161             =item check_repository
162              
163             check if the given repository is valid.
164              
165             =cut
166              
167             sub check_repository {
168 0     0 1 0 my $self = shift;
169 0         0 my %args = @_;
170              
171 0 0       0 if ( $args{action} eq 'create' ) {
172 0         0 my $repo = $self->repository;
173 0 0       0 if ( $repo =~ m{^file://(.*)} ) {
174 0 0 0     0 if ( $args{force} || !-e $1 ) {
175 0         0 return 1;
176             }
177 0         0 $self->log->fatal("$repo exists already");
178 0         0 return;
179             }
180             else {
181 0         0 $self->log->fatal(
182             "git backend doesn't support remote repository");
183 0         0 return;
184             }
185             }
186             else {
187              
188 0         0 return $self->SUPER::check_repository(@_);
189             }
190 0         0 return;
191             }
192              
193             =item fs_backend
194              
195             git's local clone is nearly the same as a fs backend, this returns
196             a Shipwright::Backend::FS object which reflects the local_dir repository.
197              
198             =cut
199              
200             sub fs_backend {
201 0     0 1 0 my $self = shift;
202 0 0       0 return $self->{fs_backend} if $self->{fs_backend};
203             # XXX TODO not a great place to clone, need refactor
204 0         0 my $local_dir = $self->local_dir;
205 0         0 $self->{fs_backend} = Shipwright::Backend::FS->new(
206             repository => $self->local_dir,
207             );
208 0         0 return $self->{fs_backend};
209             }
210              
211             sub _cmd {
212 0     0   0 my $self = shift;
213 0         0 return $self->fs_backend->_cmd(@_);
214             }
215              
216             sub _yml {
217 0     0   0 my $self = shift;
218 0         0 my $return = $self->fs_backend->_yml(@_);
219 0 0       0 if ( @_ > 1 ) {
220 0         0 $self->commit( comment => 'update ' . $_[0] );
221             }
222 0         0 return $return;
223             }
224              
225             =item info
226              
227             =cut
228              
229             sub info {
230 0     0 1 0 my $self = shift;
231 0         0 return $self->fs_backend->info(@_);
232             }
233              
234             sub _update_dir {
235 0     0   0 my $self = shift;
236 0         0 $self->fs_backend->_update_dir(@_);
237 0         0 $self->commit( comment => 'update ' . $_[0] );
238             }
239              
240             sub _update_file {
241 0     0   0 my $self = shift;
242 0         0 $self->fs_backend->_update_file(@_);
243 0         0 $self->commit( comment => 'update ' . $_[0] );
244             }
245              
246             =item import
247              
248             =cut
249              
250             sub import {
251 1     1   7 my $self = shift;
252 1 50       9 return unless ref $self; # get rid of class->import
253 0           $self->fs_backend->import(@_);
254 0           my %args = @_;
255 0           my $name = $args{source};
256 0           $name =~ s!.*/!!;
257 0           $self->commit( comment => 'import ' . $name );
258             }
259              
260             =item commit
261              
262             =cut
263              
264             sub commit {
265 0     0 1   my $self = shift;
266 0           my %args =
267             ( comment => 'comment', @_ ); # git doesn't allow comment to be blank
268              
269 0 0         if ( $self->local_dir ) {
270 0           my $cwd = getcwd;
271 0 0         chdir $self->local_dir or return;
272              
273 0           run_cmd( [ $ENV{'SHIPWRIGHT_GIT'}, 'add', '-f', '.' ] );
274              
275             # TODO comment need to be something special
276 0           run_cmd(
277             [ $ENV{'SHIPWRIGHT_GIT'}, 'commit', '-m', $args{comment} ], 1 );
278 0           run_cmd(
279             [ $ENV{'SHIPWRIGHT_GIT'}, 'push', 'origin', 'master' ] );
280 0           chdir $cwd;
281             }
282 0           return;
283             }
284              
285             =item delete
286              
287             =cut
288              
289             sub delete {
290 0     0 1   my $self = shift;
291 0           my %args = @_;
292 0           my $path = $args{path};
293 0           $path =~ s!^/!!;
294 0           my $cwd = getcwd;
295 0 0         chdir $self->local_dir or return;
296 0           run_cmd( [ $ENV{'SHIPWRIGHT_GIT'}, 'rm', '-rf', $path ] );
297 0           chdir $cwd;
298 0           $self->commit( comment => 'delete ' . $path );
299             }
300              
301             =item move
302              
303             =cut
304              
305             sub move {
306 0     0 1   my $self = shift;
307 0           my %args = @_;
308 0           my $path = $args{path};
309 0           $path =~ s!^/!!;
310 0           my $new_path = $args{new_path};
311 0           $new_path =~ s!^/!!;
312 0           my $cwd = getcwd;
313              
314 0 0         chdir $self->local_dir or return;
315 0           run_cmd( [ $ENV{'SHIPWRIGHT_GIT'}, 'mv', $path, $new_path ] );
316 0           chdir $cwd;
317 0           $self->commit( comment => "move $path to $new_path" );
318             }
319              
320             =back
321              
322             =cut
323              
324             1;
325              
326             __END__