File Coverage

blib/lib/App/Sqitch/Engine.pm
Criterion Covered Total %
statement 567 605 93.7
branch 173 194 89.1
condition 95 189 50.2
subroutine 115 120 95.8
pod 69 69 100.0
total 1019 1177 86.5


line stmt bran cond sub pod time code
1              
2             use 5.010;
3 30     30   26384 use Moo;
  29         173  
4 29     30   181 use strict;
  30         295  
  29         221  
5 29     29   9668 use utf8;
  29         97  
  29         627  
6 29     29   146 use Try::Tiny;
  29         69  
  29         213  
7 29     29   762 use Locale::TextDomain qw(App-Sqitch);
  29         58  
  29         2050  
8 29     29   205 use Path::Class qw(file);
  29         63  
  29         240  
9 29     29   5713 use App::Sqitch::X qw(hurl);
  29         73  
  29         1538  
10 29     29   180 use List::Util qw(first max);
  29         83  
  29         305  
11 29     29   8179 use URI::db 0.20;
  29         69  
  29         2074  
12 29     29   1422 use App::Sqitch::Types qw(Str Int Num Sqitch Plan Bool HashRef URI Maybe Target);
  29         42994  
  29         1100  
13 29     29   188 use namespace::autoclean;
  29         57  
  29         365  
14 29     29   46128 use constant registry_release => '1.1';
  29         77  
  29         242  
15 29     29   2330 use constant default_lock_timeout => 60;
  29         103  
  29         2162  
16 29     29   182  
  29         96  
  29         247958  
17             our $VERSION = 'v1.3.0'; # VERSION
18              
19             has sqitch => (
20             is => 'ro',
21             isa => Sqitch,
22             required => 1,
23             weak_ref => 1,
24             );
25              
26             has target => (
27             is => 'ro',
28             isa => Target,
29             required => 1,
30             weak_ref => 1,
31             handles => {
32             uri => 'uri',
33             client => 'client',
34             registry => 'registry',
35             destination => 'name',
36             }
37             );
38              
39             has username => (
40             is => 'ro',
41             isa => Maybe[Str],
42             lazy => 1,
43             default => sub {
44             my $self = shift;
45             $self->target->username || $self->_def_user
46             },
47             );
48              
49             has password => (
50             is => 'ro',
51             isa => Maybe[Str],
52             lazy => 1,
53             default => sub {
54             my $self = shift;
55             $self->target->password || $self->_def_pass
56             },
57             );
58              
59              
60       0      
61       0     has start_at => (
62             is => 'rw',
63 48     48 1 40970 isa => Str
64             );
65              
66             has no_prompt => (
67             is => 'rw',
68             isa => Bool,
69             default => 0,
70             );
71              
72             has prompt_accept => (
73             is => 'rw',
74             isa => Bool,
75             default => 1,
76             );
77              
78             has log_only => (
79             is => 'rw',
80             isa => Bool,
81             default => 0,
82             );
83              
84             has with_verify => (
85             is => 'rw',
86             isa => Bool,
87             default => 0,
88             );
89              
90             has max_name_length => (
91             is => 'rw',
92             isa => Int,
93             default => 0,
94             lazy => 1,
95             default => sub {
96             my $plan = shift->plan;
97             max map {
98             length $_->format_name_with_tags
99             } $plan->changes;
100             },
101             );
102              
103             has plan => (
104             is => 'rw',
105             isa => Plan,
106             lazy => 1,
107             default => sub { shift->target->plan }
108             );
109              
110             has _variables => (
111             is => 'rw',
112             isa => HashRef[Str],
113             default => sub { {} },
114             );
115              
116             # Usually expressed as an integer, but made a number for the purposes of
117             # shorter test run times.
118             has lock_timeout => (
119             is => 'rw',
120             isa => Num,
121             default => default_lock_timeout,
122             );
123              
124             has _locked => (
125             is => 'rw',
126             isa => Bool,
127             default => 0,
128             );
129              
130              
131              
132             my ( $class, $p ) = @_;
133              
134 67     67 1 689 # We should have an engine param.
  67         1277  
135 11     11 1 5211 my $target = $p->{target} or hurl 'Missing "target" parameter to load()';
136 3     3 1 2394  
  3         65  
137             # Load the engine class.
138 38     38 1 9347 my $ekey = $target->engine_key or hurl engine => __(
139             'No engine specified; specify via target or core.engine'
140             );
141 139     139 1 9888  
142             my $pkg = __PACKAGE__ . '::' . $target->engine_key;
143             eval "require $pkg" or hurl "Unable to load $pkg";
144 139 100       419 return $pkg->new( $p );
145             }
146              
147 138 100       2143  
148             my $class = ref $_[0] || shift;
149             hurl engine => __ 'No engine specified; specify via target or core.engine'
150             if $class eq __PACKAGE__;
151 137         7241 my $pkg = quotemeta __PACKAGE__;
152 137 100       12523 $class =~ s/^$pkg\:://;
153 135         2373 return $class;
154             }
155              
156 1     1 1 3  
157             return (
158             target => 'any',
159 6   66 6 1 563 registry => 'any',
160 6 100       21 client => 'any'
161             );
162 5         7 }
163 5         39  
164 5         24 my $self = shift;
165             my $driver = $self->driver;
166             eval "use $driver";
167 3     3 1 833 hurl $self->key => __x(
168             '{driver} required to manage {engine}',
169             driver => $driver,
170             engine => $self->name,
171 12     12 1 3168 ) if $@;
172             return $self;
173             }
174              
175             my ( $self, $to, $mode ) = @_;
176             $self->lock_destination;
177             my $sqitch = $self->sqitch;
178 8     8 1 10116 my $plan = $self->_sync_plan;
179 8         65 my $to_index = $plan->count - 1;
180 8     6   733 my $position = $plan->position;
  6         884  
  0            
  0            
181 8 50       98  
182             hurl plan => __ 'Nothing to deploy (empty plan)' if $to_index < 0;
183              
184             if (defined $to) {
185             $to_index = $plan->index_of($to) // hurl plan => __x(
186 0         0 'Unknown change: "{change}"',
187             change => $to,
188             );
189              
190 13     13 1 23449 # Just return if there is nothing to do.
191 13         33 if ($to_index == $position) {
192 13         88 $sqitch->info(__x(
193 13         31 'Nothing to deploy (already at "{change}")',
194 13         30 change => $to
195 13         155 ));
196             return $self;
197 13 100       65 }
198             }
199 12 100       23  
200 8   66     17 if ($position == $to_index) {
201             # We are up-to-date.
202             $sqitch->info( __ 'Nothing to deploy (up-to-date)' );
203             return $self;
204              
205             } elsif ($position == -1) {
206 7 100       15 # Initialize or upgrade the database, if necessary.
207 1         4 if ($self->initialized) {
208             $self->upgrade_registry;
209             } else {
210             $sqitch->info(__x(
211 1         74 'Adding registry tables to {destination}',
212             destination => $self->registry_destination,
213             ));
214             $self->initialize;
215 10 100       24 }
    100          
216             $self->register_project;
217 1         4  
218 1         56 } else {
219             # Make sure that $to_index is greater than the current point.
220             hurl deploy => __ 'Cannot deploy to an earlier change; use "revert" instead'
221             if $to_index < $position;
222 7 100       16 # Upgrade database if it needs it.
223 3         14 $self->upgrade_registry;
224             }
225 4         22  
226             $sqitch->info(
227             defined $to ? __x(
228             'Deploying changes through {change} to {destination}',
229 4         415 change => $plan->change_at($to_index)->format_name_with_tags,
230             destination => $self->destination,
231 7         23 ) : __x(
232             'Deploying changes to {destination}',
233             destination => $self->destination,
234             )
235 2 100       8 );
236              
237             # Check that all dependencies will be satisfied.
238 1         4 $self->check_deploy_dependencies($plan, $to_index);
239              
240             # Do it!
241 8 100       71 $mode ||= 'all';
242             my $meth = $mode eq 'change' ? '_deploy_by_change'
243             : $mode eq 'tag' ? '_deploy_by_tag'
244             : $mode eq 'all' ? '_deploy_all'
245             : hurl deploy => __x 'Unknown deployment mode: "{mode}"', mode => $mode;
246             ;
247              
248             $self->max_name_length(
249             max map {
250             length $_->format_name_with_tags
251             } ($plan->changes)[$position + 1..$to_index]
252             );
253 8         847  
254             $self->$meth( $plan, $to_index );
255             }
256 8   100     57  
257 8 100       24 my ( $self, $to ) = @_;
    100          
    100          
258              
259             # Check the registry and, once we know it's there, lock the destination.
260             $self->_check_registry;
261             $self->lock_destination;
262              
263             my $sqitch = $self->sqitch;
264             my $plan = $self->plan;
265 7         24 my @changes;
  15         80  
266              
267             if (defined $to) {
268             my ($change) = $self->_load_changes(
269 7         199 $self->change_for_key($to)
270             ) or do {
271             # Not deployed. Is it in the plan?
272             if ( $plan->find($to) ) {
273 14     14 1 81613 # Known but not deployed.
274             hurl revert => __x(
275             'Change not deployed: "{change}"',
276 14         38 change => $to
277 13         36 );
278             }
279 13         91 # Never heard of it.
280 13         270 hurl revert => __x(
281 13         78 'Unknown change: "{change}"',
282             change => $to,
283 13 100       29 );
284             };
285              
286 7 100       26 @changes = $self->deployed_changes_since(
287             $self->_load_changes($change)
288 3 100       10 ) or do {
289             $sqitch->info(__x(
290 1         4 'No changes deployed since: "{change}"',
291             change => $to,
292             ));
293             return $self;
294             };
295              
296 2         6 if ($self->no_prompt) {
297             $sqitch->info(__x(
298             'Reverting changes to {change} from {destination}',
299             change => $change->format_name_with_tags,
300             destination => $self->destination,
301             ));
302             } else {
303             hurl {
304 4 100       13 ident => 'revert:confirm',
305 1         11 message => __ 'Nothing reverted',
306             exitval => 1,
307             } unless $sqitch->ask_yes_no(__x(
308             'Revert changes to {change} from {destination}?',
309 1         70 change => $change->format_name_with_tags,
310             destination => $self->destination,
311             ), $self->prompt_accept );
312 3 100       63 }
313 1         8  
314             } else {
315             @changes = $self->deployed_changes or do {
316             $sqitch->info(__ 'Nothing to revert (nothing deployed)');
317             return $self;
318             };
319 2 100       17  
320             if ($self->no_prompt) {
321             $sqitch->info(__x(
322             'Reverting all changes from {destination}',
323             destination => $self->destination,
324             ));
325             } else {
326             hurl {
327             ident => 'revert',
328             message => __ 'Nothing reverted',
329             exitval => 1,
330             } unless $sqitch->ask_yes_no(__x(
331 6 100       15 'Revert all changes from {destination}?',
332 2         16 destination => $self->destination,
333 2         86 ), $self->prompt_accept );
334             }
335             }
336 4 100       77  
337 1         18 # Make change objects and check that all dependencies will be satisfied.
338             @changes = reverse $self->_load_changes( @changes );
339             $self->check_revert_dependencies(@changes);
340              
341             # Do we want to support modes, where failures would re-deploy to previous
342 3 100       57 # tag or all the way back to the starting point? This would be very much
343             # like deploy() mode. I'm thinking not, as a failure on a revert is not
344             # something you generally want to recover from by deploying back to where
345             # you started. But maybe I'm wrong?
346             $self->max_name_length(
347             max map { length $_->format_name_with_tags } @changes
348             );
349             $self->revert_change($_) for @changes;
350              
351             return $self;
352             }
353              
354 5         606 my ( $self, $from, $to ) = @_;
355 5         23 $self->_check_registry;
356             my $sqitch = $self->sqitch;
357             my $plan = $self->plan;
358             my @changes = $self->_load_changes( $self->deployed_changes );
359              
360             $sqitch->info(__x(
361             'Verifying {destination}',
362             destination => $self->destination,
363 5         37 ));
  15         106  
