diff --git a/.env.tric b/.env.tric index b1e605a..b4ec16f 100644 --- a/.env.tric +++ b/.env.tric @@ -19,6 +19,8 @@ CLI_VERBOSITY=0 # TRIC_CURRENT_PROJECT=the-events-calendar # When you `here` at the site level, all selected targets via `use` will have a relative path set. # TRIC_CURRENT_PROJECT_RELATIVE_PATH= +# When you `use` on a supported subdirectory of a plugin, this stores the subdirectory name. +#TRIC_CURRENT_PROJECT_SUBDIR= # The GitHub handle of the company to clone plugins from. TRIC_GITHUB_COMPANY_HANDLE=moderntribe diff --git a/.gitignore b/.gitignore index 5d79395..0a2df5e 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ _plugins # Any .env.tric.* file created to override the tric cli tool configuration or to configure the runs. .env.tric.local .env.tric.run + +# Any .build-version file created by the tric cli tool. +.build-version diff --git a/changelog.md b/changelog.md new file mode 100644 index 0000000..c95ee84 --- /dev/null +++ b/changelog.md @@ -0,0 +1,37 @@ +# Changelog +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.3.0] - TBD +### Added + +- Prompt to `tric update` when container build version are out of sync from the tric version. +- Output npm error log when one is generated. + +### Changed + +- Adjust pathing of subdirectories within the tric stack so that npm can find a `.git` directory when performing `npm install`. +- Suppress the `fixuid` command output in the npm `docker-entrypoint.sh`. +- Separated out poolable (passive) command functions from realtime command functions to prevent issues with interactivity. + +## [0.2.0] - 2020-06-24 +### Added + +- Added phpcs and phpcbf commands. +- Added parallel processing of commands. + +### Changed + +- Changed `tric build` to `tric build-stack`. + +## [0.1.1] - 2020-05-26 +### Changed + +- Ensure `.htaccess` file is present in `_wordpress`. + +## [0.1.0] - 2020-05-25 +### Added + +- Initial version diff --git a/containers/npm/docker-entrypoint.sh b/containers/npm/docker-entrypoint.sh index 62fb168..3e4da13 100644 --- a/containers/npm/docker-entrypoint.sh +++ b/containers/npm/docker-entrypoint.sh @@ -3,6 +3,19 @@ # This file is just a proxy to call the `npm` binary that will, but, take care of fixing file mode issues before. # If the `FIXUID` env var is set to `1`, default value, then fix UIDs. -test "${FIXUID:-1}" != "0" && eval "$( fixuid )" +test "${FIXUID:-1}" != "0" && eval "$( fixuid > /dev/null 2>&1 )" -npm --prefix /project "$@" +cd /project/${TRIC_CURRENT_PROJECT_SUBDIR} + +npm "$@" + +# Output error logs if present. +if compgen -G "/home/node/.npm/_logs/*.log" > /dev/null; then + echo "---------------------------------------" + echo "Error log found. Here are the contents (excluding the overly verbose saveTree lines):" + echo "---------------------------------------" + cat /home/node/.npm/_logs/*.log | grep -v "saveTree" + echo "---------------------------------------" + echo "End of error log" + echo "---------------------------------------" +fi diff --git a/src/docker.php b/src/docker.php index 55e60c9..e03409f 100644 --- a/src/docker.php +++ b/src/docker.php @@ -164,10 +164,11 @@ function tric_stack_array() { * Executes a docker-compose command in real time, printing the output as produced by the command. * * @param array $options A list of options to initialize the wrapper. + * @param bool $is_realtime Whether the command should be run in real time (true) or passively (false). * * @return \Closure A closure that will run the process in real time and return the process exit status. */ -function docker_compose_realtime( array $options = [] ) { +function docker_compose_process( array $options = [], $is_realtime = true ) { setup_id(); $is_ci = is_ci(); @@ -182,7 +183,7 @@ function docker_compose_realtime( array $options = [] ) { $host_ip = host_ip( 'Linux' ); } - return static function ( array $command = [], $prefix = null ) use ( $options, $host_ip, $is_ci ) { + return static function ( array $command = [], $prefix = null ) use ( $options, $host_ip, $is_ci, $is_realtime ) { $command = 'docker-compose ' . implode( ' ', $options ) . ' ' . implode( ' ', $command ); if ( ! empty( $host_ip ) ) { @@ -195,6 +196,30 @@ function docker_compose_realtime( array $options = [] ) { $command = 'XDE=0 ' . $command; } - return process_realtime( $command, $prefix ); + return $is_realtime ? process_realtime( $command ) : process_passive( $command, $prefix ); }; } + +/** + * Executes a docker-compose command in passive mode, printing the output as produced by the command. + * + * This approach is used for commands that can be run in a parallel or forked process without interactivity. + * + * @param array $options A list of options to initialize the wrapper. + * + * @return \Closure A closure that will run the process in real time and return the process exit status. + */ +function docker_compose_passive( array $options = [] ) { + return docker_compose_process( $options, false ); +} + +/** + * Executes a docker-compose command in real time, printing the output as produced by the command. + * + * @param array $options A list of options to initialize the wrapper. + * + * @return \Closure A closure that will run the process in real time and return the process exit status. + */ +function docker_compose_realtime( array $options = [] ) { + return docker_compose_process( $options, true ); +} diff --git a/src/process.php b/src/process.php index 2b719e0..87f6c80 100644 --- a/src/process.php +++ b/src/process.php @@ -34,12 +34,38 @@ function process( $command ) { /** * Runs a process in realtime, displaying its output. * + * Realtime processes are done without forking, have no need of prefixes, and support interactivity. + * + * @param string $command The command to run. + * @param string|null $prefix The prefix to place before all output. + * + * @return int The process exit status, `0` means ok. + */ +function process_realtime( $command ) { + debug( "Executing command: {$command}" ); + + echo PHP_EOL; + + setup_terminal(); + + $clean_command = escapeshellcmd( $command ); + + passthru( $clean_command, $status ); + + return (int) $status; +} + +/** + * Runs a process passively, displaying its output. + * + * Passive processes are ones that only need to dump their output. + * * @param string $command The command to run. * @param string|null $prefix The prefix to place before all output. * * @return int The process exit status, `0` means ok. */ -function process_realtime( $command, $prefix = null ) { +function process_passive( $command, $prefix = null ) { debug( "Executing command: {$command}" ); echo PHP_EOL; diff --git a/src/tric.php b/src/tric.php index f622d76..f3342e5 100644 --- a/src/tric.php +++ b/src/tric.php @@ -178,16 +178,20 @@ function setup_tric_env( $root_dir ) { * return value will always be a non empty string. */ function tric_target( $require = true ) { - $using = getenv( 'TRIC_CURRENT_PROJECT' ); + $using = getenv( 'TRIC_CURRENT_PROJECT' ); + $using_subdir = getenv( 'TRIC_CURRENT_PROJECT_SUBDIR' ); + $using_full = $using . ( $using_subdir ? '/' . $using_subdir : '' ); + if ( $require ) { - return $using; + return $using_full; } - if ( empty( $using ) ) { + + if ( empty( $using_full ) ) { echo magenta( "Use target not set; use the 'use' sub-command to set it.\n" ); exit( 1 ); } - return trim( $using ); + return trim( $using_full ); } /** @@ -199,14 +203,20 @@ function tric_switch_target( $target ) { $root = root(); $run_settings_file = "{$root}/.env.tric.run"; $target_relative_path = ''; + $subdir = ''; if ( tric_here_is_site() ) { $target_relative_path = get_target_relative_path( $target ); } + if ( false !== strpos( $target, '/' ) ) { + list( $target, $subdir ) = explode( '/', $target ); + } + $env_values = [ 'TRIC_CURRENT_PROJECT' => $target, 'TRIC_CURRENT_PROJECT_RELATIVE_PATH' => $target_relative_path, + 'TRIC_CURRENT_PROJECT_SUBDIR' => $subdir, ]; write_env_file( $run_settings_file, $env_values, true ); @@ -416,6 +426,17 @@ function github_company_handle() { return ! empty( $handle ) ? trim( $handle ) : 'moderntribe'; } +/** + * Runs a process in passive mode in tric stack and returns the exit status. + * + * This approach is used when running commands that can be done in parallel or forked processes. + * + * @return \Closure The process closure to start a real-time process using tric stack. + */ +function tric_passive() { + return docker_compose_passive( tric_stack_array() ); +} + /** * Runs a process in tric stack and returns the exit status. * @@ -446,10 +467,18 @@ function teardown_stack() { */ function rebuild_stack() { echo "Building the stack images...\n\n"; - tric_realtime()( [ 'build-stack' ] ); + tric_realtime()( [ 'build' ] ); + write_build_version(); echo light_cyan( "\nStack images built.\n\n" ); } +/** + * Write the current CLI_VERSION to the build-version file + */ +function write_build_version() { + file_put_contents( TRIC_ROOT_DIR . '/.build-version', CLI_VERSION ); +} + /** * Prints information about tric tool. */ @@ -765,7 +794,7 @@ function build_command_pool( string $base_command, array $command, array $sub_di $prefix = "{$base_command}:" . yellow( $target ); } - $status = tric_realtime()( array_merge( [ 'run', '--rm', $base_command ], $command ), $prefix ); + $status = tric_passive()( array_merge( [ 'run', '--rm', $base_command ], $command ), $prefix ); if ( 'target' !== $target ) { tric_switch_target( $using ); @@ -918,3 +947,37 @@ function switch_plugin_branch( $branch, $plugin = null ) { exit( 1 ); } } + +/** + * If tric stack is out of date, prompt for an execution of tric update. + */ +function maybe_prompt_for_update() { + $build_version = '0.0.1'; + $cli_version = CLI_VERSION; + + if ( file_exists( TRIC_ROOT_DIR . '/.build-version' ) ) { + $build_version = file_get_contents( TRIC_ROOT_DIR . '/.build-version' ); + } + + // If there isn't a .env.tric.run, this is likely a fresh install. Bail. + if ( ! file_exists( TRIC_ROOT_DIR . '/.env.tric.run' ) ) { + return; + } + + // If the version of the CLI is the same as the most recently built version, bail. + if ( version_compare( $build_version, $cli_version, '=' ) ) { + return; + } + + echo magenta( "\n****************************************************************\n\n" ); + echo yellow( " ____________ ____ __\n" ); + echo yellow( " | ____\ \ / / | |\n" ); + echo yellow( " | |__ \ \/ / | |\n" ); + echo yellow( " | __| \_ _/ | |\n" ); + echo yellow( " | | | | | |\n" ); + echo yellow( " |__| |__| |__|\n\n" ); + echo magenta( "Your tric containers are not up to date with the latest version.\n" ); + echo magenta( " To update, please run:\n\n" ); + echo yellow( " tric update\n\n" ); + echo magenta( "****************************************************************\n" ); +} diff --git a/src/utils.php b/src/utils.php index c5c82df..46578e1 100644 --- a/src/utils.php +++ b/src/utils.php @@ -407,6 +407,11 @@ function write_env_file( $file, array $lines = [], $update = false ) { return "{$key}={$value}"; }, array_keys( $new_lines ), $new_lines ) ); + // If this is the first time creating the .env.tric.run file, assume this is the first run and place the CLI version in `.build-version`. + if ( false !== strpos( $file, '.env.tric.run' ) && ! file_exists( $file ) ) { + write_build_version(); + } + $put = file_put_contents( $file, $data ); if ( false === $put ) { diff --git a/src/wordpress.php b/src/wordpress.php index c526e3b..df7bd67 100644 --- a/src/wordpress.php +++ b/src/wordpress.php @@ -81,7 +81,7 @@ static function ( SplFileInfo $file ) { } ); - $allowed_subdirs = [ 'common' ]; + $allowed_subdirs = get_allowed_use_subdirectories(); foreach ( iterator_to_array( $dir ) as $key => $value ) { $basename = basename( $key ); $dirs[ $basename ] = $value; @@ -95,3 +95,12 @@ static function ( SplFileInfo $file ) { return $dirs; } + +/** + * Returns the list of allowed subdirectories for tric use. + * + * @return array Allowed subdirectories for use. + */ +function get_allowed_use_subdirectories() { + return [ 'common' ]; +} diff --git a/tric b/tric index 124688b..e294854 100755 --- a/tric +++ b/tric @@ -14,6 +14,7 @@ use function Tribe\Test\args; use function Tribe\Test\colorize; use function Tribe\Test\root; use function Tribe\Test\light_cyan; +use function Tribe\Test\maybe_prompt_for_update; use function Tribe\Test\setup_tric_env; // Set up the argument parsing function. @@ -23,7 +24,7 @@ $args = args( [ ] ); $cli_name = basename( $argv[0] ); -const CLI_VERSION = '0.2.0'; +const CLI_VERSION = '0.3.0'; $cli_header = implode( ' - ', [ light_cyan( $cli_name ) . ' version ' . light_cyan( CLI_VERSION ), @@ -90,10 +91,15 @@ $subcommand = $args( 'subcommand', 'help' ); $cli_name = basename( $argv[0] ); +if ( ! in_array( $subcommand, [ 'help', 'update'] ) ) { + maybe_prompt_for_update(); +} + switch ( $subcommand ) { default: case 'help': echo $help_message; + maybe_prompt_for_update(); break; case 'airplane-mode': case 'build-prompt': diff --git a/tric-stack.yml b/tric-stack.yml index 84547f7..4d16580 100644 --- a/tric-stack.yml +++ b/tric-stack.yml @@ -146,7 +146,7 @@ services: # XDH=$(ip route | grep docker0 | awk '{print $9}') docker-compose ... XDEBUG_CONFIG: "idekey=${XDK:-tric} remote_enable=${XDE:-1} remote_host=${XDH:-host.docker.internal} remote_port=${XDP:-9001}" # Move to the target directory before running the command from the plugins directory. - CODECEPTION_PROJECT_DIR: /var/www/html/wp-content/plugins/${TRIC_CURRENT_PROJECT:-test} + CODECEPTION_PROJECT_DIR: /var/www/html/wp-content/plugins/${TRIC_CURRENT_PROJECT:-test}/${TRIC_CURRENT_PROJECT_SUBDIR} # When running the container in shell mode (using the tric `shell` command), then use this CC configuration. CODECEPTION_SHELL_CONFIG: "-c codeception.tric.yml" # After the WordPress container comes online, wait a further 3s to give it some boot-up time. @@ -180,7 +180,7 @@ services: FIXUID: "${FIXUID:-1}" volumes: # Set the current plugin as project. - - ${TRIC_PLUGINS_DIR}/${TRIC_CURRENT_PROJECT:-test}:/project:cached + - ${TRIC_PLUGINS_DIR}/${TRIC_CURRENT_PROJECT:-test}/${TRIC_CURRENT_PROJECT_SUBDIR}:/project:cached # Share SSH keys with the container to pull from private repositories. - ${DOCKER_RUN_SSH_AUTH_SOCK}:/ssh-agent:ro @@ -191,6 +191,7 @@ services: user: "${DOCKER_RUN_UID:-}:${DOCKER_RUN_GID:-}" environment: FIXUID: ${FIXUID:-1} + TRIC_CURRENT_PROJECT_SUBDIR: ${TRIC_CURRENT_PROJECT_SUBDIR} volumes: # Set the current plugin as project. - ${TRIC_PLUGINS_DIR}/${TRIC_CURRENT_PROJECT:-test}:/project:cached @@ -205,7 +206,7 @@ services: working_dir: /project volumes: # Set the current plugin as project. - - ${TRIC_PLUGINS_DIR}/${TRIC_CURRENT_PROJECT:-test}:/project:cached + - ${TRIC_PLUGINS_DIR}/${TRIC_CURRENT_PROJECT:-test}/${TRIC_CURRENT_PROJECT_SUBDIR}:/project:cached # Share SSH keys with the container to pull from private repositories. - ${DOCKER_RUN_SSH_AUTH_SOCK}:/ssh-agent:ro