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':