364              
365 5         262 if (!@changes) {
366             my $msg = $plan->count
367 5         79 ? __ 'No changes deployed'
368             : __ 'Nothing to verify (no planned or deployed changes)';
369             $sqitch->info($msg);
370             return $self;
371 9     9 1 21191 }
372 9         28  
373 8         22 if ($plan->count == 0) {
374 8         158 # Oy, there are deployed changes, but not planned!
375 8         52 hurl verify => __ 'There are deployed changes, but none planned!';
376             }
377 8         158  
378             # Figure out where to start and end relative to the plan.
379             my $from_idx = $self->_from_idx('verify', $from, \@changes);
380             my $to_idx = $self->_to_idx('verify', $to, \@changes);
381              
382 8 100       720 # Run the verify tests.
383 2 100       7 if ( my $count = $self->_verify_changes($from_idx, $to_idx, !$to, @changes) ) {
384             # Emit a quick report.
385             # XXX Consider coloring red.
386 2         66 my $num_changes = 1 + $to_idx - $from_idx;
387 2         92 $num_changes = @changes if @changes > $num_changes;
388             my $msg = __ 'Verify Summary Report';
389             $sqitch->emit("\n", $msg);
390 6 100       18 $sqitch->emit('-' x length $msg);
391             $sqitch->emit(__x 'Changes: {number}', number => $num_changes );
392 1         7 $sqitch->emit(__x 'Errors: {number}', number => $count );
393             hurl verify => __ 'Verify failed';
394             }
395              
396 5         27 # Success!
397 5         16 # XXX Consider coloring green.
398             $sqitch->emit(__ 'Verify successful');
399              
400 5 100       22 return $self;
401             }
402              
403 1         3 my ( $self, $ident, $from, $changes) = @_;
404 1 50       4 return 0 unless defined $from;
405 1         3 return $self->_trim_to($ident, $from, $changes)
406 1         30 }
407 1         9  
408 1         6 my ( $self, $ident, $to, $changes) = @_;
409 1         64 my $plan = $self->plan;
410 1         50 return $self->_trim_to($ident, $to, $changes, 1) if defined $to;
411             if (my $id = $self->latest_change_id) {
412             return $plan->index_of( $id ) // $plan->count - 1;
413             }
414             return $plan->count - 1;
415 4         9 }
416              
417 4         128 my ( $self, $ident, $key, $changes, $pop ) = @_;
418             my $sqitch = $self->sqitch;
419             my $plan = $self->plan;
420              
421 11     11   24 # Find the to change in the database.
422 11 100       25 my $to_id = $self->change_id_for_key( $key ) || hurl $ident => (
423 3         9 $plan->contains( $key ) ? __x(
424             'Change "{change}" has not been deployed',
425             change => $key,
426             ) : __x(
427 12     12   4562 'Cannot find "{change}" in the database or the plan',
428 12         195 change => $key,
429 12 100       81 )
430 9 100       24 );
431 1   33     7  
432             # Find the change in the plan.
433 8         43 my $to_idx = $plan->index_of( $to_id ) // hurl $ident => __x(
434             'Change "{change}" is deployed, but not planned',
435             change => $key,
436             );
437 19     19   30160  
438 19         53 # Pop or shift changes till we find the change we want.
439 19         289 if ($pop) {
440             pop @{ $changes } while $changes->[-1]->id ne $to_id;
441             } else {
442 19   66     116 shift @{ $changes } while $changes->[0]->id ne $to_id;
443             }
444              
445             # We good.
446             return $to_idx;
447             }
448              
449             my $self = shift;
450             my $from_idx = shift;
451             my $to_idx = shift;
452             my $pending = shift;
453 17   66     135 my $sqitch = $self->sqitch;
454             my $plan = $self->plan;
455             my $errcount = 0;
456             my $i = -1;
457             my @seen;
458              
459 16 100       33 my $max_name_len = max map {
460 9         136 length $_->format_name_with_tags
  23         393  
461             } @_, map { $plan->change_at($_) } $from_idx..$to_idx;
462 7         105  
  9         157  
463             for my $change (@_) {
464             $i++;
465             my $errs = 0;
466 16         149 my $reworked = 0;
467             my $name = $change->format_name_with_tags;
468             $sqitch->emit_literal(
469             " * $name ..",
470 14     14   10656 '.' x ($max_name_len - length $name), ' '
471 14         23 );
472 14         15  
473 14         17 my $plan_index = $plan->index_of( $change->id );
474 14         35 if (defined $plan_index) {
475 14         229 push @seen => $plan_index;
476 14         73 if ( $plan_index != ($from_idx + $i) ) {
477 14         17 $sqitch->comment(__ 'Out of order');
478 14         18 $errs++;
479             }
480             # Is it reworked?
481 37         171 $reworked = $plan->change_at($plan_index)->is_reworked;
482 14         28 } else {
  16         38  
483             $sqitch->comment(__ 'Not present in the plan');
484 14         108 $errs++;
485 21         27 }
486 21         26  
487 21         29 # Run the verify script.
488 21         44 try { $self->verify_change( $change ) } catch {
489 21         163 $sqitch->comment(eval { $_->message } // $_);
490             $errs++;
491             } unless $reworked;
492              
493             # Emit pass/fail and add to the total error count.
494 21         382 $sqitch->emit( $errs ? __ 'not ok' : __ 'ok' );
495 21 100       75 $errcount += $errs;
496 20         29 }
497 20 100       39  
498 4         14 # List any undeployed changes.
499 4         149 for my $idx ($from_idx..$to_idx) {
500             next if defined first { $_ == $idx } @seen;
501             my $change = $plan->change_at( $idx );
502 20         48 my $name = $change->format_name_with_tags;
503             $sqitch->emit_literal(
504 1         3 " * $name ..",
505 1         42 '.' x ($max_name_len - length $name), ' ',
506             __ 'not ok', ' '
507             );
508             $sqitch->comment(__ 'Not deployed');
509 19     19   714 $errcount++;
510 5   33 5   7341 }
  5         30  
511 5         94  
512 21 100       245 # List any pending changes.
513             if ($pending && $to_idx < ($plan->count - 1)) {
514             if (my @pending = map {
515 21 100       355 $plan->change_at($_)
516 21         805 } ($to_idx + 1)..($plan->count - 1) ) {
517             $sqitch->emit(__n(
518             'Undeployed change:',
519             'Undeployed changes:',
520 14         34 @pending,
521 16 100   20   61 ));
  20         55  
522 2         9  
523 2         6 $sqitch->emit( ' * ', $_->format_name_with_tags ) for @pending;
524 2         22 }
525             }
526              
527             return $errcount;
528             }
529 2         75  
530 2         54 my ( $self, $change ) = @_;
531             my $file = $change->verify_file;
532             if (-e $file) {
533             return try { $self->run_verify($file) }
534 14 100 100     67 catch {
535 1 50       6 hurl {
536 7         14 ident => 'verify',
537             previous_exception => $_,
538 1         4 message => __x(
539             'Verify script "{script}" failed.',
540             script => $file,
541             ),
542             };
543             };
544 1         44 }
545              
546             # The file does not exist. Complain, but don't die.
547             $self->sqitch->vent(__x(
548 14         79 'Verify script {file} does not exist',
549             file => $file,
550             ));
551              
552 20     20 1 1683 return $self;
553 20         52 }
554 20 100       1797  
555 11     11   495  
556             my ( $self, $plan, $to_index ) = @_;
557 1     1   22 my $from_index = $plan->position + 1;
558             $to_index //= $plan->count - 1;
559             my @changes = map { $plan->change_at($_) } $from_index..$to_index;
560             my (%seen, @conflicts, @required);
561              
562             for my $change (@changes) {
563             # Check for conflicts.
564             push @conflicts => grep {
565 11         584 $seen{ $_->id // '' } || $self->change_id_for_depend($_)
566             } $change->conflicts;
567              
568             # Check for prerequisites.
569 9         443 push @required => grep { !$_->resolved_id(do {
570             if ( my $req = $seen{ $_->id // '' } ) {
571             $req->id;
572             } else {
573             $self->change_id_for_depend($_);
574 9         947 }
575             }) } $change->requires;
576             $seen{ $change->id } = $change;
577 58     58 1 4410 }
578 35     35 1 2623  
579 10     10 1 37 if (@conflicts or @required) {
580 0     0 1 0 require List::MoreUtils;
581             my $listof = sub { List::MoreUtils::uniq(map { $_->as_string } @_) };
582             # Dependencies not satisfied. Put together the error messages.
583 8     8 1 608 my @msg;
584 8         133 push @msg, __nx(
585 8   66     61 'Conflicts with previously deployed change: {changes}',
586 8         16 'Conflicts with previously deployed changes: {changes}',
  47         81  
587 8         17 scalar @conflicts,
588             changes => join ' ', @conflicts,
589 8         12 ) if @conflicts = $listof->(@conflicts);
590              
591             push @msg, __nx(
592 47 100 100     285 'Missing required change: {changes}',
  9         105  
593             'Missing required changes: {changes}',
594             scalar @required,
595             changes => join ' ', @required,
596 47         214 ) if @required = $listof->(@required);
  10         220  
597 10 100 100     42  
598 2         76 hurl deploy => join "\n" => @msg;
599             }
600 8         42  
601             # Make sure nothing isn't already deployed.
602             if ( my @ids = $self->are_deployed_changes(@changes) ) {
603 47         914 hurl deploy => __nx(
604             'Change "{changes}" has already been deployed',
605             'Changes have already been deployed: {changes}',
606 8 100 100     66 scalar @ids,
607 5         22 changes => join ', ', map { $seen{$_}->format_name_with_tags . " ($_)" } @ids
608 5     10   20 );
  10         24  
  14         32  
609             }
610 5         8  
611 5 100       9 return $self;
612             }
613              
614             my $self = shift;
615             my $proj = $self->plan->project;
616             my (%seen, @msg);
617              
618 5 100       237 for my $change (@_) {
619             $seen{ $change->id } = 1;
620             my @requiring = grep {
621             !$seen{ $_->{change_id} }
622             } $self->changes_requiring_change($change) or next;
623              
624             # XXX Include change_id in the output?
625 5         125 push @msg => __nx(
626             'Change "{change}" required by currently deployed change: {changes}',
627             'Change "{change}" required by currently deployed changes: {changes}',
628             scalar @requiring,
629 3 100       13 change => $change->format_name_with_tags,
630             changes => join ' ', map {
631             ($_->{project} eq $proj ? '' : "$_->{project}:" )
632             . $_->{change}
633             . ($_->{asof_tag} // '')
634 1         9 } @requiring
  2         16  
635             );
636             }
637              
638 2         22 hurl revert => join "\n", @msg if @msg;
639              
640             # XXX Should we make sure that they are all deployed before trying to
641             # revert them?
642 4     4 1 4566  
643 4         73 return $self;
644 4         87 }
645              
646 4         7 my ( $self, $dep ) = @_;
647 5         159 hurl engine => __x(
648             'Invalid dependency: {dependency}',
649 5 100       41 dependency => $dep->as_string,
650 6         37 ) unless defined $dep->id
651             || defined $dep->change
652             || defined $dep->tag;
653              
654             # Return the first one.
655             return $self->change_id_for(
656             change_id => $dep->id,
657             change => $dep->change,
658             tag => $dep->tag,
659 4         13 project => $dep->project,
660             first => 1,
661 6 100 100     96 );
662             }
663              
664             my ( $self, $key ) = @_;
665             my $offset = App::Sqitch::Plan::ChangeList::_offset $key;
666 4 100       278 my ( $cname, $tag ) = split /@/ => $key, 2;
667              
668             my @off = ( offset => $offset );
669             return ( @off, change => $cname, tag => $tag ) if $tag;
670             return ( @off, change_id => $cname ) if $cname =~ /^[0-9a-f]{40}$/;
671 1         4 return ( @off, tag => $cname ) if $cname eq 'HEAD' || $cname eq 'ROOT';
672             return ( @off, change => $cname );
673             }
674              
675 18     18 1 1866 my $self = shift;
676 18 100 66     51 return $self->find_change_id( $self->_params_for_key(shift) );
      66        
