1package App::Sqitch::Command; 2 3use 5.010; 4use strict; 5use warnings; 6use utf8; 7use Try::Tiny; 8use Locale::TextDomain qw(App-Sqitch); 9use App::Sqitch::X qw(hurl); 10use Hash::Merge 'merge'; 11use Moo; 12use App::Sqitch::Types qw(Sqitch Target); 13 14our $VERSION = '0.9994'; 15 16use constant ENGINES => qw( 17 pg 18 sqlite 19 mysql 20 oracle 21 firebird 22 vertica 23); 24 25has sqitch => ( 26 is => 'ro', 27 isa => Sqitch, 28 required => 1, 29 handles => [qw( 30 run 31 shell 32 quote_shell 33 capture 34 probe 35 verbosity 36 trace 37 trace_literal 38 debug 39 debug_literal 40 info 41 info_literal 42 comment 43 comment_literal 44 emit 45 emit_literal 46 vent 47 vent_literal 48 warn 49 warn_literal 50 page 51 page_literal 52 prompt 53 ask_y_n 54 )], 55); 56 57has default_target => ( 58 is => 'ro', 59 isa => Target, 60 lazy => 1, 61 default => sub { 62 my $sqitch = shift->sqitch; 63 my @params = (sqitch => $sqitch); 64 unless ( 65 $sqitch->options->{engine} 66 || $sqitch->config->get(key => 'core.engine') 67 || $sqitch->config->get(key => 'core.target') 68 ) { 69 # No specified engine, so specify an engineless URI. 70 require URI::db; 71 push @params, uri => URI::db->new('db:'); 72 } 73 require App::Sqitch::Target; 74 return App::Sqitch::Target->new(@params); 75 }, 76); 77 78sub command { 79 my $class = ref $_[0] || shift; 80 return '' if $class eq __PACKAGE__; 81 my $pkg = quotemeta __PACKAGE__; 82 $class =~ s/^$pkg\:://; 83 $class =~ s/_/-/g; 84 return $class; 85} 86 87sub load { 88 my ( $class, $p ) = @_; 89 my $sqitch = $p->{sqitch}; 90 91 # We should have a command. 92 $class->usage unless $p->{command}; 93 ( my $cmd = $p->{command} ) =~ s/-/_/g; 94 95 # Load the command class. 96 my $pkg = __PACKAGE__ . "::$cmd"; 97 try { 98 eval "require $pkg" or die $@; 99 } 100 catch { 101 # Emit the original error for debugging. 102 $sqitch->debug($_); 103 104 # Suggest help if it's not a valid command. 105 hurl { 106 ident => 'command', 107 exitval => 1, 108 message => __x( 109 '"{command}" is not a valid command', 110 command => $cmd, 111 ), 112 }; 113 }; 114 115 # Merge the command-line options and configuration parameters 116 my $params = $pkg->configure( 117 $p->{config}, 118 $pkg->_parse_opts( $p->{args} ) 119 ); 120 121 # Instantiate and return the command. 122 $params->{sqitch} = $sqitch; 123 return $pkg->new($params); 124} 125 126sub configure { 127 my ( $class, $config, $options ) = @_; 128 129 return Hash::Merge->new->merge( 130 $options, 131 $config->get_section( section => $class->command ), 132 ); 133} 134 135sub options { 136 return; 137} 138 139sub _parse_opts { 140 my ( $class, $args ) = @_; 141 return {} unless $args && @{$args}; 142 143 my %opts; 144 Getopt::Long::Configure(qw(bundling no_pass_through)); 145 Getopt::Long::GetOptionsFromArray( $args, \%opts, $class->options ) 146 or $class->usage; 147 148 # Convert dashes to underscores. 149 for my $k (keys %opts) { 150 next unless ( my $nk = $k ) =~ s/-/_/g; 151 $opts{$nk} = delete $opts{$k}; 152 } 153 154 return \%opts; 155} 156 157sub _bn { 158 require File::Basename; 159 File::Basename::basename($0); 160} 161 162sub _pod2usage { 163 my ( $self, %params ) = @_; 164 my $command = $self->command; 165 require Pod::Find; 166 require Pod::Usage; 167 my $bn = _bn; 168 my $find_pod = sub { 169 Pod::Find::pod_where({ '-inc' => 1, '-script' => 1 }, shift ); 170 }; 171 $params{'-input'} ||= $find_pod->("$bn-$command") 172 || $find_pod->("sqitch-$command") 173 || $find_pod->($bn) 174 || $find_pod->('sqitch') 175 || $find_pod->(ref $self || $self) 176 || $find_pod->(__PACKAGE__); 177 Pod::Usage::pod2usage( 178 '-verbose' => 99, 179 '-sections' => '(?i:(Usage|Synopsis|Options))', 180 '-exitval' => 2, 181 %params 182 ); 183} 184 185sub execute { 186 my $self = shift; 187 hurl( 188 'The execute() method must be called from a subclass of ' 189 . __PACKAGE__ 190 ) if ref $self eq __PACKAGE__; 191 192 hurl 'The execute() method has not been overridden in ' . ref $self; 193} 194 195sub usage { 196 my $self = shift; 197 require Pod::Find; 198 my $upod = _bn . '-' . $self->command . '-usage'; 199 $self->_pod2usage( 200 '-input' => Pod::Find::pod_where( { '-inc' => 1 }, $upod ) || undef, 201 '-message' => join '', @_ 202 ); 203} 204 205 206sub parse_args { 207 my ($self, %p) = @_; 208 my $sqitch = $self->sqitch; 209 my $config = $sqitch->config; 210 require App::Sqitch::Target; 211 my $target = App::Sqitch::Target->new( sqitch => $sqitch, name => $p{target} ); 212 my (%seen, %target_for); 213 214 my %rec = map { $_ => [] } qw(targets unknown); 215 $rec{changes} = [] unless $p{no_changes}; 216 if ($p{target}) { 217 push @{ $rec{targets} } => $target; 218 $seen{$target->name}++; 219 } 220 221 my %engines = map { $_ => 1 } ENGINES; 222 for my $arg (@{ $p{args} }) { 223 if ( !$p{no_changes} && $target && $target->plan->contains($arg) ) { 224 # A change. Keep the target if it's the default. 225 push @{ $rec{targets} } => $target unless $seen{$target->name}++; 226 push @{ $rec{changes} } => $arg; 227 } elsif ($config->get( key => "target.$arg.uri") || URI->new($arg)->isa('URI::db')) { 228 # A target. Instantiate and keep for subsequente change searches. 229 $target = App::Sqitch::Target->new( sqitch => $sqitch, name => $arg ); 230 push @{ $rec{targets} } => $target unless $seen{$target->name}++; 231 } elsif ($engines{$arg}) { 232 # An engine. Add its target. 233 my $name = $config->get(key => "engine.$arg.target") || "db:$arg:"; 234 $target = App::Sqitch::Target->new( sqitch => $sqitch, name => $name ); 235 push @{ $rec{targets} } => $target unless $seen{$target->name}++; 236 } elsif (-e $arg) { 237 # Maybe it's a plan file? 238 %target_for = map { 239 $_->plan_file => $_ 240 } reverse App::Sqitch::Target->all_targets(sqitch => $sqitch) unless %target_for; 241 if ($target_for{$arg}) { 242 # It *is* a plan file. 243 $target = $target_for{$arg}; 244 push @{ $rec{targets} } => $target unless $seen{$target->name}++; 245 } else { 246 # Nah, who knows. 247 push @{ $rec{unknown} } => $arg; 248 } 249 } else { 250 # Who knows? 251 push @{ $rec{unknown} } => $arg; 252 } 253 } 254 255 # Make sure we have the default target 256 push @{ $rec{targets} } => $target 257 if $target && !$p{no_default} && !@{ $rec{targets} }; 258 259 # Replace missing names with unnknown values. 260 my @names = map { $_ || shift @{ $rec{unknown} } } @{ $p{names} || [] }; 261 262 # Die on unknowns. 263 if (my @unknown = @{ $rec{unknown} } ) { 264 hurl $self->command => __nx( 265 'Unknown argument "{arg}"', 266 'Unknown arguments: {arg}', 267 scalar @unknown, 268 arg => join ', ', @unknown 269 ); 270 } 271 272 # Figure out what targets to access. Use default unless --all. 273 my @targets = @{ $rec{targets} }; 274 if ($p{all}) { 275 # Got --all. 276 hurl $self->command => __( 277 'Cannot specify both --all and engine, target, or plan arugments' 278 ) if @targets; 279 @targets = App::Sqitch::Target->all_targets( sqitch => $sqitch ); 280 } elsif (!@targets) { 281 # Use all if tag.all is set, otherwise just the default. 282 my $key = $self->command . '.all'; 283 @targets = $self->sqitch->config->get(key => $key, as => 'bool') 284 ? App::Sqitch::Target->all_targets( sqitch => $sqitch ) 285 : ($self->default_target); 286 } 287 288 return (@names, \@targets, $rec{changes}); 289} 290 2911; 292 293__END__ 294 295=head1 Name 296 297App::Sqitch::Command - Sqitch Command support 298 299=head1 Synopsis 300 301 my $cmd = App::Sqitch::Command->load( deploy => \%params ); 302 $cmd->run; 303 304=head1 Description 305 306App::Sqitch::Command is the base class for all Sqitch commands. 307 308=head1 Interface 309 310=head2 Constants 311 312=head3 C<ENGINES> 313 314Returns the list of supported engines, currently: 315 316=over 317 318=item * C<firebird> 319 320=item * C<mysql> 321 322=item * C<oracle> 323 324=item * C<pg> 325 326=item * C<sqlite> 327 328=item * C<vertica> 329 330=back 331 332=head2 Class Methods 333 334=head3 C<options> 335 336 my @spec = App::Sqitch::Command->options; 337 338Returns a list of L<Getopt::Long> options specifications. When C<load> loads 339the class, any options passed to the command will be parsed using these 340values. The keys in the resulting hash will be the first part of each option, 341with dashes converted to underscores. This hash will be passed to C<configure> 342along with a L<App::Sqitch::Config> object for munging into parameters to be 343passed to the constructor. 344 345Here's an example excerpted from the C<config> command: 346 347 sub options { 348 return qw( 349 get 350 unset 351 list 352 global 353 system 354 config-file=s 355 ); 356 } 357 358This will result in hash keys with the same names as each option except for 359C<config-file=s>, which will be named C<config_file>. 360 361=head3 C<configure> 362 363 my $params = App::Sqitch::Command->configure($config, $options); 364 365Takes two arguments, an L<App::Sqitch::Config> object and the hash of 366command-line options as specified by C<options>. The returned hash should be 367the result of munging these two objects into a hash reference of parameters to 368be passed to the command subclass constructor. 369 370By default, this method converts dashes to underscores in command-line options 371keys, and then merges the configuration values with the options, with the 372command-line options taking priority. You may wish to override this method to 373do something different. 374 375=head2 Constructors 376 377=head3 C<load> 378 379 my $cmd = App::Sqitch::Command->load( \%params ); 380 381A factory method for instantiating Sqitch commands. It loads the subclass for 382the specified command, uses the options returned by C<options> to parse 383command-line options, calls C<configure> to merge configuration with the 384options, and finally calls C<new> with the resulting hash. Supported parameters 385are: 386 387=over 388 389=item C<sqitch> 390 391The App::Sqitch object driving the whole thing. 392 393=item C<config> 394 395An L<App::Sqitch::Config> representing the current application configuration 396state. 397 398=item C<command> 399 400The name of the command to be executed. 401 402=item C<args> 403 404An array reference of command-line arguments passed to the command. 405 406=back 407 408=head3 C<new> 409 410 my $cmd = App::Sqitch::Command->new(%params); 411 412Instantiates and returns a App::Sqitch::Command object. This method is not 413designed to be overridden by subclasses; they should implement 414L<C<BUILDARGS>|Moo::Manual::Construction/BUILDARGS> or 415L<C<BUILD>|Moo::Manual::Construction/BUILD>, instead. 416 417=head2 Accessors 418 419=head3 C<sqitch> 420 421 my $sqitch = $cmd->sqitch; 422 423Returns the L<App::Sqitch> object that instantiated the command. Commands may 424access its properties in order to manage global state. 425 426=head2 Overridable Instance Methods 427 428These methods should be overridden by all subclasses. 429 430=head3 C<execute> 431 432 $cmd->execute; 433 434Executes the command. This is the method that does the work of the command. 435Must be overridden in all subclasses. Dies if the method is not overridden for 436the object on which it is called, or if it is called against a base 437App::Sqitch::Command object. 438 439=head3 C<command> 440 441 my $command = $cmd->command; 442 443The name of the command. Defaults to the last part of the package name, so as 444a rule you should not need to override it, since it is that string that Sqitch 445uses to find the command class. 446 447=head2 Utility Instance Methods 448 449These methods are mainly provided as utilities for the command subclasses to 450use. 451 452=head3 C<default_target> 453 454 my $target = $cmd->default_target; 455 456This method returns the default target. It should only be used by commands 457that don't use a C<parse_args()> to find and load a target. 458 459This method should always return a target option, never C<undef>. If the 460C<--engine> option or C<core.engine> configuration option has been set, then 461the target will support that engine. In the latter case, if 462C<engine.$engine.target> is set, that value will be used. Otherwise, the 463returned target will have a URI of C<db:> and no associated engine; the 464C<engine> method will throw an exception. This behavior should be fine for 465commands that don't need to load the engine. 466 467=head3 C<parse_args> 468 469 my ($name1, $name2, $targets, $changes) = $cmd->parse_args( 470 names => \@names, 471 target => $target_name, 472 args => \@args 473 ); 474 475Examines each argument to determine whether it's a known change spec or 476identifies a target. Unrecognized arguments will replace false values in the 477C<names> array reference. Any remaining unknown arguments will trigger an 478error. 479 480Returns a list consisting all the desired names, followed by an array 481reference of target objects and an array reference of change specs. 482 483This method is useful for commands that take a number of arguments where the 484order may be mixed. 485 486The supported parameters are: 487 488=over 489 490=item C<args> 491 492An array reference of the command arguments. 493 494=item C<target> 495 496The name of a target, if any. Useful for commands that offer their own 497C<--target> option. This target will be the default target, and the first 498returned in the targets array. 499 500=item C<names> 501 502An array reference of names. If any is false, its place will be taken by an 503otherwise unrecognized argument. The number of values in this array reference 504determines the number of values returned as names in the return values. Such 505values may still be false or undefined; it's up to the caller to decide what 506to do about that. 507 508=item C<all> 509 510In the event that no targets are recognized (or changes that implicitly 511recognize the default target), if this parameter is true, then all known 512targets from the configuration will be returned. 513 514=item C<no_changes> 515 516If true, the parser will not check to see if any argument corresponds to a 517change. The last value returned will be C<undef> instead of the usual array 518reference. Any argument that might have been recognized as a change will 519instead be included in either the C<targets> array -- if it's recognized as a 520target -- or used to set names to return. Any remaining are considered 521unknown arguments and will result in an exception. 522 523=item C<no_default> 524 525If true, no default target will be returned, even if no other targets are 526found. See below for details. 527 528=back 529 530If a target parameter is passed, it will always be instantiated and returned 531as the first item in the "target" array, and arguments recognized as changes 532in the plan associated with that target will be returned as changes. 533 534If no target is passed or appears in the arguments, a default target will be 535instantiated based on the command-line options and configuration -- unless the 536C<no_default> parameter is true. Unlike the target returned by 537C<default_target>, this target B<must> have an associated engine specified by 538the C<--engine> option or configuration. This is on the assumption that it 539will be used by commands that require an engine to do their work. Of course, 540any changes must be recognized from the plan associated with this target. 541 542Changes are only recognized if they're found in the plan of the target that 543precedes them. If no target precedes them, the target specified by the 544C<target> parameter or the default target will be searched. Such changes can 545be specified in any way documented in L<sqitchchanges>. 546 547Targets may be recognized by any one of these types of arguments: 548 549=over 550 551=item * Target Name 552 553=item * Database URI 554 555=item * Engine Name 556 557=item * Plan File 558 559=back 560 561In the case of plan files, C<parse_args()> will return the first target it 562finds for that plan file, even if multiple targets use the same plan file. The 563order of precedence for this determination is the default project target, 564followed by named targets, then engine targets. 565 566=head3 C<run> 567 568 $cmd->run('echo hello'); 569 570Runs a system command and waits for it to finish. Throws an exception on 571error. 572 573=head3 C<capture> 574 575 my @files = $cmd->capture(qw(ls -lah)); 576 577Runs a system command and captures its output to C<STDOUT>. Returns the output 578lines in list context and the concatenation of the lines in scalar context. 579Throws an exception on error. 580 581=head3 C<probe> 582 583 my $git_version = $cmd->capture(qw(git --version)); 584 585Like C<capture>, but returns just the C<chomp>ed first line of output. 586 587=head3 C<verbosity> 588 589 my $verbosity = $cmd->verbosity; 590 591Returns the verbosity level. 592 593=head3 C<trace> 594 595Send trace information to C<STDOUT> if the verbosity level is 3 or higher. 596Trace messages will have C<trace: > prefixed to every line. If it's lower than 5973, nothing will be output. 598 599=head3 C<debug> 600 601 $cmd->debug('Found snuggle in the crib.'); 602 603Send debug information to C<STDOUT> if the verbosity level is 2 or higher. 604Debug messages will have C<debug: > prefixed to every line. If it's lower than 6052, nothing will be output. 606 607=head3 C<info> 608 609 $cmd->info('Nothing to deploy (up-to-date)'); 610 611Send informational message to C<STDOUT> if the verbosity level is 1 or higher, 612which, by default, it is. Should be used for normal messages the user would 613normally want to see. If verbosity is lower than 1, nothing will be output. 614 615=head3 C<comment> 616 617 $cmd->comment('On database flipr_test'); 618 619Send comments to C<STDOUT> if the verbosity level is 1 or higher, which, by 620default, it is. Comments have C<# > prefixed to every line. If verbosity is 621lower than 1, nothing will be output. 622 623=head3 C<emit> 624 625 $cmd->emit('core.editor=emacs'); 626 627Send a message to C<STDOUT>, without regard to the verbosity. Should be used 628only if the user explicitly asks for output, such as for 629C<sqitch config --get core.editor>. 630 631=head3 C<vent> 632 633 $cmd->vent('That was a misage.'); 634 635Send a message to C<STDERR>, without regard to the verbosity. Should be used 636only for error messages to be printed before exiting with an error, such as 637when reverting failed changes. 638 639=head3 C<page> 640 641 $sqitch->page('Search results:'); 642 643Like C<emit()>, but sends the output to a pager handle rather than C<STDOUT>. 644Unless there is no TTY (such as when output is being piped elsewhere), in 645which case it I<is> sent to C<STDOUT>. Meant to be used to send a lot of data 646to the user at once, such as when display the results of searching the event 647log: 648 649 $iter = $sqitch->engine->search_events; 650 while ( my $change = $iter->() ) { 651 $cmd->page(join ' - ', @{ $change }{ qw(change_id event change) }); 652 } 653 654=head3 C<warn> 655 656 $cmd->warn('Could not find nerble; using nobble instead.'); 657 658Send a warning messages to C<STDERR>. Warnings will have C<warning: > prefixed 659to every line. Use if something unexpected happened but you can recover from 660it. 661 662=head3 C<usage> 663 664 $cmd->usage('Missing "value" argument'); 665 666Sends the specified message to C<STDERR>, followed by the usage sections of 667the command's documentation. Those sections may be named "Name", "Synopsis", 668or "Options". Any or all of these will be shown. The doc used to display them 669will be the first found of: 670 671=over 672 673=item C<sqitch-$command-usage> 674 675=item C<sqitch-$command> 676 677=item C<sqitch> 678 679=item C<App::Sqitch::Command::$command> 680 681=item C<App::Sqitch::Command> 682 683=back 684 685For an ideal usage messages, C<sqitch-$command-usage.pod> should be created by 686all command subclasses. 687 688=head1 See Also 689 690=over 691 692=item L<sqitch> 693 694The Sqitch command-line client. 695 696=back 697 698=head1 Author 699 700David E. Wheeler <david@justatheory.com> 701 702=head1 License 703 704Copyright (c) 2012-2015 iovation Inc. 705 706Permission is hereby granted, free of charge, to any person obtaining a copy 707of this software and associated documentation files (the "Software"), to deal 708in the Software without restriction, including without limitation the rights 709to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 710copies of the Software, and to permit persons to whom the Software is 711furnished to do so, subject to the following conditions: 712 713The above copyright notice and this permission notice shall be included in all 714copies or substantial portions of the Software. 715 716THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 717IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 718FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 719AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 720LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 721OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 722SOFTWARE. 723 724=cut 725 726