diff --git a/changelog.md b/changelog.md index afb10a6..e90c857 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.5.0] - 2020-07-30 +### Added + +- Added the `tric target` command to support running the same command against multiple targets. + ## [0.4.9] - 2020-07-07 ### Changed diff --git a/src/commands/target.php b/src/commands/target.php new file mode 100644 index 0000000..1d637ad --- /dev/null +++ b/src/commands/target.php @@ -0,0 +1,60 @@ +tric target\n" ); + + return; +} + +$targets = []; +do { + $last_target = ask( 'Target (return when done): ', null ); + if ( $last_target && ensure_valid_target( $last_target, false ) ) { + $targets[] = $last_target; + } else { + continue; + } +} while ( $last_target ); + +$targets = array_unique( $targets ); + +echo yellow( "\nTargets: " ) . implode( ', ', $targets ) . "\n\n"; + +// Allow users to enter a command prefixing it with `tric` or not. +do { + $command_line = trim( + preg_replace( '/^\\s*tric/', '', ask( 'Command: ' ) + ) + ); +} while ( ! $command_line ); + +echo "\n"; + +if ( preg_match( '/^n/i', ask( + colorize( + sprintf( + "Are you sure you want to run %s on %s?", + $command_line, + implode( ', ', $targets ) + ) + ), + 'yes' +) ) ) { + echo "\nDone!"; + + return; +} + +$command = preg_split( '/\\s/', $command_line ); +$base_command = array_shift( $command ); + +exit( execute_command_pool( build_targets_command_pool( $targets, $base_command, $command, [ 'common' ] ) ) ); diff --git a/src/tric.php b/src/tric.php index 9500c8c..6094e6b 100644 --- a/src/tric.php +++ b/src/tric.php @@ -23,8 +23,12 @@ function tric_here_is_site() { * - If tric here was done on the site level, "site" is also a valid target. * * @param string $target The target to check in the valid list of targets. + * @param bool $exit Whether to exit if the target is invalid, or to return `false`. + * + * @return string|false $target The validated target or `false` to indicate the target is not valid if the `$exit` + * parameter is set to `false`. */ -function ensure_valid_target( $target ) { +function ensure_valid_target( $target, $exit = true ) { $targets_str = ''; $plugins = array_keys( dev_plugins() ); $themes = array_keys( dev_themes() ); @@ -50,13 +54,23 @@ function ensure_valid_target( $target ) { if ( false === $target ) { echo magenta( "This command needs a target argument; available targets are:\n${targets_str}\n" ); - exit( 1 ); + if ( $exit ) { + exit( 1 ); + } + + return false; } if ( ! in_array( $target, $targets, true ) ) { echo magenta( "'{$target}' is not a valid target; available targets are:\n${targets_str}\n" ); - exit( 1 ); + if ( $exit ) { + exit( 1 ); + } + + return false; } + + return $target; } /** @@ -783,22 +797,26 @@ function maybe_build_install_command_pool( $base_command, $target, array $sub_di * If any subdirectories are provided and are available in the target, then the user will be prompted to run the same * command on those subdirectories. * - * @param string $base_command The base service command to run, e.g. `npm`, `composer`, etc. - * @param array $command The command to run, e.g. `['install','--save-dev']` in array format. + * @param string $base_command The base service command to run, e.g. `npm`, `composer`, etc. + * @param array $command The command to run, e.g. `['install','--save-dev']` in array format. * @param array $sub_directories Sub directories to prompt for additional execution. + * @param string $using An optional target to use in place of the specified one. * - * @return int Result of command execution. + * @return array The built command pool. */ -function build_command_pool( string $base_command, array $command, array $sub_directories = [] ) { - $using = tric_target(); +function build_command_pool( $base_command, array $command, array $sub_directories = [], $using = null ) { + $using_alias = $using; + $using = $using ?: tric_target(); $targets = [ 'target' ]; // Prompt for execution within subdirectories if enabled. if ( getenv( 'TRIC_BUILD_SUBDIR' ) ) { foreach ( $sub_directories as $dir ) { + $dir_name = $using_alias ? "{$using_alias}/{$dir}" : $dir; + $question = "\nWould you also like to run that {$base_command} command against {$dir_name}?"; if ( file_exists( tric_plugins_dir( "{$using}/{$dir}" ) ) - && ask( "\nWould you also like to run that {$base_command} command against {$dir}?", 'yes' ) + && ask( $question, 'yes' ) ) { $targets[] = $dir; } @@ -806,13 +824,15 @@ function build_command_pool( string $base_command, array $command, array $sub_di } // Build the command process. - $command_process = static function( $target, $subnet = '') use ( $using, $base_command, $command, $sub_directories ) { - $prefix = "{$base_command}:" . light_cyan( $target ); + $command_process = static function( $target, $subnet = '' ) use ( $using, $using_alias, $base_command, $command, $sub_directories ) { + $target_name = $using_alias ?: $target; + $prefix = "{$base_command}:" . light_cyan( $target_name ); // Execute command as the parent. if ( 'target' !== $target ) { tric_switch_target( "{$using}/{$target}" ); - $prefix = "{$base_command}:" . yellow( $target ); + $sub_target_name = $using_alias ? "{$using_alias}/{$target}" : $target; + $prefix = "{$base_command}:" . yellow( $sub_target_name ); } putenv( "TRIC_TEST_SUBNET={$subnet}" ); @@ -1137,3 +1157,51 @@ function build_subdir_status() { echo 'Sub-directories build status is: ' . ( $enabled ? light_cyan( 'on' ) : magenta( 'off' ) ) . PHP_EOL; } + +/** + * Build a command pool, suitable to be run using the `execute_command_pool` function, for multiple targets. + * + * If any subdirectories are provided and are available in the target, then the user will be prompted to run the same + * command on those subdirectories. + * + * @param array $targets An array of targets for the command pool; note the targets are NOT validated by + * this function and the validation should be done by the calling code. + * @param string $base_command The base service command to run, e.g. `npm`, `composer`, etc. + * @param array $command The command to run, e.g. `['install','--save-dev']` in array format. + * @param array $sub_directories Sub directories to prompt for additional execution. + * + * @return array The built command pool for all the targets. + */ +function build_targets_command_pool( array $targets, $base_command, array $command, array $sub_directories = [] ) { + $raw_command_pool = array_combine( + $targets, + array_map( static function ( $target ) use ( $base_command, $command, $sub_directories ) { + return build_command_pool( $base_command, $command, $sub_directories, $target ); + }, $targets ) + ); + + // Set the keys correctly to have the command prefixes correctly built. + $command_pool = []; + foreach ( $raw_command_pool as $target => $target_pool ) { + foreach ( $target_pool as $target_key => $process ) { + $key = preg_replace( + [ + // Main target. + '/^target:/', + // Sub-directories. + '/^([\w\d]+):/' + ], + [ + // Replace with `:`. + $target . ':', + // Replace with `/:`. + $target . '/$1:' + ], + $target_key + ); + $command_pool[ $key ] = $process; + } + } + + return $command_pool; +} diff --git a/tric b/tric index 9c3a8b3..4373ef2 100755 --- a/tric +++ b/tric @@ -25,7 +25,7 @@ $args = args( [ ] ); $cli_name = basename( $argv[0] ); -const CLI_VERSION = '0.4.9'; +const CLI_VERSION = '0.5.0'; $cli_header = implode( ' - ', [ light_cyan( $cli_name ) . ' version ' . light_cyan( CLI_VERSION ), @@ -53,6 +53,7 @@ Available commands: init Initializes a plugin for use in tric. composer Runs a Composer command in the stack. npm Runs an npm command in the stack. +target Runs a command on a set of targets. xdebug Activates and deactivates XDebug in the stack, returns the current XDebug status or sets its values. airplane-mode Activates or deactivates the airplane-mode plugin. cache Activates and deactivates object cache support, returns the current object cache status. @@ -133,6 +134,7 @@ switch ( $subcommand ) { case 'run': case 'serve': case 'shell': + case 'target': case 'up': case 'update': case 'upgrade':