677             }
678              
679             my ( $self, %p ) = @_;
680              
681             # Find the change ID or return undef.
682             my $change_id = $self->change_id_for(
683             change_id => $p{change_id},
684 16         78 change => $p{change},
685             tag => $p{tag},
686             project => $p{project} || $self->plan->project,
687             ) // return;
688              
689             # Return relative to the offset.
690             return $self->change_id_offset_from_id($change_id, $p{offset});
691             }
692              
693             my $self = shift;
694 26     26   48 return $self->find_change( $self->_params_for_key(shift) );
695 26         62 }
696 26         76  
697             my ( $self, %p ) = @_;
698 26         48  
699 26 100       72 # Find the change ID or return undef.
700 19 100       41 my $change_id = $self->change_id_for(
701 17 100 100     73 change_id => $p{change_id},
702 15         53 change => $p{change},
703             tag => $p{tag},
704             project => $p{project} || $self->plan->project,
705             ) // return;
706 19     19 1 28  
707 19         49 # Return relative to the offset.
708             return $self->change_offset_from_id($change_id, $p{offset});
709             }
710              
711 21     21 1 3851 my $self = shift;
712             my $plan = $self->plan;
713             my (@changes, %seen);
714             my %rework_tags_for;
715             for my $params (@_) {
716             next unless $params;
717             my $tags = $params->{tags} || [];
718 21   66     350 my $c = App::Sqitch::Plan::Change->new(%{ $params }, plan => $plan );
      100        
719              
720             # Add tags.
721             $c->add_tag(
722 19         577 App::Sqitch::Plan::Tag->new(name => $_, plan => $plan, change => $c )
723             ) for map { s/^@//; $_ } @{ $tags };
724              
725             if ( defined ( my $prev_idx = $seen{ $params->{name} } ) ) {
726 7     7 1 10 # It's reworked; grab all subsequent tags up to but not including
727 7         18 # the reworking change to the reworked change.
728             my $ctags = $rework_tags_for{ $prev_idx } ||= [];
729             my $i;
730             for my $x ($prev_idx..$#changes) {
731 9     9 1 3621 my $rtags = $ctags->[$i++] ||= [];
732             my %s = map { $_->name => 1 } @{ $rtags };
733             push @{ $rtags } => grep { !$s{$_->name} } $changes[$x]->tags;
734             }
735             }
736              
737             if ( defined ( my $reworked_idx = eval {
738 9   66     161 $plan->first_index_of( @{ $params }{qw(name id)} )
      100        
739             } ) ) {
740             # The plan has it reworked later; grab all tags from this change
741             # up to but not including the reworked change.
742 6         171 my $ctags = $rework_tags_for{ $#changes + 1 } ||= [];
743             my $idx = $plan->index_of($params->{id});
744             my $i;
745             for my $x ($idx..$reworked_idx - 1) {
746 29     29   40101 my $c = $plan->change_at($x);
747 29         543 my $rtags = $ctags->[$i++] ||= [];
748 29         222 push @{ $rtags } => $plan->change_at($x)->tags;
749 29         0 }
750 29         51 }
751 45 100       83  
752 44   100     117 push @changes => $c;
753 44         62 $seen{ $params->{name} } = $#changes;
  44         763  
754             }
755              
756             # Associate all rework tags in reverse order. Tags fetched from the plan
757             # have priority over tags fetched from the database.
758 44         4005 while (my ($idx, $tags) = each %rework_tags_for) {
  25         47  
  25         320  
  44         87  
759             my %seen;
760 44 100       342 $changes[$idx]->add_rework_tags(
761             grep { !$seen{$_->name}++ }
762             map { @{ $_ } } reverse @{ $tags }
763 5   100     20 );
764 5         8 }
765 5         12  
766 6   100     21 return @changes;
767 6         9 }
  1         7  
  6         10  
768 6         8  
  6         16  
  10         70  
769             my ( $self, $change, $ids ) = @_;
770              
771             # Return if 0 or 1 ID.
772 44 100       59 return $ids->[0] if @{ $ids } <= 1;
773 44         113  
  44         147  
774             # Too many found! Let the user know.
775             my $sqitch = $self->sqitch;
776             $sqitch->vent(__x(
777 2   50     12 'Change "{change}" is ambiguous. Please specify a tag-qualified change:',
778 2         7 change => $change,
779 2         3 ));
780 2         6  
781 6         31 # Lookup, emit reverse-chron list of tag-qualified changes, and die.
782 6   50     20 my $plan = $self->plan;
783 6         8 for my $id ( reverse @{ $ids } ) {
  6         12  
784             # Look in the plan, first.
785             if ( my $change = $plan->find($id) ) {
786             $self->sqitch->vent( ' * ', $change->format_tag_qualified_name )
787 44         2700 } else {
788 44         121 # Look it up in the database.
789             $self->sqitch->vent( ' * ', $self->name_for_change_id($id) // '' )
790             }
791             }
792             hurl engine => __ 'Change Lookup Failed';
793 29         78 }
794 6         27  
795             my ( $self, $plan, $to_index ) = @_;
796 15         141  
797 6         11 # Just deploy each change. If any fails, we just stop.
  11         11  
  11         21  
  6         9  
798             while ($plan->position < $to_index) {
799             $self->deploy_change($plan->next);
800             }
801 29         207  
802             return $self;
803             }
804              
805 1     1   617 my ($self, $tagged) = (shift, shift);
806             my $sqitch = $self->sqitch;
807              
808 1 50       1 if (my @run = reverse @_) {
  1         5  
809             $tagged = $tagged ? $tagged->format_name_with_tags : $self->start_at;
810             $sqitch->vent(
811 1         5 $tagged ? __x('Reverting to {change}', change => $tagged)
812 1         4 : __ 'Reverting all changes'
813             );
814              
815             try {
816             $self->revert_change($_) for @run;
817             } catch {
818 1         95 # Sucks when this happens.
819 1         11 $sqitch->vent(eval { $_->message } // $_);
  1         3  
820             $sqitch->vent(__ 'The schema will need to be manually repaired');
821 2 50       17 };
822 0         0 }
823              
824             hurl deploy => __ 'Deploy failed';
825 2   50     9 }
826              
827             my ( $self, $plan, $to_index ) = @_;
828 1         11  
829             my ($last_tagged, @run);
830             try {
831             while ($plan->position < $to_index) {
832 5     5   3382 my $change = $plan->next;
833             $self->deploy_change($change);
834             push @run => $change;
835 5         83 if ($change->tags) {
836 11         244 @run = ();
837             $last_tagged = $change;
838             }
839 4         136 }
840             } catch {
841             if (my $ident = eval { $_->ident }) {
842             $self->sqitch->vent($_->message) unless $ident eq 'private'
843 10     10   19 } else {
844 10         18 $self->sqitch->vent($_);
845             }
846 10 50       26 $self->_rollback($last_tagged, @run);
847 10 100       142 };
848 10 100       73  
849             return $self;
850             }
851              
852             my ( $self, $plan, $to_index ) = @_;
853              
854 10     10   364 my @run;
855             try {
856             while ($plan->position < $to_index) {
857 1   33 1   24 my $change = $plan->next;
  1         8  
858 1         6 $self->deploy_change($change);
859 10         551 push @run => $change;
860             }
861             } catch {
862 10         222 if (my $ident = try { $_->ident }) {
863             $self->sqitch->vent($_->message) unless $ident eq 'private'
864             } else {
865             $self->sqitch->vent($_);
866 9     9   11247 }
867             $self->_rollback(undef, @run);
868 9         15 };
869              
870 9     9   464 return $self;
871 27         425 }
872 27         72  
873 22         213 my $self = shift;
874 22 100       60 my $plan = $self->plan;
875 8         59  
876 8         107 if (my $state = $self->current_state) {
877             my $idx = $plan->index_of($state->{change_id}) // hurl plan => __x(
878             'Cannot find change {id} ({change}) in {file}',
879             id => $state->{change_id},
880 5 100   5   107 change => join(' ', $state->{change}, @{ $state->{tags} || [] }),
  5         27  
881 4 100       19 file => $plan->file,
882             );
883 1         6  
884             # Upgrade the registry if there is no script_hash column.
885 5         30 unless ( exists $state->{script_hash} ) {
886 9         60 $self->upgrade_registry;
887             $state->{script_hash} = $state->{change_id};
888 4         106 }
889              
890             # Update the script hashes if they're the same as the change ID.
891             # DEPRECATTION: Added in v0.998 (Jan 2015, c86cba61c); consider removing
892 10     10   3935 # in the future when all databases are likely to be updated already.
893             $self->_update_script_hashes if $state->{script_hash}
894 10         25 && $state->{script_hash} eq $state->{change_id};
895              
896 10     10   529 $plan->position($idx);
897 24         184 my $change = $plan->change_at($idx);
898 24         68 if (my @tags = $change->tags) {
899 19         506 $self->log_new_tags($change);
900             $self->start_at( $change->format_name . $tags[-1]->format_name );
901             } else {
902 5 100   5   118 $self->start_at( $change->format_name );
  5         119  
903 4 100       50 }
904              
905 1         16 } else {
906             $plan->reset;
907 5         30 }
908 10         65 return $plan;
909             }
910 5         126  
911             my ($self, $thing) = @_;
912             return $thing->isa('App::Sqitch::Plan::Tag')
913             ? $self->is_deployed_tag($thing)
914 20     20   5659 : $self->is_deployed_change($thing);
915 20         401 }
916              
917 20 100       271 my ( $self, $change ) = @_;
918             my $sqitch = $self->sqitch;
919             my $name = $change->format_name_with_tags;
920             $sqitch->info_literal(
921 8 0 33     65 " + $name ..",
  0         0  
922             '.' x ($self->max_name_length - length $name), ' '
923             );
924             $self->begin_work($change);
925              
926 8 100       19 return try {
927 1         5 $self->run_deploy($change->deploy_file) unless $self->log_only;
928 1         4 try {
929             $self->verify_change( $change ) if $self->with_verify;
930             $self->log_deploy_change($change);
931             $sqitch->info(__ 'ok');
932             } catch {
933             # Oy, logging or verify failed. Rollback.
934             $sqitch->vent(eval { $_->message } // $_);
935 8 100 100     36 $self->rollback_work($change);
936              
937 8         131 # Begin work and run the revert.
938 8         198 try {
939 8 100       22 # Don't bother displaying the reverting change name.
940 7         62 # $self->sqitch->info(' - ', $change->format_name_with_tags);
941 7         32 $self->begin_work($change);
942             $self->run_revert($change->revert_file) unless $self->log_only;
943 1         10 } catch {
944             # Oy, the revert failed. Just emit the error.
945             $sqitch->vent(eval { $_->message } // $_);
946             };
947 12         83 hurl private => __ 'Deploy failed';
948             };
949 20         247 } finally {
950             $self->finish_work($change);
951             } catch {
952             $self->log_fail_change($change);
953 2     2 1 1135 $sqitch->info(__ 'not ok');
954 2 100       19 die $_;
955             };
956             }
957              
958             my ( $self, $change ) = @_;
959             my $sqitch = $self->sqitch;
960 74     74 1 26654 my $name = $change->format_name_with_tags;
961 74         176 $sqitch->info_literal(
962 74         189 " - $name ..",
963 74         1386 '.' x ($self->max_name_length - length $name), ' '
964             );
965              
966             $self->begin_work($change);
967 74         1066  
968             try {
969             $self->run_revert($change->revert_file) unless $self->log_only;
970 74 100   74   4911 try {
971             $self->log_revert_change($change);
972 66 100       3258 $sqitch->info(__ 'ok');
973 64         477 } catch {
974 57         298 # Oy, our logging died. Rollback and revert this change.
975             $self->sqitch->vent(eval { $_->message } // $_);
976             $self->rollback_work($change);
977 9   33     18090 hurl revert => 'Revert failed';
  9         69  
978 9         78 };
979             } finally {
980             $self->finish_work($change);
981             } catch {
982             $sqitch->info(__ 'not ok');
983             die $_;
984 9         327 };
985 9 100       181 }
986              
987             my $self = shift;
988 1   33     2006  
  1         7  
989 9         48 # Try to acquire the lock without waiting.
990 9         187 return $self if $self->_locked;
991 66         716 return $self->_locked(1) if $self->try_lock;
992              
993 74     74   3964 # Lock not acquired. Tell the user what's happening.
994             my $wait = $self->lock_timeout;
995 17     17   28043 $self->sqitch->info(__x(
996 17         99 'Blocked by another instance of Sqitch working on {dest}; waiting {secs} seconds...',
997 17         755 dest => $self->destination,
998 74         544 secs => $wait,
999             ));
1000              
1001             # Try waiting for the lock.
1002 40     40 1 10362 return $self->_locked(1) if $self->wait_lock;
1003 40         92  
1004 40         98 # Timed out, so bail.
1005 40         763 hurl engine => __x(
1006             'Timed out waiting {secs} seconds for another instance of Sqitch to finish work on {dest}',
1007             dest => $self->destination,
1008             secs => $wait,
1009             );
1010 40         537 }
1011              
1012             my ($self, $code) = @_;
1013 40 100   40   2645 require Algorithm::Backoff::Exponential;
1014             my $ab = Algorithm::Backoff::Exponential->new(
1015 39         1342 max_actual_duration => $self->lock_timeout,
1016 38         187 initial_delay => 0.01, # 10 ms
1017             max_delay => 10, # 10 s
1018             );
1019 1   33     1776  
  1         9  
