File Coverage

blib/lib/App/Dest.pm
Criterion Covered Total %
statement 388 401 96.7
branch 128 172 74.4
condition 47 64 73.4
subroutine 48 48 100.0
pod 21 21 100.0
total 632 706 89.5


line stmt bran cond sub pod time code
1             package App::Dest;
2             # ABSTRACT: Deployment State Manager
3              
4 4     4   1237565 use 5.016;
  4         44  
5 4     4   967 use exact;
  4         62493  
  4         25  
6              
7 4     4   6552 use File::Basename qw( dirname basename );
  4         9  
  4         221  
8 4     4   549 use File::Copy 'copy';
  4         2538  
  4         200  
9 4     4   562 use File::Copy::Recursive qw( dircopy rcopy );
  4         4189  
  4         203  
10 4     4   1857 use File::DirCompare ();
  4         15497  
  4         104  
11 4     4   27 use File::Find 'find';
  4         8  
  4         305  
12 4     4   28 use File::Path qw( mkpath rmtree );
  4         9  
  4         237  
13 4     4   4019 use IPC::Run 'run';
  4         105858  
  4         231  
14 4     4   3553 use Path::Tiny 'path';
  4         52383  
  4         254  
15 4     4   2345 use Text::Diff ();
  4         36599  
  4         35687  