1020 1         8 while (1) {
1021 1         2 if (my $ret = $code->()) {
1022 39         366 return 1;
1023             }
1024 40     40   2381 my $secs = $ab->failure;
1025             return 0 if $secs < 0;
1026 2     2   3969 sleep $secs;
1027 2         118 }
1028 40         285 }
1029              
1030             my $class = ref $_[0] || $_[0];
1031             hurl "$class has not implemented wait_lock()";
1032 4     4 1 7953 }
1033              
1034              
1035 4 100       93 my $self = shift;
1036 3 100       21 my $change_id = $self->earliest_change_id(@_) // return undef;
1037             return $self->plan->get( $change_id );
1038             }
1039 2         32  
1040 2         55 my $self = shift;
1041             my $change_id = $self->latest_change_id(@_) // return undef;
1042             return $self->plan->get( $change_id );
1043             }
1044              
1045             my $self = shift;
1046             $self->registry_version != $self->registry_release;
1047 2 100       240 }
1048              
1049             my $self = shift;
1050 1         22 my $newver = $self->registry_release;
1051             my $oldver = $self->registry_version;
1052             return $self if $newver == $oldver;
1053              
1054             hurl engine => __x(
1055             'No registry found in {destination}. Have you ever deployed?',
1056             destination => $self->registry_destination,
1057             ) if $oldver == 0 && !$self->initialized;
1058 0     0   0  
1059 0         0 hurl engine => __x(
1060 0         0 'Registry version is {old} but {new} is the latest known. Please upgrade Sqitch',
1061             old => $oldver,
1062             new => $newver,
1063             ) if $newver < $oldver;
1064              
1065             hurl engine => __x(
1066 0         0 'Registry is at version {old} but latest is {new}. Please run the "upgrade" command',
1067 0 0       0 old => $oldver,
1068 0         0 new => $newver,
1069             ) if $newver > $oldver;
1070 0         0 }
1071 0 0       0  
1072 0         0 my $self = shift;
1073             return $self unless $self->needs_upgrade;
1074              
1075             my $sqitch = $self->sqitch;
1076 1     1 1 4 my $newver = $self->registry_release;
1077             my $oldver = $self->registry_version;
1078 1   33 1 1 522  
1079 1         5 hurl __x(
1080             'Registry version is {old} but {new} is the latest known. Please upgrade Sqitch.',
1081             old => $oldver,
1082 1     1 1 5 new => $newver,
1083 1     1 1 5 ) if $newver < $oldver;
1084 10     10 1 16  
1085             my $key = $self->key;
1086             my $dir = file(__FILE__)->dir->subdir(qw(Engine Upgrade));
1087 2     2 1 1329  
1088 2   50     8 my @scripts = sort { $a->[0] <=> $b->[0] } grep { $_->[0] > $oldver } map {
1089 2         40 $_->basename =~ /\A\Q$key\E-(\d(?:[.]\d*)?)/;
1090             [ $1 || 0, $_ ];
1091             } $dir->children;
1092              
1093 2     2 1 724 # Make sure we're upgrading to where we want to be.
1094 2   50     8 hurl engine => __x(
1095 2         40 'Cannot upgrade to {version}: Cannot find upgrade script "{file}"',
1096             version => $newver,
1097             file => $dir->file("$key-$newver.*"),
1098             ) unless @scripts && $scripts[-1]->[0] == $newver;
1099 5     5 1 8  
1100 5         14 # Run the upgrades.
1101             $sqitch->info(__x(
1102             'Upgrading the Sqitch registry from {old} to {new}',
1103             old => $oldver,
1104 35     35   4392 new => $newver,
1105 35         87 ));
1106 35         76 for my $script (@scripts) {
1107 35 100       195 my ($version, $file) = @{ $script };
1108             $sqitch->info(' * ' . __x(
1109 5 100 66     21 'From {old} to {new}',
1110             old => $oldver,
1111             new => $version,
1112             ));
1113             $self->run_upgrade($file);
1114 2 100       9 $self->_register_release($version);
1115             $oldver = $version;
1116             }
1117              
1118             return $self;
1119             }
1120 1 50       6  
1121             my ($self, $from_idx, @deployed_changes) = @_;
1122             my $i = -1;
1123             my $plan = $self->plan;
1124              
1125             foreach my $change (@deployed_changes) {
1126             $i++;
1127             return $i if $i + $from_idx >= $plan->count
1128 0     0 1 0 || $change->script_hash ne $plan->change_at($i + $from_idx)->script_hash;
1129 0 0       0 }
1130              
1131 0         0 return -1;
1132 0         0 }
1133 0         0  
1134             my ( $self ) = @_;
1135 0 0       0 my @deployed_changes = $self->_load_changes( $self->deployed_changes );
1136             my $divergent_idx = $self->_find_planned_deployed_divergence_idx(0, @deployed_changes);
1137              
1138             return $deployed_changes[-1]->id if $divergent_idx == -1;
1139             return undef if $divergent_idx == 0;
1140             return $deployed_changes[$divergent_idx - 1]->id;
1141 0         0 }
1142 0         0  
1143             my ( $self, $from, $to ) = @_;
1144 0         0 $self->_check_registry;
  0         0  
1145 0         0 my $sqitch = $self->sqitch;
  0         0  
1146 0   0     0 my $plan = $self->plan;
1147             my @deployed_changes = $self->_load_changes( $self->deployed_changes );
1148             my $num_failed = 0;
1149              
1150 0 0 0     0 $sqitch->info(__x(
1151             'Checking {destination}',
1152             destination => $self->destination,
1153             ));
1154              
1155             if (!@deployed_changes) {
1156             my $msg = $plan->count
1157 0         0 ? __ 'No changes deployed'
1158             : __ 'Nothing to check (no planned or deployed changes)';
1159             $sqitch->info($msg);
1160             return $self;
1161             }
1162 0         0  
1163 0         0 # Figure out where to start and end relative to the plan.
  0         0  
1164 0         0 my $from_idx = $self->_from_idx('check', $from, \@deployed_changes);
1165             $self->_to_idx('check', $to, \@deployed_changes);
1166              
1167             my $divergent_change_idx = $self->_find_planned_deployed_divergence_idx($from_idx, @deployed_changes);
1168             if ($divergent_change_idx != -1) {
1169 0         0 $num_failed++;
1170 0         0 $sqitch->emit(__x(
1171 0         0 'Script signatures diverge at change {change}',
1172             change => $deployed_changes[$divergent_change_idx]->format_name_with_tags,
1173             ));
1174 0         0 }
1175              
1176             hurl {
1177             ident => 'check',
1178 7     7   13 message => __nx(
1179 7         9 'Failed one check',
1180 7         101 'Failed {count} checks',
1181             $num_failed,
1182 7         38 count => $num_failed,
1183 9         26 ),
1184 9 100 100     19 exitval => $num_failed,
1185             } if $num_failed;
1186              
1187             $sqitch->emit(__ 'Check successful');
1188 2         19  
1189             return $self;
1190             }
1191              
1192 1     1 1 3474 my $class = ref $_[0] || $_[0];
1193 1         5 hurl "$class has not implemented initialized()";
1194 1         12 }
1195              
1196 1 50       7 my $class = ref $_[0] || $_[0];
1197 1 50       4 hurl "$class has not implemented initialize()";
1198 1         16 }
1199              
1200             my $class = ref $_[0] || $_[0];
1201             hurl "$class has not implemented register_project()";
1202 8     8 1 17154 }
1203 8         22  
1204 8         20 my $class = ref $_[0] || $_[0];
1205 8         158 hurl "$class has not implemented run_file()";
1206 8         54 }
1207 8         84  
1208             my $class = ref $_[0] || $_[0];
1209 8         119 hurl "$class has not implemented run_handle()";
1210             }
1211              
1212             my $class = ref $_[0] || $_[0];
1213             hurl "$class has not implemented log_deploy_change()";
1214 8 100       724 }
1215 2 100       7  
1216             my $class = ref $_[0] || $_[0];
1217             hurl "$class has not implemented log_fail_change()";
1218 2         68 }
1219 2         16  
1220             my $class = ref $_[0] || $_[0];
1221             hurl "$class has not implemented log_revert_change()";
1222             }
1223 6         29  
1224 6         16 my $class = ref $_[0] || $_[0];
1225             hurl "$class has not implemented log_new_tags()";
1226 6         27 }
1227 6 100       153  
1228 4         6 my $class = ref $_[0] || $_[0];
1229 4         16 hurl "$class has not implemented is_deployed_tag()";
1230             }
1231              
1232             my $class = ref $_[0] || $_[0];
1233             hurl "$class has not implemented is_deployed_change()";
1234             }
1235              
1236 6 100       310 my $class = ref $_[0] || $_[0];
1237             hurl "$class has not implemented are_deployed_changes()";
1238             }
1239              
1240             my $class = ref $_[0] || $_[0];
1241             hurl "$class has not implemented change_id_for()";
1242             }
1243              
1244             my $class = ref $_[0] || $_[0];
1245             hurl "$class has not implemented earliest_change_id()";
1246 2         8 }
1247              
1248 2         83 my $class = ref $_[0] || $_[0];
1249             hurl "$class has not implemented latest_change_id()";
1250             }
1251              
1252 1   33 1 1 447 my $class = ref $_[0] || $_[0];
1253 1         6 hurl "$class has not implemented deployed_changes()";
1254             }
1255              
1256             my $class = ref $_[0] || $_[0];
1257 1   33 1 1 611 hurl "$class has not implemented deployed_changes_since()";
1258 1         4 }
1259              
1260             my $class = ref $_[0] || $_[0];
1261             hurl "$class has not implemented load_change()";
1262 1   33 1 1 556 }
1263 1         5  
1264             my $class = ref $_[0] || $_[0];
1265             hurl "$class has not implemented changes_requiring_change()";
1266             }
1267 1   33 1 1 521  
1268 1         5 my $class = ref $_[0] || $_[0];
1269             hurl "$class has not implemented name_for_change_id()";
1270             }
1271              
1272 1   33 1 1 520 my $class = ref $_[0] || $_[0];
1273 1         5 hurl "$class has not implemented change_offset_from_id()";
1274             }
1275              
1276             my $class = ref $_[0] || $_[0];
1277 1   33 1 1 544 hurl "$class has not implemented change_id_offset_from_id()";
1278 1         5 }
1279              
1280             my $class = ref $_[0] || $_[0];
1281             hurl "$class has not implemented registered_projects()";
1282 1   33 1 1 522 }
1283 1         5  
1284             my $class = ref $_[0] || $_[0];
1285             hurl "$class has not implemented current_state()";
1286             }
1287 1   33 1 1 533  
1288 1         5 my $class = ref $_[0] || $_[0];
1289             hurl "$class has not implemented current_changes()";
1290             }
1291              
1292 1   33 1 1 555 my $class = ref $_[0] || $_[0];
1293 1         5 hurl "$class has not implemented current_tags()";
1294             }
1295              
1296             my $class = ref $_[0] || $_[0];
1297 1   33 1 1 557 hurl "$class has not implemented search_events()";
1298 1         5 }
1299              
1300             my $class = ref $_[0] || $_[0];
1301             hurl "$class has not implemented registry_version()";
1302 1   33 1 1 551 }
1303 1         5  
1304             my $class = ref $_[0] || $_[0];
1305             hurl "$class has not implemented _update_script_hashes()";
1306             }
1307 1   33 1 1 518  
1308 1         5 1;
1309              
1310              
1311             =head1 Name
1312 1   33 1 1 516  
1313 1         6 App::Sqitch::Engine - Sqitch Deployment Engine
1314              
1315             =head1 Synopsis
1316              
1317 1   33 1 1 530 my $engine = App::Sqitch::Engine->new( sqitch => $sqitch );
1318 1         5  
1319             =head1 Description
1320              
1321             App::Sqitch::Engine provides the base class for all Sqitch storage engines.
1322 1   33 1 1 555 Most likely this will not be of much interest to you unless you are hacking on
1323 1         6 the engine code.
1324              
1325             =head1 Interface
1326              
1327 1   33 1 1 535 =head2 Class Methods
1328 1         6  
1329             =head3 C<key>
1330              
1331             my $name = App::Sqitch::Engine->key;
1332 1   33 1 1 537  
1333 1         5 The key name of the engine. Should be the last part of the package name.
1334              
1335             =head3 C<name>
1336              
1337 1   33 1 1 519 my $name = App::Sqitch::Engine->name;
1338 1         5  
1339             The name of the engine. Returns the same value as C<key> by default, but
1340             should probably be overridden to return a display name for the engine.
1341              
1342 1   33 1 1 522 =head3 C<default_registry>
1343 1         5  
1344             my $reg = App::Sqitch::Engine->default_registry;
1345              
1346             Returns the name of the default registry for the engine. Most engines just
1347 1   33 1 1 540 inherit the default value, C<sqitch>, but some must do more munging, such as
1348 1         5 specifying a file name, to determine the default registry name.
1349              
1350             =head3 C<default_client>
1351              
1352 1   33 1 1 532 my $cli = App::Sqitch::Engine->default_client;
1353 1         5  
1354             Returns the name of the default client for the engine. Must be implemented by
1355             each engine.
1356              
1357 1   33 1 1 518 =head3 C<driver>
1358 1         5  
1359             my $driver = App::Sqitch::Engine->driver;
1360              
1361             The name and version of the database driver to use with the engine, returned
1362 1   33 1 1 555 as a string suitable for passing to C<use>. Used internally by C<use_driver()>
1363 1         5 to C<use> the driver and, if it dies, to display an appropriate error message.
1364             Must be overridden by subclasses.
1365              
1366             =head3 C<use_driver>
1367 1   33 1 1 540  
1368 1         4 App::Sqitch::Engine->use_driver;
1369              
1370             Uses the driver and version returned by C<driver>. Returns an error on failure
1371             and returns true on success.
1372 1   33 1 1 519  
1373 1         5 =head3 C<config_vars>
1374              
1375             my %vars = App::Sqitch::Engine->config_vars;
1376              
1377 1   33 1 1 559 Returns a hash of names and types to use for configuration variables for the
1378 1         4 engine. These can be set under the C<engine.$engine_name> section in any
1379             configuration file.
1380              
1381             The keys in the returned hash are the names of the variables. The values are
1382 1   33 1 1 540 the data types. Valid data types include:
1383 1         5  
1384             =over
1385              
1386             =item C<any>
1387 1   33 1 1 549  
1388 1         7 =item C<int>
1389              
1390             =item C<num>
1391              
1392 1   33 1   520 =item C<bool>
1393 1         5  
1394             =item C<bool-or-int>
1395              
1396             =back
1397              
1398             Values ending in C<+> (a plus sign) may be specified multiple times. Example:
1399              
1400             (
1401             client => 'any',
1402             host => 'any',
1403             port => 'int',
1404             set => 'any+',
1405             )
1406              
1407             In this example, the C<port> variable will be stored and retrieved as an
1408             integer. The C<set> variable may be of any type and may be included multiple
1409             times. All the other variables may be of any type.
1410              
1411             By default, App::Sqitch::Engine returns:
1412              
1413             (
1414             target => 'any',
1415             registry => 'any',
1416             client => 'any',
1417             )
1418              
1419             Subclasses for supported engines will return more.
1420              
1421             =head3 C<registry_release>
1422              
1423             Returns the version of the registry understood by this release of Sqitch. The
1424             C<needs_upgrade()> method compares this value to that returned by
1425             C<registry_version()> to determine whether the target's registry needs
1426             upgrading.
1427              
1428             =head3 C<default_lock_timeout>
1429              
1430             Returns C<60>, the default value for the C<lock_timeout> attribute.
1431              
1432             =head2 Constructors
1433              
1434             =head3 C<load>
1435              
1436             my $cmd = App::Sqitch::Engine->load(%params);
1437              
1438             A factory method for instantiating Sqitch engines. It loads the subclass for
1439             the specified engine and calls C<new>, passing the Sqitch object. Supported
1440             parameters are:
1441              
1442             =over
1443              
1444             =item C<sqitch>
1445              
1446             The App::Sqitch object driving the whole thing.
1447              
1448             =back
1449              
1450             =head3 C<new>
1451              
1452             my $engine = App::Sqitch::Engine->new(%params);
1453              
1454             Instantiates and returns a App::Sqitch::Engine object.
1455              
1456             =head2 Instance Accessors
1457              
1458             =head3 C<sqitch>
1459              
1460             The current Sqitch object.
1461              
1462             =head3 C<target>
1463              
1464             An L<App::Sqitch::Target> object identifying the database target, usually
1465             derived from the name of target specified on the command-line, or the default.
1466              
1467             =head3 C<uri>
1468              
1469             A L<URI::db> object representing the target database. Defaults to a URI
1470             constructed from the L<App::Sqitch> C<db_*> attributes.
1471              
1472             =head3 C<destination>
1473              
1474             A string identifying the target database. Usually the same as the C<target>,
1475             unless it's a URI with the password included, in which case it returns the
1476             value of C<uri> with the password removed.
1477              
1478             =head3 C<registry>
1479              
1480             The name of the registry schema or database.
1481              
1482             =head3 C<start_at>
1483              
1484             The point in the plan from which to start deploying changes.
1485              
1486             =head3 C<no_prompt>
1487              
1488             Boolean indicating whether or not to prompt for reverts. False by default.
1489              
1490             =head3 C<log_only>
1491              
1492             Boolean indicating whether or not to log changes I<without running deploy or
1493             revert scripts>. This is useful for an existing database schema that needs to
1494             be converted to Sqitch. False by default.
1495              
1496             =head3 C<with_verify>
1497              
1498             Boolean indicating whether or not to run the verification script after each
1499             deploy script. False by default.
1500              
1501             =head3 C<variables>
1502              
1503             A hash of engine client variables to be set. May be set and retrieved as a
1504             list.
1505              
1506             =head2 Instance Methods
1507              
1508             =head3 C<username>
1509              
1510             my $username = $engine->username;
1511              
1512             The username to use to connect to the database, for engines that require
1513             authentication. The username is looked up in the following places, returning
1514             the first to have a value:
1515              
1516             =head3 C<lock_timeout>
1517              
1518             Number of seconds to C<lock_destination()> to wait to acquire a lock before
1519             timing out. Defaults to 60.
1520              
1521             =over
1522              
1523             =item 1.
1524              
1525             The C<$SQITCH_USERNAME> environment variable.
1526              
1527             =item 2.
1528              
1529             The username from the target URI.
1530              
1531             =item 3.
1532              
1533             An engine-specific default password, which may be derived from an environment
1534             variable, engine configuration file, the system user, or none at all.
1535              
1536             =back
1537              
1538             See L<sqitch-authentication> for details and best practices for Sqitch engine
1539             authentication.
1540              
1541             =head3 C<password>
1542              
1543             my $password = $engine->password;
1544              
1545             The password to use to connect to the database, for engines that require
1546             authentication. The password is looked up in the following places, returning
1547             the first to have a value:
1548              
1549             =over
1550              
1551             =item 1.
1552              
1553             The C<$SQITCH_PASSWORD> environment variable.
1554              
1555             =item 2.
1556              
1557             The password from the target URI.
1558              
1559             =item 3.
1560              
1561             An engine-specific default password, which may be derived from an environment
1562             variable, engine configuration file, or none at all.
1563              
1564             =back
1565              
1566             See L<sqitch-authentication> for details and best practices for Sqitch engine
1567             authentication.
1568              
1569             =head3 C<registry_destination>
1570              
1571             my $registry_destination = $engine->registry_destination;
1572              
1573             Returns the name of the registry database. In other words, the database in
1574             which Sqitch's own data is stored. It will usually be the same as C<target()>,
1575             but some engines, such as L<SQLite|App::Sqitch::Engine::sqlite>, may use a
1576             separate database. Used internally to name the target when the registration
1577             tables are created.
1578              
1579             =head3 C<variables>
1580              
1581             =head3 C<set_variables>
1582              
1583             =head3 C<clear_variables>
1584              
1585             my %vars = $engine->variables;
1586             $engine->set_variables(foo => 'bar', baz => 'hi there');
1587             $engine->clear_variables;
1588              
1589             Get, set, and clear engine variables. Variables are defined as key/value pairs
1590             to be passed to the engine client in calls to C<deploy> and C<revert>, if the
1591             client supports variables. For example, the
1592             L<PostgreSQL|App::Sqitch::Engine::pg> and
1593             L<Vertica|App::Sqitch::Engine::vertica> engines pass all the variables to
1594             their C<psql> and C<vsql> clients via the C<--set> option, while the
1595             L<MySQL engine|App::Sqitch::Engine::mysql> engine sets them via the C<SET>
1596             command and the L<Oracle engine|App::Sqitch::Engine::oracle> engine sets them
1597             via the SQL*Plus C<DEFINE> command.
1598              
1599              
1600             =head3 C<deploy>
1601              
1602             $engine->deploy($to_change);
1603             $engine->deploy($to_change, $mode);
1604             $engine->deploy($to_change, $mode);
1605              
1606             Deploys changes to the target database, starting with the current deployment
1607             state, and continuing to C<$to_change>. C<$to_change> must be a valid change
1608             specification as passable to the C<index_of()> method of L<App::Sqitch::Plan>.
1609             If C<$to_change> is not specified, all changes will be applied.
1610              
1611             The second argument specifies the reversion mode in the case of deployment
1612             failure. The allowed values are:
1613              
1614             =over
1615              
1616             =item C<all>
1617              
1618             In the event of failure, revert all deployed changes, back to the point at
1619             which deployment started. This is the default.
1620              
1621             =item C<tag>
1622              
1623             In the event of failure, revert all deployed changes to the last
1624             successfully-applied tag. If no tags were applied during this deployment, all
1625             changes will be reverted to the pint at which deployment began.
1626              
1627             =item C<change>
1628              
1629             In the event of failure, no changes will be reverted. This is on the
1630             assumption that a change failure is total, and the change may be applied again.
1631              
1632             =back
1633              
1634             Note that, in the event of failure, if a reversion fails, the target database
1635             B<may be left in a corrupted state>. Write your revert scripts carefully!
1636              
1637             =head3 C<revert>
1638              
1639             $engine->revert;
1640             $engine->revert($tag);
1641             $engine->revert($tag);
1642              
1643             Reverts the L<App::Sqitch::Plan::Tag> from the database, including all of its
1644             associated changes.
1645              
1646             =head3 C<verify>
1647              
1648             $engine->verify;
1649             $engine->verify( $from );
1650             $engine->verify( $from, $to );
1651             $engine->verify( undef, $to );
1652              
1653             Verifies the database against the plan. Pass in change identifiers, as
1654             described in L<sqitchchanges>, to limit the changes to verify. For each
1655             change, information will be emitted if:
1656              
1657             =over
1658              
1659             =item *
1660              
1661             It does not appear in the plan.
1662              
1663             =item *
1664              
1665             It has not been deployed to the database.
1666              
1667             =item *
1668              
1669             It has been deployed out-of-order relative to the plan.
1670              
1671             =item *
1672              
1673             Its verify script fails.
1674              
1675             =back
1676              
1677             Changes without verify scripts will emit a warning, but not constitute a
1678             failure. If there are any failures, an exception will be thrown once all
1679             verifications have completed.
1680              
1681             =head3 C<check>
1682              
1683             $engine->check;
1684             $engine->check( $from );
1685             $engine->check( $from, $to );
1686             $engine->check( undef, $to );
1687              
1688             Compares the state of the working directory and the database by comparing the
1689             SHA1 hashes of the deploy scripts. Fails and reports divergence for all
1690             changes with non-matching hashes, indicating that the project deploy scripts
1691             differ from the scripts that were used to deploy to the database.
1692              
1693             Pass in change identifiers, as described in L<sqitchchanges>, to limit the
1694             changes to check. For each change, information will be emitted if the SHA1
1695             digest of the current deploy script does not match its SHA1 digest at the
1696             time of deployment.
1697              
1698             =head3 C<check_deploy_dependencies>
1699              
1700             $engine->check_deploy_dependencies;
1701             $engine->check_deploy_dependencies($to_index);
1702              
1703             Validates that all dependencies will be met for all changes to be deployed,
1704             starting with the currently-deployed change up to the specified index, or to
1705             the last change in the plan if no index is passed. If any of the changes to be
1706             deployed would conflict with previously-deployed changes or are missing any
1707             required changes, an exception will be thrown. Used internally by C<deploy()>
1708             to ensure that dependencies will be satisfied before deploying any changes.
1709              
1710             =head3 C<check_revert_dependencies>
1711              
1712             $engine->check_revert_dependencies(@changes);
1713              
1714             Validates that the list of changes to be reverted, which should be passed in
1715             the order in which they will be reverted, are not depended upon by other
1716             changes. If any are depended upon by other changes, an exception will be
1717             thrown listing the changes that cannot be reverted and what changes depend on
1718             them. Used internally by C<revert()> to ensure no dependencies will be
1719             violated before revering any changes.
1720              
1721             =head3 C<deploy_change>
1722              
1723             $engine->deploy_change($change);
1724             $engine->deploy_change($change);
1725              
1726             Used internally by C<deploy()> to deploy an individual change.
1727              
1728             =head3 C<revert_change>
1729              
1730             $engine->revert_change($change);
1731             $engine->revert_change($change);
1732              
1733             Used internally by C<revert()> (and, by C<deploy()> when a deploy fails) to
1734             revert an individual change.
1735              
1736             =head3 C<verify_change>
1737              
1738             $engine->verify_change($change);
1739              
1740             Used internally by C<deploy_change()> to verify a just-deployed change if
1741             C<with_verify> is true.
1742              
1743             =head3 C<is_deployed>
1744              
1745             say "Tag deployed" if $engine->is_deployed($tag);
1746             say "Change deployed" if $engine->is_deployed($change);
1747              
1748             Convenience method that dispatches to C<is_deployed_tag()> or
1749             C<is_deployed_change()> as appropriate to its argument.
1750              
1751             =head3 C<earliest_change>
1752              
1753             my $change = $engine->earliest_change;
1754             my $change = $engine->earliest_change($offset);
1755              
1756             Returns the L<App::Sqitch::Plan::Change> object representing the earliest
1757             applied change. With the optional C<$offset> argument, the returned change
1758             will be the offset number of changes following the earliest change.
1759              
1760              
1761             =head3 C<latest_change>
1762              
1763             my $change = $engine->latest_change;
1764             my $change = $engine->latest_change($offset);
1765              
1766             Returns the L<App::Sqitch::Plan::Change> object representing the latest
1767             applied change. With the optional C<$offset> argument, the returned change
1768             will be the offset number of changes before the latest change.
1769              
1770             =head3 C<change_for_key>
1771              
1772             my $change = if $engine->change_for_key($key);
1773              
1774             Searches the deployed changes for a change corresponding to the specified key,
1775             which should be in a format as described in L<sqitchchanges>. Throws an
1776             exception if the key matches more than one changes. Returns C<undef> if it
1777             matches no changes.
1778              
1779             =head3 C<change_id_for_key>
1780              
1781             my $change_id = if $engine->change_id_for_key($key);
1782              
1783             Searches the deployed changes for a change corresponding to the specified key,
1784             which should be in a format as described in L<sqitchchanges>, and returns the
1785             change's ID. Throws an exception if the key matches more than one change.
1786             Returns C<undef> if it matches no changes.
1787              
1788             =head3 C<change_for_key>
1789              
1790             my $change = if $engine->change_for_key($key);
1791              
1792             Searches the list of deployed changes for a change corresponding to the
1793             specified key, which should be in a format as described in L<sqitchchanges>.
1794             Throws an exception if the key matches multiple changes.
1795              
1796             =head3 C<change_id_for_depend>
1797              
1798             say 'Dependency satisfied' if $engine->change_id_for_depend($depend);
1799              
1800             Returns the change ID for a L<dependency|App::Sqitch::Plan::Depend>, if the
1801             dependency resolves to a change currently deployed to the database. Returns
1802             C<undef> if the dependency resolves to no currently-deployed change.
1803              
1804             =head3 C<find_change>
1805              
1806             my $change = $engine->find_change(%params);
1807              
1808             Finds and returns a deployed change, or C<undef> if the change has not been
1809             deployed. The supported parameters are:
1810              
1811             =over
1812              
1813             =item C<change_id>
1814              
1815             The change ID.
1816              
1817             =item C<change>
1818              
1819             A change name.
1820              
1821             =item C<tag>
1822              
1823             A tag name.
1824              
1825             =item C<project>
1826              
1827             A project name. Defaults to the current project.
1828              
1829             =item C<offset>
1830              
1831             The number of changes offset from the change found by the other parameters
1832             should actually be returned. May be positive or negative.
1833              
1834             =back
1835              
1836             The order of precedence for the search is:
1837              
1838             =over
1839              
1840             =item 1.
1841              
1842             Search by change ID, if passed.
1843              
1844             =item 2.
1845              
1846             Search by change name as of tag, if both are passed.
1847              
1848             =item 3.
1849              
1850             Search by change name or tag.
1851              
1852             =back
1853              
1854             The offset, if passed, will be applied relative to whatever change is found by
1855             the above algorithm.
1856              
1857             =head3 C<find_change_id>
1858              
1859             my $change_id = $engine->find_change_id(%params);
1860              
1861             Like C<find_change()>, taking the same parameters, but returning an ID instead
1862             of a change.
1863              
1864             =head3 C<run_deploy>
1865              
1866             $engine->run_deploy($deploy_file);
1867              
1868             Runs a deploy script. The implementation is just an alias for C<run_file()>;
1869             subclasses may override as appropriate.
1870              
1871             =head3 C<run_revert>
1872              
1873             $engine->run_revert($revert_file);
1874              
1875             Runs a revert script. The implementation is just an alias for C<run_file()>;
1876             subclasses may override as appropriate.
1877              
1878             =head3 C<run_verify>
1879              
1880             $engine->run_verify($verify_file);
1881              
1882             Runs a verify script. The implementation is just an alias for C<run_file()>;
1883             subclasses may override as appropriate.
1884              
1885             =head3 C<run_upgrade>
1886              
1887             $engine->run_upgrade($upgrade_file);
1888              
1889             Runs an upgrade script. The implementation is just an alias for C<run_file()>;
1890             subclasses may override as appropriate.
1891              
1892             =head3 C<needs_upgrade>
1893              
1894             if ($engine->needs_upgrade) {
1895             $engine->upgrade_registry;
1896             }
1897              
1898             Determines if the target's registry needs upgrading and returns true if it
1899             does.
1900              
1901             =head3 C<upgrade_registry>
1902              
1903             $engine->upgrade_registry;
1904              
1905             Upgrades the target's registry, if it needs upgrading. Used by the
1906             L<C<upgrade>|App::Sqitch::Command::upgrade> command.
1907              
1908             =head3 C<lock_destination>
1909              
1910             $engine->lock_destination;
1911              
1912             This method is called before deploying or reverting changes. It attempts
1913             to acquire a lock in the destination database to ensure that no other
1914             instances of Sqitch can act on the database at the same time. If it fails
1915             to acquire the lock, it emits a message to that effect, then tries again
1916             and waits. If it acquires the lock, it continues its work. If the attempt
1917             times out after C<lock_timeout> seconds, it exits with an error.
1918              
1919             The default implementation is effectively a no-op; consult the documentation
1920             for specific engines to determine whether they have implemented support for
1921             destination locking (by overriding C<try_lock()> and C<wait_lock()>).
1922              
1923             =head2 Abstract Instance Methods
1924              
1925             These methods must be overridden in subclasses.
1926              
1927             =head3 C<try_lock>
1928              
1929             $engine->try_lock;
1930              
1931             This method is called by C<lock_destination>, and this default implementation
1932             simply returns true. May be overridden in subclasses to acquire a database
1933             lock that would prevent any other instance of Sqitch from making changes at
1934             the same time. If it cannot acquire the lock, it should immediately return
1935             false without waiting.
1936              
1937             =head3 C<wait_lock>
1938              
1939             $engine->wait_lock;
1940              
1941             This method is called by C<lock_destination> when C<try_lock> returns false.
1942             It must be implemented if C<try_lock> is overridden; otherwise it throws
1943             an error when C<try_lock> returns false. It should attempt to acquire the
1944             same lock as C<try_lock>, but wait for it and time out after C<lock_timeout>
1945             seconds.
1946              
1947             =head3 C<begin_work>
1948              
1949             $engine->begin_work($change);
1950              
1951             This method is called just before a change is deployed or reverted. It should
1952             create a lock to prevent any other processes from making changes to the
1953             database, to be freed in C<finish_work> or C<rollback_work>. Unlike
1954             C<lock_destination>, this method generally starts a transaction for the
1955             duration of the deployment or reversion of a single change.
1956              
1957             =head3 C<finish_work>
1958              
1959             $engine->finish_work($change);
1960              
1961             This method is called after a change has been deployed or reverted. It should
1962             unlock the lock created by C<begin_work>.
1963              
1964             =head3 C<rollback_work>
1965              
1966             $engine->rollback_work($change);
1967              
1968             This method is called after a change has been deployed or reverted and the
1969             logging of that change has failed. It should rollback changes started by
1970             C<begin_work>.
1971              
1972             =head3 C<initialized>
1973              
1974             $engine->initialize unless $engine->initialized;
1975              
1976             Returns true if the database has been initialized for Sqitch, and false if it
1977             has not.
1978              
1979             =head3 C<initialize>
1980              
1981             $engine->initialize;
1982              
1983             Initializes the target database for Sqitch by installing the Sqitch registry
1984             schema and/or tables. Should be overridden by subclasses. This implementation
1985             throws an exception
1986              
1987             =head3 C<register_project>
1988              
1989             $engine->register_project;
1990              
1991             Registers the current project plan in the registry database. The
1992             implementation should insert the project name and URI if they have not already
1993             been inserted. If a project already exists with the same name but different
1994             URI, or a different name and the same URI, an exception should be thrown.
1995              
1996             =head3 C<is_deployed_tag>
1997              
1998             say 'Tag deployed' if $engine->is_deployed_tag($tag);
1999              
2000             Should return true if the L<tag|App::Sqitch::Plan::Tag> has been applied to
2001             the database, and false if it has not.
2002              
2003             =head3 C<is_deployed_change>
2004              
2005             say 'Change deployed' if $engine->is_deployed_change($change);
2006              
2007             Should return true if the L<change|App::Sqitch::Plan::Change> has been
2008             deployed to the database, and false if it has not.
2009              
2010             =head3 C<are_deployed_changes>
2011              
2012             say "Change $_ is deployed" for $engine->are_deployed_change(@changes);
2013              
2014             Should return the IDs of any of the changes passed in that are currently
2015             deployed. Used by C<deploy> to ensure that no changes already deployed are
2016             re-deployed.
2017              
2018             =head3 C<change_id_for>
2019              
2020             say $engine->change_id_for(
2021             change => $change_name,
2022             tag => $tag_name,
2023             project => $project,
2024             );
2025              
2026             Searches the database for the change with the specified name, tag, project,
2027             or ID. Returns C<undef> if it matches no changes. If it matches more than one
2028             change, it returns the earliest deployed change if the C<first> parameter is
2029             passed; otherwise it throws an exception The parameters are as follows:
2030              
2031             =over
2032              
2033             =item C<change>
2034              
2035             The name of a change. Required unless C<tag> or C<change_id> is passed.
2036              
2037             =item C<change_id>
2038              
2039             The ID of a change. Required unless C<tag> or C<change> is passed. Useful
2040             to determine whether an ID in a plan has been deployed to the database.
2041              
2042             =item C<tag>
2043              
2044             The name of a tag. Required unless C<change> is passed.
2045              
2046             =item C<project>
2047              
2048             The name of the project to search. Defaults to the current project.
2049              
2050             =item C<first>
2051              
2052             Return the earliest deployed change ID if the search matches more than one
2053             change. If false or not passed and more than one change is found, an
2054             exception will be thrown.
2055              
2056             =back
2057              
2058             If both C<change> and C<tag> are passed, C<find_change_id> will search for the
2059             last instance of the named change deployed I<before> the tag.
2060              
2061             =head3 C<changes_requiring_change>
2062              
2063             my @requiring = $engine->changes_requiring_change($change);
2064              
2065             Returns a list of hash references representing currently deployed changes that
2066             require the passed change. When this method returns one or more hash
2067             references, the change should not be reverted. Each hash reference should
2068             contain the following keys:
2069              
2070             =over
2071              
2072             =item C<change_id>
2073              
2074             The requiring change ID.
2075              
2076             =item C<change>
2077              
2078             The requiring change name.
2079              
2080             =item C<project>
2081              
2082             The project the requiring change is from.
2083              
2084             =item C<asof_tag>
2085              
2086             Name of the first tag to be applied after the requiring change was deployed,
2087             if any.
2088              
2089             =back
2090              
2091             =head3 C<log_deploy_change>
2092              
2093             $engine->log_deploy_change($change);
2094              
2095             Should write the records to the registry necessary to indicate that the change
2096             has been deployed.
2097              
2098             =head3 C<log_fail_change>
2099              
2100             $engine->log_fail_change($change);
2101              
2102             Should write to the database event history a record reflecting that deployment
2103             of the change failed.
2104              
2105             =head3 C<log_revert_change>
2106              
2107             $engine->log_revert_change($change);
2108              
2109             Should write to and/or remove from the registry the records necessary to
2110             indicate that the change has been reverted.
2111              
2112             =head3 C<log_new_tags>
2113              
2114             $engine->log_new_tags($change);
2115              
2116             Given a change, if it has any tags that are not currently logged in the
2117             database, they should be logged. This is assuming, of course, that the change
2118             itself has previously been logged.
2119              
2120             =head3 C<earliest_change_id>
2121              
2122             my $change_id = $engine->earliest_change_id($offset);
2123              
2124             Returns the ID of the earliest applied change from the current project. With
2125             the optional C<$offset> argument, the ID of the change the offset number of
2126             changes following the earliest change will be returned.
2127              
2128             =head3 C<latest_change_id>
2129              
2130             my $change_id = $engine->latest_change_id;
2131             my $change_id = $engine->latest_change_id($offset);
2132              
2133             Returns the ID of the latest applied change from the current project.
2134             With the optional C<$offset> argument, the ID of the change the offset
2135             number of changes before the latest change will be returned.
2136              
2137             =head3 C<deployed_changes>
2138              
2139             my @change_hashes = $engine->deployed_changes;
2140              
2141             Returns a list of hash references, each representing a change from the current
2142             project in the order in which they were deployed. The keys in each hash
2143             reference must be:
2144              
2145             =over
2146              
2147             =item C<id>
2148              
2149             The change ID.
2150              
2151             =item C<name>
2152              
2153             The change name.
2154              
2155             =item C<project>
2156              
2157             The name of the project with which the change is associated.
2158              
2159             =item C<note>
2160              
2161             The note attached to the change.
2162              
2163             =item C<planner_name>
2164              
2165             The name of the user who planned the change.
2166              
2167             =item C<planner_email>
2168              
2169             The email address of the user who planned the change.
2170              
2171             =item C<timestamp>
2172              
2173             An L<App::Sqitch::DateTime> object representing the time the change was planned.
2174              
2175             =item C<tags>
2176              
2177             An array reference of the tag names associated with the change.
2178              
2179             =back
2180              
2181             =head3 C<deployed_changes_since>
2182              
2183             my @change_hashes = $engine->deployed_changes_since($change);
2184              
2185             Returns a list of hash references, each representing a change from the current
2186             project deployed after the specified change. The keys in the hash references
2187             should be the same as for those returned by C<deployed_changes()>.
2188              
2189             =head3 C<name_for_change_id>
2190              
2191             my $change_name = $engine->name_for_change_id($change_id);
2192              
2193             Returns the tag-qualified name of the change identified by the ID. If a tag
2194             was applied to a change after that change, the name will be returned with the
2195             tag qualification, e.g., C<app_user@beta>. Otherwise, it will include the
2196             symbolic tag C<@HEAD>. e.g., C<widgets@HEAD>. This value should be suitable
2197             for uniquely identifying the change, and passing to the C<get> or C<index_of>
2198             methods of L<App::Sqitch::Plan>.
2199              
2200             =head3 C<registered_projects>
2201              
2202             my @projects = $engine->registered_projects;
2203              
2204             Returns a list of the names of Sqitch projects registered in the database.
2205              
2206             =head3 C<current_state>
2207              
2208             my $state = $engine->current_state;
2209             my $state = $engine->current_state($project);
2210              
2211             Returns a hash reference representing the current project deployment state of
2212             the database, or C<undef> if the database has no changes deployed. If a
2213             project name is passed, the state will be returned for that project. Otherwise,
2214             the state will be returned for the local project.
2215              
2216             The hash contains information about the last successfully deployed change, as
2217             well as any associated tags. The keys to the hash should include:
2218              
2219             =over
2220              
2221             =item C<project>
2222              
2223             The name of the project for which the state is reported.
2224              
2225             =item C<change_id>
2226              
2227             The current change ID.
2228              
2229             =item C<script_hash>
2230              
2231             The deploy script SHA-1 hash.
2232              
2233             =item C<change>
2234              
2235             The current change name.
2236              
2237             =item C<note>
2238              
2239             A brief description of the change.
2240              
2241             =item C<tags>
2242              
2243             An array reference of the names of associated tags.
2244              
2245             =item C<committed_at>
2246              
2247             An L<App::Sqitch::DateTime> object representing the date and time at which the
2248             change was deployed.
2249              
2250             =item C<committer_name>
2251              
2252             Name of the user who deployed the change.
2253              
2254             =item C<committer_email>
2255              
2256             Email address of the user who deployed the change.
2257              
2258             =item C<planned_at>
2259              
2260             An L<App::Sqitch::DateTime> object representing the date and time at which the
2261             change was added to the plan.
2262              
2263             =item C<planner_name>
2264              
2265             Name of the user who added the change to the plan.
2266              
2267             =item C<planner_email>
2268              
2269             Email address of the user who added the change to the plan.
2270              
2271             =back
2272              
2273             =head3 C<current_changes>
2274              
2275             my $iter = $engine->current_changes;
2276             my $iter = $engine->current_changes($project);
2277             while (my $change = $iter->()) {
2278             say '* ', $change->{change};
2279             }
2280              
2281             Returns a code reference that iterates over a list of the currently deployed
2282             changes in reverse chronological order. If a project name is not passed, the
2283             current project will be assumed. Each change is represented by a hash
2284             reference containing the following keys:
2285              
2286             =over
2287              
2288             =item C<change_id>
2289              
2290             The current change ID.
2291              
2292             =item C<script_hash>
2293              
2294             The deploy script SHA-1 hash.
2295              
2296             =item C<change>
2297              
2298             The current change name.
2299              
2300             =item C<committed_at>
2301              
2302             An L<App::Sqitch::DateTime> object representing the date and time at which the
2303             change was deployed.
2304              
2305             =item C<committer_name>
2306              
2307             Name of the user who deployed the change.
2308              
2309             =item C<committer_email>
2310              
2311             Email address of the user who deployed the change.
2312              
2313             =item C<planned_at>
2314              
2315             An L<App::Sqitch::DateTime> object representing the date and time at which the
2316             change was added to the plan.
2317              
2318             =item C<planner_name>
2319              
2320             Name of the user who added the change to the plan.
2321              
2322             =item C<planner_email>
2323              
2324             Email address of the user who added the change to the plan.
2325              
2326             =back
2327              
2328             =head3 C<current_tags>
2329              
2330             my $iter = $engine->current_tags;
2331             my $iter = $engine->current_tags($project);
2332             while (my $tag = $iter->()) {
2333             say '* ', $tag->{tag};
2334             }
2335              
2336             Returns a code reference that iterates over a list of the currently deployed
2337             tags in reverse chronological order. If a project name is not passed, the
2338             current project will be assumed. Each tag is represented by a hash reference
2339             containing the following keys:
2340              
2341             =over
2342              
2343             =item C<tag_id>
2344              
2345             The tag ID.
2346              
2347             =item C<tag>
2348              
2349             The name of the tag.
2350              
2351             =item C<committed_at>
2352              
2353             An L<App::Sqitch::DateTime> object representing the date and time at which the
2354             tag was applied.
2355              
2356             =item C<committer_name>
2357              
2358             Name of the user who applied the tag.
2359              
2360             =item C<committer_email>
2361              
2362             Email address of the user who applied the tag.
2363              
2364             =item C<planned_at>
2365              
2366             An L<App::Sqitch::DateTime> object representing the date and time at which the
2367             tag was added to the plan.
2368              
2369             =item C<planner_name>
2370              
2371             Name of the user who added the tag to the plan.
2372              
2373             =item C<planner_email>
2374              
2375             Email address of the user who added the tag to the plan.
2376              
2377             =back
2378              
2379             =head3 C<search_events>
2380              
2381             my $iter = $engine->search_events( %params );
2382             while (my $change = $iter->()) {
2383             say '* $change->{event}ed $change->{change}";
2384             }
2385              
2386             Searches the deployment event log and returns an iterator code reference with
2387             the results. If no parameters are provided, a list of all events will be
2388             returned from the iterator reverse chronological order. The supported parameters
2389             are:
2390              
2391             =over
2392              
2393             =item C<event>
2394              
2395             An array of the type of event to search for. Allowed values are "deploy",
2396             "revert", and "fail".
2397              
2398             =item C<project>
2399              
2400             Limit the events to those with project names matching the specified regular
2401             expression.
2402              
2403             =item C<change>
2404              
2405             Limit the events to those with changes matching the specified regular
2406             expression.
2407              
2408             =item C<committer>
2409              
2410             Limit the events to those logged for the actions of the committers with names
2411             matching the specified regular expression.
2412              
2413             =item C<planner>
2414              
2415             Limit the events to those with changes who's planner's name matches the
2416             specified regular expression.
2417              
2418             =item C<limit>
2419              
2420             Limit the number of events to the specified number.
2421              
2422             =item C<offset>
2423              
2424             Skip the specified number of events.
2425              
2426             =item C<direction>
2427              
2428             Return the results in the specified order, which must be a value matching
2429             C</^(:?a|de)sc/i> for "ascending" or "descending".
2430              
2431             =back
2432              
2433             Each event is represented by a hash reference containing the following keys:
2434              
2435             =over
2436              
2437             =item C<event>
2438              
2439             The type of event, which is one of:
2440              
2441             =over
2442              
2443             =item C<deploy>
2444              
2445             =item C<revert>
2446              
2447             =item C<fail>
2448              
2449             =back
2450              
2451             =item C<project>
2452              
2453             The name of the project with which the change is associated.
2454              
2455             =item C<change_id>
2456              
2457             The change ID.
2458              
2459             =item C<change>
2460              
2461             The name of the change.
2462              
2463             =item C<note>
2464              
2465             A brief description of the change.
2466              
2467             =item C<tags>
2468              
2469             An array reference of the names of associated tags.
2470              
2471             =item C<requires>
2472              
2473             An array reference of the names of any changes required by the change.
2474              
2475             =item C<conflicts>
2476              
2477             An array reference of the names of any changes that conflict with the change.
2478              
2479             =item C<committed_at>
2480              
2481             An L<App::Sqitch::DateTime> object representing the date and time at which the
2482             event was logged.
2483              
2484             =item C<committer_name>
2485              
2486             Name of the user who deployed the change.
2487              
2488             =item C<committer_email>
2489              
2490             Email address of the user who deployed the change.
2491              
2492             =item C<planned_at>
2493              
2494             An L<App::Sqitch::DateTime> object representing the date and time at which the
2495             change was added to the plan.
2496              
2497             =item C<planner_name>
2498              
2499             Name of the user who added the change to the plan.
2500              
2501             =item C<planner_email>
2502              
2503             Email address of the user who added the change to the plan.
2504              
2505             =back
2506              
2507             =head3 C<run_file>
2508              
2509             $engine->run_file($file);
2510              
2511             Should execute the commands in the specified file. This will generally be an
2512             SQL file to run through the engine's native client.
2513              
2514             =head3 C<run_handle>
2515              
2516             $engine->run_handle($file_handle);
2517              
2518             Should execute the commands in the specified file handle. The file handle's
2519             contents should be piped to the engine's native client.
2520              
2521             =head3 C<load_change>
2522              
2523             my $change = $engine->load_change($change_id);
2524              
2525             Given a deployed change ID, loads an returns a hash reference representing the
2526             change in the database. The keys should be the same as those in the hash
2527             references returned by C<deployed_changes()>. Returns C<undef> if the change
2528             has not been deployed.
2529              
2530             =head3 C<change_offset_from_id>
2531              
2532             my $change = $engine->change_offset_from_id( $change_id, $offset );
2533              
2534             Given a change ID and an offset, returns a hash reference of the data for a
2535             deployed change (with the same keys as defined for C<deployed_changes()>) in
2536             the current project that was deployed C<$offset> steps before the change
2537             identified by C<$change_id>. If C<$offset> is C<0> or C<undef>, the change
2538             represented by C<$change_id> should be returned (just like C<load_change()>).
2539             Otherwise, the change returned should be C<$offset> steps from that change ID,
2540             where C<$offset> may be positive (later step) or negative (earlier step).
2541             Returns C<undef> if the change was not found or if the offset is more than the
2542             number of changes before or after the change, as appropriate.
2543              
2544             =head3 C<change_id_offset_from_id>
2545              
2546             my $id = $engine->change_id_offset_from_id( $change_id, $offset );
2547              
2548             Like C<change_offset_from_id()> but returns the change ID rather than the
2549             change object.
2550              
2551             =head3 C<planned_deployed_common_ancestor_id>
2552              
2553             my $change_id = $engine->planned_deployed_common_ancestor_id;
2554              
2555             Compares the SHA1 hashes of the deploy scripts to their values at the time of
2556             deployment to the database and returns the latest change ID prior to any
2557             changes for which the values diverge. Used for the C<--modified> option to
2558             the C<revert> and C<rebase> commands.
2559              
2560             =head3 C<registry_version>
2561              
2562             Should return the current version of the target's registry.
2563              
2564             =head1 See Also
2565              
2566             =over
2567              
2568             =item L<sqitch>
2569              
2570             The Sqitch command-line client.
2571              
2572             =back
2573              
2574             =head1 Author
2575              
2576             David E. Wheeler <david@justatheory.com>
2577              
2578             =head1 License
2579              
2580             Copyright (c) 2012-2022 iovation Inc., David E. Wheeler
2581              
2582             Permission is hereby granted, free of charge, to any person obtaining a copy
2583             of this software and associated documentation files (the "Software"), to deal
2584             in the Software without restriction, including without limitation the rights
2585             to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
2586             copies of the Software, and to permit persons to whom the Software is
2587             furnished to do so, subject to the following conditions:
2588              
2589             The above copyright notice and this permission notice shall be included in all
2590             copies or substantial portions of the Software.
2591              
2592             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2593             IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2594             FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2595             AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2596             LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2597             OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2598             SOFTWARE.
2599              
2600             =cut