16              
17             our $VERSION = '1.32'; # VERSION
18              
19             sub init {
20 5     5 1 39411 my $self = _new( shift, 'expect_no_root_dir' );
21              
22 4 50       307 mkdir('.dest') or die "Unable to create .dest directory\n";
23 4 50       353 open( my $watch, '>', '.dest/watch' ) or die "Unable to create ~/.dest/watch file\n";
24              
25 4         24 $self = _new();
26              
27 4 50       117 if ( -f 'dest.watch' ) {
28 4 50       168 open( my $watches, '<', 'dest.watch' ) or die "Unable to read dest.watch file\n";
29              
30 4         158 my @watches = map { chomp; $_ } <$watches>;
  4         30  
  4         21  
31              
32 4         12 my @errors;
33 4         13 for my $watch (@watches) {
34             try {
35             $self->add($watch);
36             }
37 4         21 catch ($e) {
38             push( @errors, $watch . ': ' . $e );
39             }
40             }
41              
42             warn
43             "Created new watch list based on dest.watch file:\n" .
44 4         249 join( "\n", map { ' ' . $_ } @watches ) . "\n" .
45             (
46             (@errors)
47 4 50       26 ? "With the following errors:\n" . join( '', map { ' ' . $_ } @errors )
  0         0  
48             : ''
49             );
50             }
51              
52 4         76 return 0;
53             }
54              
55             sub add {
56 8     8 1 3458 my $self = _new(shift);
57 8 50       30 die "No directory specified; usage: dest add [directory]\n" unless @_;
58              
59 8         36 my @watches = $self->_watch_list;
60             my @adds = map {
61 8         33 my $dir = $_;
  8         20  
62 8 100       145 die "Directory specified does not exist\n" unless ( -d $dir );
63              
64 7         37 my $rel_dir = $self->_rel2root($dir);
65 7 100       1874 die "Directory $dir already added\n" if ( grep { $rel_dir eq $_ } @watches );
  2         16  
66 6         24 $rel_dir;
67             } @_;
68              
69 6 50       36 open( my $watch, '>', $self->_rel2dir('.dest/watch') ) or die "Unable to write ~/.dest/watch file\n";
70 6         110 print $watch $_, "\n" for ( sort @adds, map { $self->_rel2root($_) } @watches );
  1         7  
71 6         262 mkpath("$self->{root_dir}/.dest/$_") for (@adds);
72              
73 6         1601 return 0;
74             }
75              
76             sub rm {
77 1     1 1 848 my $self = _new(shift);
78 1 50       10 die "No directory specified; usage: dest rm [directory]\n" unless @_;
79              
80 1         5 my @watches = $self->_watch_list;
81             my @rms = map {
82 1         5 my $dir = $_;
  1         5  
83 1   50     4 $dir //= '';
84 1         3 $dir =~ s|/$||;
85              
86 1 50       3 die "Directory $dir not currently tracked\n" unless ( grep { $dir eq $_ } @watches );
  1         5  
87              
88 1         3 $self->_rel2root($dir);
89             } @_;
90              
91 1 50       207 open( my $watch_file, '>', $self->_rel2dir('.dest/watch') ) or die "Unable to write ~/.dest/watch file\n";
92 1         6 for my $watch_dir ( map { $self->_rel2root($_) } @watches ) {
  1         5  
93 1 50       203 if ( grep { $watch_dir eq $_ } @rms ) {
  1         4  
94 1         5 rmtree( $self->_rel2dir(".dest/$watch_dir") );
95             }
96             else {
97 0         0 print $watch_file $watch_dir, "\n";
98             }
99             }
100              
101 1         20 return 0;
102             }
103              
104             sub watches {
105 3     3 1 3605 my $self = _new(shift);
106              
107 3         10 my @watches = $self->_watch_list;
108 3 100       73 print join( "\n", @watches ), "\n" if @watches;
109              
110 3         25 return 0;
111             }
112              
113             sub putwatch {
114 1     1 1 797 my $self = _new(shift);
115 1         12 my ($file) = @_;
116 1 50       18 die "File specified does not exist\n" unless ( -f $file );
117              
118 1 50       42 open( my $new_watches, '<', $file ) or die "Unable to read specified file\n";
119              
120 1         31 my @new = map { chomp; $_ } <$new_watches>;
  2         6  
  2         8  
121 1         5 my @old = $self->_watch_list;
122              
123 1         7 for my $old (@old) {
124 1 50       2 next if ( grep { $_ eq $old } @new );
  2         8  
125 0         0 $self->rm($old);
126             }
127 1         3 for my $new (@new) {
128 2 100       7 next if ( grep { $_ eq $new } @old );
  2         5  
129 1         5 $self->add($new);
130             }
131              
132 1         19 return 0;
133             }
134              
135             sub writewatch {
136 1     1 1 819 my $self = _new(shift);
137              
138 1 50       5 copy( $self->_rel2dir('.dest/watch'), $self->_rel2dir('dest.watch') ) or die "$!\n";
139              
140 1         432 return 0;
141             }
142              
143             sub make {
144 2     2 1 3075 my ( $self, $path, $ext ) = @_;
145 2 50       8 die "No name specified; usage: dest make [path]\n" unless ($path);
146              
147 2 100       7 $ext = '.' . $ext if ( defined $ext );
148 2   100     10 $ext //= '';
149              
150             try {
151             mkpath($path);
152             for ( qw( deploy verify revert ) ) {
153             open( my $file, '>', "$path/$_$ext" ) or die;
154             print $file "\n";
155             }
156             }
157 2         7 catch ($e) {
158             die "Failed to fully make $path; check permissions or existing files\n";
159             }
160              
161 2         18 $self->expand($path);
162              
163 2         10 return 0;
164             }
165              
166             sub expand {
167 2     2 1 8 my ( $self, $path ) = @_;
168              
169 2         5 print join( ' ', map { <"$path/$_*"> } qw( deploy verify revert ) ), "\n";
  6         417  
170              
171 2         11 return 0;
172             }
173              
174             sub list {
175 2     2 1 8268 my $self = _new(shift);
176 2         7 my ($filter) = @_;
177              
178 2         12 my $tree = $self->_actions_tree($filter);
179 2         16 for my $path ( sort keys %$tree ) {
180 2 50       5 print $path, ( ( @{ $tree->{$path} } ) ? ' actions:' : ' has no actions' ), "\n";
  2         95  
181 2         11 print ' ', $_, "\n" for ( @{ $tree->{$path} } );
  2         70  
182             }
183              
184 2         21 return 0;
185             }
186              
187             sub prereqs {
188 1     1 1 1912 my $self = _new(shift);
189 1         13 my ($filter) = @_;
190              
191 1         3 for my $action ( @{ $self->_prereq_tree($filter)->{actions} } ) {
  1         4  
192 3 100       7 print $action->{action}, ( ( @{ $action->{prereqs} } ) ? ' prereqs:' : ' has no prereqs' ), "\n";
  3         67  
193 3         9 print ' ', $_, "\n" for ( @{ $action->{prereqs} } );
  3         20  
194             }
195              
196 1         11 return 0;
197             }
198              
199             sub status {
200 5     5 1 13927 my $self = _new(shift);
201              
202 5 50       30 if ( -f $self->_rel2dir('dest.watch') ) {
203 5         22 my $diff = Text::Diff::diff( $self->_rel2dir('.dest/watch'), $self->_rel2dir('dest.watch') );
204 5 50       2021 warn "Diff between current watch list and dest.watch file:\n" . $diff . "\n" if ($diff);
205             }
206              
207 5         9 for my $path ( @{ $self->_status_data->{paths} } ) {
  5         25  
208 5         179 print "$path->{state} - $path->{path}\n";
209              
210 5         31 for my $action ( @{ $path->{actions} } ) {
  5         19  
211 12 100       40 unless ( $action->{modified} ) {
212 10 100       166 print ' ' . ( ( $action->{deployed} ) ? '-' : '+' ) . ' ' . $action->{action} . "\n";
213             }
214             else {
215 2         34 print " $action->{action}\n";
216 2         36 print " M $action->{file}\n";
217             }
218             }
219             }
220              
221 5         46 return 0;
222             }
223              
224             sub diff {
225 2     2 1 1602 my $self = _new(shift);
226 2         7 my ($path) = @_;
227              
228 2 100       6 if ( not defined $path ) {
229 1         6 $self->diff($_) for ( $self->_watch_list );
230 1         8 return 0;
231             }
232              
233             File::DirCompare->compare( $self->_rel2dir( '.dest/' . $self->_rel2root($path) ), $path, sub {
234 3     3   2938 my ( $a, $b ) = @_;
235 3   100     13 $a ||= '';
236 3   100     12 $b ||= '';
237              
238 3 100 33     80 return if ( $a =~ /\/dest.wrap$/ or $b =~ /\/dest.wrap$/ or not -f $a or not -f $b );
      66        
      66        
239 1         8 print Text::Diff::diff( $a, $b );
240 1         669 return;
241 1         7 } );
242              
243 1         12 return 0;
244             }
245              
246             sub clean {
247 2     2 1 1740 my $self = _new(shift);
248              
249 2 100       12 if (@_) {
250 1         5 for (@_) {
251 1         6 my $dest = $self->_rel2dir(".dest/$_");
252 1         389 rmtree($dest);
253 1         13 rcopy( $self->_rel2dir($_), $dest );
254             }
255             }
256             else {
257 1         8 for ( map { $self->_rel2root($_) } $self->_watch_list ) {
  1         5  
258 1         231 my $dest = $self->_rel2dir(".dest/$_");
259 1         1209 rmtree($dest);
260 1         14 dircopy( $self->_rel2dir($_), $dest );
261             }
262             }
263              
264 2         8266 return 0;
265             }
266              
267             sub preinstall {
268 1     1 1 910 my $self = _new(shift);
269              
270 1 50       4 if (@_) {
271 0         0 for (@_) {
272 0         0 my $dest = $self->_rel2dir(".dest/$_");
273 0         0 rmtree($dest);
274 0         0 mkdir($dest);
275             }
276             }
277             else {
278 1         5 for ( map { $self->_rel2root($_) } $self->_watch_list ) {
  1         6  
279 1         218 my $dest = $self->_rel2dir(".dest/$_");
280 1         1161 rmtree($dest);
281 1         66 mkdir($dest);
282             }
283             }
284              
285 1         10 return 0;
286             }
287              
288             sub deploy {
289 5     5 1 9231 my $self = _new(shift);
290 5         23 my ( $dry_run, $action ) = _dry_check(@_);
291 5 50       19 die "File to deploy required; usage: dest deploy file\n" unless ($action);
292              
293 5         21 my $redeploy = delete $self->{redeploy};
294 5         36 my $execute_stack = $self->_build_execute_stack( $action, 'deploy', undef, $redeploy );
295             die "Action already deployed\n" if (
296             not @$execute_stack or
297 5 50 66     58 not scalar( grep { $_->{action} eq $action and $_->{type} eq 'deploy' } @$execute_stack )
  8 100       59  
298             );
299              
300 4 100       12 unless ($dry_run) {
301 3         22 $self->_execute_action($_) for (@$execute_stack);
302             }
303             else {
304 1         5 _dry_run_report($execute_stack);
305             }
306              
307 4         176 return 0;
308             }
309              
310             sub redeploy {
311 1     1 1 1681 my $self = _new(shift);
312 1         4 $self->{redeploy} = 1;
313 1         5 return $self->deploy(@_);
314             }
315              
316             sub revdeploy {
317 1     1 1 3670 my $self = _new(shift);
318 1         9 my ($action) = @_;
319              
320 1         15 $self->revert($action);
321 1         12 return $self->deploy($action);
322             }
323              
324             sub verify {
325 1     1 1 1589 my $self = _new(shift);
326 1         4 my ($action) = @_;
327              
328 1         18 return $self->_execute_action( $self->_build_execute_stack( $action, 'verify' )->[0] );
329             }
330              
331             sub revert {
332 3     3 1 10169 my $self = _new(shift);
333 3         39 my ( $dry_run, $action ) = _dry_check(@_);
334 3 50       26 die "File to revert required; usage: dest revert file\n" unless ($action);
335              
336 3         59 my $execute_stack = $self->_build_execute_stack( $action, 'revert' );
337             die "Action not deployed\n" if (
338             not @$execute_stack or
339 3 100 33     39 not scalar( grep { $_->{action} eq $action and $_->{type} eq 'revert' } @$execute_stack )
  7 50       53  
340             );
341              
342 3 100       35 unless ($dry_run) {
343 2         11 $self->_execute_action($_) for (@$execute_stack);
344             }
345             else {
346 1         13 _dry_run_report($execute_stack);
347             }
348              
349 3         94 return 0;
350             }
351              
352             sub update {
353 3     3 1 6982 my $self = _new(shift);
354 3         16 my ( $dry_run, @incs ) = _dry_check(@_);
355              
356 3         10 my $seen_action = {};
357 3         7 my $execute_stack;
358              
359 3         6 for (
360             sort {
361             ( $b->{modified} || 0 ) <=> ( $a->{modified} || 0 ) ||
362             $a->{action} cmp $b->{action}
363 22 50 50     117 }
      100        
364 15         56 grep { not $_->{matches} }
365 3         18 values %{ $self->_status_data->{actions} }
366             ) {
367 2         16 push( @$execute_stack, @{ $self->_build_execute_stack( $_->{action}, 'revert' ) } ) if (
368             $_->{modified} or $_->{deployed}
369 14 100 100     79 );
370 14         28 push( @$execute_stack, @{ $self->_build_execute_stack( $_->{action}, 'deploy', $seen_action ) } );
  14         39  
371             }
372              
373             $execute_stack = [
374             grep {
375 3 100       34 my $execution = $_;
  8         21  
376 8         11 my $match = 0;
377              
378 8         14 for (@incs) {
379 8 50       19 if ( index( $execution->{file}, $_ ) > -1 ) {
380 8         13 $match = 1;
381 8         13 last;
382             }
383             }
384              
385 8         14 $match;
386             }
387             @$execute_stack
388             ] if (@incs);
389              
390 3 100       15 unless ($dry_run) {
391 2         14 $self->_execute_action($_) for (@$execute_stack);
392             }
393             else {
394 1         6 _dry_run_report($execute_stack);
395             }
396              
397 3         305 return 0;
398             }
399              
400             sub version {
401 1   50 1 1 1763 print 'dest version ', $__PACKAGE__::VERSION || 0, "\n";
402 1         7 return 0;
403             }
404              
405             sub _new {
406 50     50   146 my ( $self, $expect_no_root_dir ) = @_;
407              
408 50 100       192 if ( not ref $self ) {
409 41         126 $self = bless( {}, __PACKAGE__ );
410              
411 41         201 $self->{root_dir} = Path::Tiny->cwd;
412              
413 41         2324 while ( not $self->{root_dir}->child('.dest')->is_dir ) {
414 28 100       1825 if ( $self->{root_dir}->is_rootdir ) {
415 4         108 $self->{root_dir} = '';
416 4         14 last;
417             }
418 24         628 $self->{root_dir} = $self->{root_dir}->parent;
419 24         1175 $self->{dir_depth}++;
420             }
421              
422 41 100       2349 if ( $expect_no_root_dir ) {
423 5 100       45 die "Project already initialized\n" if $self->{root_dir};
424             }
425             else {
426 36 50       214 die "Project not initialized\n" unless $self->{root_dir};
427             }
428             }
429              
430 49         155 return $self;
431             }
432              
433             sub _dry_check {
434 11     11   38 my @clean = grep { $_ ne '-d' } @_;
  12         84  
435 11 100       72 return ( @clean != @_ ) ? 1 : 0, @clean;
436             }
437              
438             sub _rel2root {
439 131     131   567 my ( $self, $dir ) = @_;
440 131   100     769 my $path = path( $dir || '.' );
441              
442             try {
443             $path = $path->realpath;
444             }
445 131         7274 catch ($e) {
446             $path = $path->absolute;
447             }
448              
449 131         28533 return $path->relative( $self->{root_dir} )->stringify;
450             }
451              
452             sub _rel2dir {
453 449     449   22954 my ( $self, $dir ) = @_;
454 449   50     9487 return ( '../' x ( $self->{dir_depth} || 0 ) ) . ( $dir || '.' );
      50        
455             }
456              
457             sub _watch_list {
458 110     110   199 my ($self) = @_;
459 110 50       553 open( my $watch, '<', $self->_rel2dir('.dest/watch') ) or die "Unable to read ~/.dest/watch file\n";
460 110         2651 return sort map { chomp; $self->_rel2dir($_) } <$watch>;
  105         354  
  105         294  
461             }
462              
463             sub _actions_tree {
464 61     61   110 my ( $self, $filter ) = @_;
465              
466 61         88 my $tree;
467 61         124 for my $path ( $self->_watch_list ) {
468 61         173 my @actions;
469              
470             find( {
471             follow => 1,
472             no_chdir => 1,
473             wanted => sub {
474 1160 100   1160   45457 return unless ( m|/deploy(?:\.[^\/]+)?| );
475 261         1307 ( my $action = $_ ) =~ s|/deploy(?:\.[^\/]+)?||;
476              
477 261 50 66     8292 push( @actions, $action ) if (
478             not defined $filter or
479             index( $action, $filter ) > -1
480             );
481             },
482 61         7639 }, $path );
483              
484 61         733 $tree->{$path} = [ sort @actions ];
485             }
486              
487 61         176 return $tree;
488             }
489              
490             sub _prereq_tree {
491 26     26   51 my ( $self, $filter ) = @_;
492 26         63 my $tree = $self->_actions_tree($filter);
493              
494 26         47 my @actions;
495 26         171 for my $path ( sort keys %$tree ) {
496 26         46 for my $action ( @{ $tree->{$path} } ) {
  26         73  
497 114         6216 my ($file) = <"$action/deploy*">;
498 114 50       4164 open( my $content, '<', $file ) or die "Unable to read $file\n";
499              
500             my $prereqs = [
501 59         179 map { $self->_rel2dir($_) }
502 59         152 grep { defined }
503 59 50       452 map { /dest\.prereq\b[\s:=-]+(.+?)\s*$/; $1 || undef }
  59         322  
504 114         2389 grep { /dest\.prereq/ } <$content>
  187         1021  
505             ];
506              
507 114         501 $action = {
508             action => $action,
509             prereqs => $prereqs,
510             };
511              
512 114         1658 push( @actions, $action );
513             }
514             }
515              
516 26         301 return { tree => $tree, actions => \@actions };
517             }
518              
519             sub _status_data {
520 33     33   87 my ($self) = @_;
521              
522 33         44 my $actions;
523 33         75 my $tree = $self->_actions_tree;
524 33         171 for my $path ( keys %$tree ) {
525 33         128 for my $action ( @{ $tree->{$path} } ) {
  33         106  
526 141         485 $actions->{$action} = {
527             action => $action,
528             deployed => 1,
529             matches => 1,
530             };
531             }
532             }
533              
534 33         59 my @paths;
535 33         98 for my $path ( $self->_watch_list ) {
536 33         130 my $printed_path = 0;
537 33         45 my $data;
538              
539             try {
540             File::DirCompare->compare(
541             $self->_rel2dir( '.dest/' . $self->_rel2root($path) ),
542             $path,
543             sub {
544 157     157   74497 my ( $a, $b ) = @_;
545 157 100 66     1075 return if ( $a and $a =~ /\/dest.wrap$/ or $b and $b =~ /\/dest.wrap$/ );
      100        
      66        
546 128 100       357 $data->{state} = 'diff' unless ( $printed_path++ );
547              
548 128 100 33     335 if ( not $b ) {
    100          
    50          
549             push(
550 18         27 @{ $data->{actions} },
  18         61  
551             {
552             action => $self->_rel2dir( substr( $self->_rel2root($a), 6 ) ),
553             deployed => 1,
554             },
555             );
556             }
557             elsif ( not $a ) {
558             push(
559 93         125 @{ $data->{actions} },
  93         304  
560             {
561             action => $b,
562             deployed => 0,
563             },
564             );
565             }
566             elsif ( $a and $b ) {
567 17         230 ( my $action = $b ) =~ s,/(?:deploy|verify|revert)(?:\.[^\/]+)?$,,;
568             push(
569 17         37 @{ $data->{actions} },
  17         133  
570             {
571             action => $action,
572             modified => 1,
573             file => $b,
574             },
575             );
576             }
577              
578 128         344 return;
579             },
580             )
581             }
582             catch ($e) {
583             $data->{state} = '?' if ( $e =~ /Not a directory/ );
584             }
585             finally {
586             $data->{state} = 'ok' unless $printed_path;
587             }
588              
589 33         177 $data->{path} = $path;
  33         299  
590 33         86 push( @paths, $data );
591              
592 33 100       105 if ( ref $data->{actions} eq 'ARRAY' ) {
593 31         48 $actions->{ $_->{action} } = $_ for ( @{ $data->{actions} } );
  31         246  
594             }
595             }
596              
597 33         221 return { paths => \@paths, actions => $actions };
598             }
599              
600             sub _build_execute_stack {
601 25     25   96 my ( $self, $name, $type, $seen_action, $redeploy ) = @_;
602              
603 25         64 my @actions_stack = ($name);
604 25         56 my $prereqs = { map { $_->{action} => $_->{prereqs} } @{ $self->_prereq_tree->{actions} } };
  111         322  
  25         60  
605 25         138 my $state = $self->_status_data->{actions};
606              
607 25 100       98 if ( $type eq 'revert' ) {
608 5         10 my $postreqs;
609 5         36 for my $action ( keys %$prereqs ) {
610 21         34 push( @{ $postreqs->{$_} }, $action ) for ( @{ $prereqs->{$action} } );
  21         58  
  8         28  
611             }
612 5         24 $prereqs = $postreqs;
613             }
614              
615 25         58 my ( @execute_stack, $wraps );
616 25   100     119 $seen_action //= {};
617              
618 25         60 while (@actions_stack) {
619 39         76 my $action = shift @actions_stack;
620 39 100       132 next if ( $seen_action->{$action}++ );
621 30 100       56 push( @actions_stack, @{ $prereqs->{$action} || [] } );
  30         119  
622              
623 30 100       107 my $location = ( $type ne 'revert' )
624             ? $action
625             : $self->_rel2dir( '.dest/' . $self->_rel2root($action) );
626              
627 30         1763 my $file = ( <"$location/$type*"> )[0];
628              
629 30 50       167 unless ( exists $wraps->{$action} ) {
630 30         75 $wraps->{$action} = undef;
631 30         95 my @nodes = split( '/', $self->_rel2root($file) );
632 30         5646 pop @nodes;
633 30 100 100     183 shift @nodes if ( defined $nodes[0] and $nodes[0] eq '.dest' );
634 30         89 while (@nodes) {
635 56         260 my $path = $self->_rel2dir( join( '/', @nodes ) . '/dest.wrap' );
636 56 100       789 if ( -f $path ) {
637 28         108 $wraps->{$action} = $path;
638 28         74 last;
639             }
640 28         118 pop @nodes;
641             }
642             }
643              
644             my $add = sub {
645 27 50   27   64 die "Action file does not exist for: $type $action\n" unless ($file);
646              
647 27 50       80 my $executable = ( $wraps->{$action} ) ? $wraps->{$action} : $file;
648 27 50       365 die 'Execute permission denied for: ' . $executable . "\n" unless ( -x $executable );
649              
650             unshift(
651             @execute_stack,
652             {
653             type => $type,
654             action => $action,
655             file => $file,
656 27         203 wrap => $wraps->{$action},
657             },
658             );
659              
660 27 100       176 if ( $type eq 'deploy' ) {
661 17         924 my $verify_file = ( <"$action/verify*"> )[0];
662 17 50       78 die "Action file does not exist for: verify $action\n" unless ($verify_file);
663              
664             splice(
665             @execute_stack,
666             1,
667             0,
668             {
669             type => 'verify',
670             action => $action,
671             file => $verify_file,
672 17         232 wrap => $wraps->{$action},
673             },
674             );
675             }
676 30         260 };
677              
678 30 100       100 if ( $type eq 'deploy' ) {
    100          
    50          
679 19 100 100     220 $add->() unless ( $state->{$action}{deployed} and not $redeploy );
680             }
681             elsif ( $type eq 'revert' ) {
682 10 100 100     68 $add->() if ( $state->{$action}{deployed} or $state->{$action}{modified} );
683             }
684             elsif ( $type eq 'verify' ) {
685 1         3 $add->();
686             }
687             }
688              
689 25         254 return \@execute_stack;
690             }
691              
692             sub _dry_run_report {
693 3     3   15 my ($execute_stack) = @_;
694 3 50       3177 print '' . ( ( $_->{wrap} ) ? $_->{wrap} . ' ' : '' ) . $_->{file}, "\n" for (@$execute_stack);
695             }
696              
697             sub _execute_action {
698 27     27   103 my ( $self, $input ) = @_;
699 27         222 my ( $type, $wrap, $action, $file, $location ) = @$input{ qw( type wrap action file location ) };
700              
701 27         57 my ( $out, $err, $died );
702             my $run = sub {
703             try {
704             run(
705             [ ( grep { defined } $wrap ), $file, $type ],
706             \undef, \$out, \$err,
707             ) or $died = 1;
708             }
709 27     27   87 catch ($e) {
710             $err = $e;
711             }
712              
713 27 50       363996 if ($err) {
714 0         0 ( my $err_str = $err ) =~ s/\s*at\s+.*$//;
715 0         0 chomp($err_str);
716 0 0       0 if ($died) {
717 0         0 die "Failed to execute $file: $err_str\n";
718             }
719             else {
720 0         0 warn "Warnings from executed $file: $err_str\n";
721             }
722             }
723 27         299 };
724              
725 27 100       98 if ( $type eq 'verify' ) {
726 12         84 $run->();
727 12         113 chomp($out);
728 12 50       64 die "$err\n" if ($err);
729              
730 12 50       51 if ($out) {
731 12         489 print "ok - verify: $action\n";
732             }
733             else {
734 0         0 die "not ok - verify: $action\n";
735             }
736             }
737             else {
738 15         503 print "begin - $type: $action\n";
739 15         75 $run->();
740 15         575 print "ok - $type: $action\n";
741             }
742              
743 27         584 my $dest_copy = $self->_rel2dir( '.dest/' . $self->_rel2root($action) );
744 27 100       6273 rmtree($dest_copy) unless ( $type eq 'verify' );
745 27 100       340 dircopy( $action, $dest_copy ) if ( $type eq 'deploy' );
746              
747 27         31399 return 0;
748             }
749              
750             1;
751              
752             __END__