diff --git a/qit b/qit index 5eeddabf..f4be07d3 100755 Binary files a/qit and b/qit differ diff --git a/src/src/Commands/Environment/DownEnvironmentCommand.php b/src/src/Commands/Environment/DownEnvironmentCommand.php index b8bf21ac..520c6ca7 100644 --- a/src/src/Commands/Environment/DownEnvironmentCommand.php +++ b/src/src/Commands/Environment/DownEnvironmentCommand.php @@ -9,6 +9,7 @@ use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\NullOutput; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\ChoiceQuestion; use function QIT_CLI\format_elapsed_time; @@ -43,44 +44,58 @@ protected function execute( InputInterface $input, OutputInterface $output ): in return Command::SUCCESS; } - if ( count( $running_environments ) === 1 ) { - $this->stop_environment( array_shift( $running_environments ), $output ); + $selected_environment = null; - return Command::SUCCESS; + if ( count( $running_environments ) === 1 ) { + $selected_environment = array_shift( $running_environments ); } - $environment_choices = array_map( function ( EnvInfo $environment ) { - return sprintf( 'Created: %s, Status: %s', - format_elapsed_time( time() - $environment->created_at ), - $environment->status ); - }, $running_environments ); - - $environment_choices['all'] = 'Stop all environments'; - - // More than one environment running, let user choose which one to stop. - $helper = new QuestionHelper(); - $question = new ChoiceQuestion( - 'Please select the environment to stop (or choose to stop all):', - $environment_choices, - 'all' - ); - $question->setErrorMessage( 'Environment %s is invalid.' ); - - $selected_environment = $helper->ask( $input, $output, $question ); + if ( is_null( $selected_environment ) ) { + $environment_choices = array_map( function ( EnvInfo $environment ) { + return sprintf( 'Created: %s, Status: %s', + format_elapsed_time( time() - $environment->created_at ), + $environment->status ); + }, $running_environments ); + + $environment_choices['all'] = 'Stop all environments'; + + // More than one environment running, let user choose which one to stop. + $helper = new QuestionHelper(); + $question = new ChoiceQuestion( + 'Please select the environment to stop (or choose to stop all):', + $environment_choices, + 'all' + ); + $question->setErrorMessage( 'Environment %s is invalid.' ); + + $selected_environment = $helper->ask( $input, $output, $question ); + } if ( $selected_environment === 'all' ) { + $total_environments = count( $running_environments ); + $counter = 1; + foreach ( $running_environments as $environment ) { - $this->stop_environment( $environment, $output ); + $output->write( "\rStopping all environments... [{$counter}/{$total_environments}]" ); + $this->stop_environment( $environment, $output->isVerbose() ? $output : new NullOutput() ); + ++$counter; } } else { - $this->stop_environment( $this->environment_monitor->get_env_info_by_id( $selected_environment ), $output ); + $total_environments = 1; + if ( ! $selected_environment instanceof EnvInfo ) { + $selected_environment = $this->environment_monitor->get_env_info_by_id( $selected_environment ); + } + $output->write( "\rStopping all environments... [1/1]" ); + $this->stop_environment( $selected_environment, $output->isVerbose() ? $output : new NullOutput() ); } + $output->write( "\rStopped all environments [{$total_environments}/{$total_environments}]." ); + return Command::SUCCESS; } private function stop_environment( EnvInfo $environment, OutputInterface $output ): void { - Environment::down( $environment ); + Environment::down( $environment, $output ); $environment_id = $environment->env_id; $output->writeln( "Environment '$environment_id' stopped." ); } diff --git a/src/src/Commands/Environment/UpEnvironmentCommand.php b/src/src/Commands/Environment/UpEnvironmentCommand.php index 4d32952b..dda52cdc 100644 --- a/src/src/Commands/Environment/UpEnvironmentCommand.php +++ b/src/src/Commands/Environment/UpEnvironmentCommand.php @@ -252,7 +252,9 @@ protected function execute( InputInterface $input, OutputInterface $output ): in $env_info = App::make( EnvConfigLoader::class )->init_env_info( $options_to_env_info ); - $this->output->writeln( json_encode( $env_info, JSON_PRETTY_PRINT ) ); + if ( $output->isVeryVerbose() ) { + $this->output->writeln( 'Environment info: ' . json_encode( $env_info, JSON_PRETTY_PRINT ) ); + } $this->e2e_environment->init( $env_info ); $this->e2e_environment->up(); diff --git a/src/src/Environment/Docker.php b/src/src/Environment/Docker.php index 80e06e88..7ccbff81 100644 --- a/src/src/Environment/Docker.php +++ b/src/src/Environment/Docker.php @@ -113,7 +113,7 @@ public function run_inside_docker( EnvInfo $env_info, array $command, array $env $docker_image = $env_info->get_docker_container( $image ); $docker_command = [ $this->find_docker(), 'exec' ]; - if ( use_tty() ) { + if ( $this->output->isVerbose() && use_tty() ) { $docker_command = array_merge( $docker_command, [ '-it' ] ); } @@ -157,7 +157,7 @@ public function run_inside_docker( EnvInfo $env_info, array $command, array $env } $process = new Process( $docker_command ); - $process->setTty( use_tty() ); + $process->setTty( $this->output->isVerbose() && use_tty() ); $process->setTimeout( $timeout ); $process->setIdleTimeout( $timeout ); @@ -167,7 +167,9 @@ public function run_inside_docker( EnvInfo $env_info, array $command, array $env } $process->run( function ( $type, $buffer ) { - $this->output->write( $buffer ); + if ( $this->output->isVerbose() ) { + $this->output->write( $buffer ); + } } ); if ( ! $process->isSuccessful() ) { diff --git a/src/src/Environment/EnvironmentDanglingCleanup.php b/src/src/Environment/EnvironmentDanglingCleanup.php index 0366c30c..9502e8e5 100644 --- a/src/src/Environment/EnvironmentDanglingCleanup.php +++ b/src/src/Environment/EnvironmentDanglingCleanup.php @@ -76,7 +76,7 @@ public function cleanup_dangling(): void { } if ( ! $this->header_printed ) { - $this->output->writeln( 'Cleaning up dangling temp environments...' ); + $this->output->writeln( 'Cleaning up dangling temporary environments...' ); $this->header_printed = true; } @@ -119,7 +119,7 @@ public function cleanup_dangling(): void { // Skip asking the user permission to delete in this directory if they answer with an "A". $always_delete_from_this_directory = $this->cache->get( 'always_delete_from_this_directory' ); - $parent_dir = $this->get_parent_dir_to_delete(); + $parent_dir = $this->get_parent_dir_to_delete(); if ( $always_delete_from_this_directory !== $parent_dir && ! is_ci() ) { $this->output->writeln( "Found dangling temporary environments in directory: $parent_dir" ); @@ -140,7 +140,7 @@ public function cleanup_dangling(): void { break; case 'n': default: - $this->output->writeln( 'Action cancelled by user.' ); + $this->output->writeln( 'Please delete the dangling environments in that directory manually.' ); return; } @@ -339,12 +339,12 @@ protected function remove_dangling_environments(): void { } if ( ! empty( $containers_not_running ) ) { if ( ! $this->header_printed ) { - $this->output->writeln( 'Dangling Temporary Environments found.' ); + $this->output->writeln( 'Cleaning up dangling temporary environments...' ); $this->header_printed = true; } - $this->output->writeln( "Removing dangling environment: {$env_info->env_id}" ); - if ( $this->output->isVerbose() ) { + $this->debug_output( "Removing dangling environment: {$env_info->env_id}" ); + if ( $this->output->isVeryVerbose() ) { $this->output->writeln( 'Expected containers: ' . implode( ', ', $env_info->docker_images ) ); $this->output->writeln( 'Missing containers: ' . implode( ', ', $containers_not_running ) ); } diff --git a/src/src/Environment/EnvironmentDownloader.php b/src/src/Environment/EnvironmentDownloader.php index 85a9cc4c..7fb28271 100644 --- a/src/src/Environment/EnvironmentDownloader.php +++ b/src/src/Environment/EnvironmentDownloader.php @@ -87,6 +87,15 @@ public function maybe_download( string $env_name ): void { if ( ! $zip->extractTo( $environments_dir . '/' . $env_name ) ) { throw new \RuntimeException( 'Could not extract environment zip.' ); } + + // Ensure .sh files in the extracted directory are executable, just in case. + $iterator = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator( $environments_dir . '/' . $env_name ) ); + foreach ( $iterator as $file ) { + if ( $file->isFile() && $file->getExtension() === 'sh' ) { + chmod( $file->getPathname(), 0755 ); + } + } + $zip->close(); $this->cache->set( "{$env_name}_environment_hash", $manager_hashes[ $env_name ]['checksum'], MONTH_IN_SECONDS ); } else { diff --git a/src/src/Environment/Environments/E2E/E2EEnvironment.php b/src/src/Environment/Environments/E2E/E2EEnvironment.php index b8253789..c75376b3 100644 --- a/src/src/Environment/Environments/E2E/E2EEnvironment.php +++ b/src/src/Environment/Environments/E2E/E2EEnvironment.php @@ -61,7 +61,7 @@ protected function post_up(): void { // Setup WordPress. $this->output->writeln( 'Setting up WordPress...' ); - $this->docker->run_inside_docker( $this->env_info, [ '/bin/bash', '/qit/bin/wordpress-setup.sh' ], [ + $this->docker->run_inside_docker( $this->env_info, [ '/bin/bash', '-c', '/qit/bin/wordpress-setup.sh 2>&1' ], [ 'WORDPRESS_VERSION' => $this->env_info->wordpress_version, 'WOOCOMMERCE_VERSION' => $this->env_info->woocommerce_version, 'PLUGINS_TO_INSTALL' => json_encode( $this->env_info->plugins ), diff --git a/src/src/Environment/Environments/Environment.php b/src/src/Environment/Environments/Environment.php index 76978e5a..a159c76f 100644 --- a/src/src/Environment/Environments/Environment.php +++ b/src/src/Environment/Environments/Environment.php @@ -109,14 +109,20 @@ public function up( bool $attached = false ): void { $this->maybe_create_cache_dir(); $this->copy_environment(); $this->environment_monitor->environment_added_or_updated( $this->env_info ); + if ( ! empty( $this->env_info->plugins ) || ! empty( $this->env_info->themes ) ) { + $this->output->writeln( 'Downloading plugins and themes...' ); + } $this->extension_downloader->download( $this->env_info, $this->cache_dir, $this->env_info->plugins, $this->env_info->themes ); + $this->output->writeln( 'Setting up Docker...' ); $this->generate_docker_compose(); $this->post_generate_docker_compose(); $this->up_docker_compose( $attached ); $this->post_up(); - $this->output->writeln( 'Server started at ' . round( microtime( true ) - $start, 2 ) . ' seconds' ); - $this->output->writeln( "Temporary environment: {$this->env_info->temporary_env}\n" ); + if ( $this->output->isVerbose() ) { + $this->output->writeln( 'Server started in ' . round( microtime( true ) - $start, 2 ) . ' seconds' ); + } + $this->additional_output(); } @@ -194,13 +200,14 @@ protected function generate_docker_compose(): void { 'QIT_DOCKER_REDIS' => 'no', // Default. Might be overridden by the concrete environment. ], $this->get_generate_docker_compose_envs() ) ); - if ( $this->output->isVerbose() ) { + if ( $this->output->isVeryVerbose() ) { $this->output->writeln( $process->getCommandLine() ); - $this->output->writeln( json_encode( $process->getEnv(), JSON_PRETTY_PRINT ) ); } $process->run( function ( $type, $buffer ) { - $this->output->write( $buffer ); + if ( $this->output->isVerbose() || $type === Process::ERR ) { + $this->output->write( $buffer ); + } } ); if ( ! $process->isSuccessful() ) { @@ -233,10 +240,12 @@ protected function up_docker_compose( bool $attached ): void { $up_process->setTimeout( 300 ); $up_process->setIdleTimeout( 300 ); - $up_process->setTty( use_tty() ); + $up_process->setPty( use_tty() ); $up_process->run( function ( $type, $buffer ) { - $this->output->write( $buffer ); + if ( $this->output->isVerbose() ) { + $this->output->write( $buffer ); + } } ); if ( ! $up_process->isSuccessful() ) { @@ -249,8 +258,8 @@ protected function up_docker_compose( bool $attached ): void { $this->environment_monitor->environment_added_or_updated( $this->env_info ); } - public static function down( EnvInfo $env_info ): void { - $output = App::make( OutputInterface::class ); + public static function down( EnvInfo $env_info, ?OutputInterface $output = null ): void { + $output = $output ?? App::make( OutputInterface::class ); $environment_monitor = App::make( EnvironmentMonitor::class ); if ( ! file_exists( $env_info->temporary_env ) ) { @@ -267,7 +276,7 @@ public static function down( EnvInfo $env_info ): void { $down_process = new Process( array_merge( App::make( Docker::class )->find_docker_compose(), [ '-f', $env_info->temporary_env . '/docker-compose.yml', 'down' ] ) ); $down_process->setTimeout( 300 ); $down_process->setIdleTimeout( 300 ); - $down_process->setTty( use_tty() ); + $down_process->setPty( use_tty() ); $down_process->run( static function ( $type, $buffer ) use ( $output ) { $output->write( $buffer ); diff --git a/src/src/Environment/ExtensionDownload/ExtensionZip.php b/src/src/Environment/ExtensionDownload/ExtensionZip.php index 7a4ce7ff..f97c55d8 100644 --- a/src/src/Environment/ExtensionDownload/ExtensionZip.php +++ b/src/src/Environment/ExtensionDownload/ExtensionZip.php @@ -52,13 +52,13 @@ public function extract_zip( string $zip_file, string $extract_to ): void { $zip->extractTo( $extract_to ); $zip->close(); - if ( $this->output->isVerbose() ) { + if ( $this->output->isVeryVerbose() ) { $this->output->writeln( sprintf( 'ZipArchive extraction of %s successful (%f seconds).', basename( $zip_file ), microtime( true ) - $start ) ); } return; } else { - if ( $this->output->isVerbose() ) { + if ( $this->output->isVeryVerbose() ) { $this->output->writeln( sprintf( 'ZipArchive extraction of %s failed, falling back to Docker (%f seconds).', basename( $zip_file ), microtime( true ) - $start ) ); } } @@ -86,7 +86,7 @@ public function extract_zip( string $zip_file, string $extract_to ): void { 'Docker ZIP Extraction: ' . $this->output->writeln( $out ); } } ); - if ( $this->output->isVerbose() ) { + if ( $this->output->isVeryVerbose() ) { $this->output->writeln( sprintf( 'Docker ZIP extraction of %s successful (%f seconds).', basename( $zip_file ), microtime( true ) - $start ) ); } } @@ -104,13 +104,13 @@ public function validate_zip( string $zip_file ): void { if ( $zip->open( $zip_file, \ZipArchive::CHECKCONS ) === true ) { $zip->close(); - if ( $this->output->isVerbose() ) { + if ( $this->output->isVeryVerbose() ) { $this->output->writeln( sprintf( 'ZipArchive validation of %s successful (%f seconds).', basename( $zip_file ), microtime( true ) - $start ) ); } return; } else { - if ( $this->output->isVerbose() ) { + if ( $this->output->isVeryVerbose() ) { $this->output->writeln( sprintf( 'ZipArchive validation of %s failed, falling back to Docker (%f seconds).', basename( $zip_file ), microtime( true ) - $start ) ); } } @@ -140,7 +140,7 @@ public function validate_zip( string $zip_file ): void { } } ); - if ( $this->output->isVerbose() ) { + if ( $this->output->isVeryVerbose() ) { $this->output->writeln( sprintf( 'Docker ZIP validation of %s successful (%f seconds).', basename( $zip_file ), microtime( true ) - $start ) ); } } diff --git a/src/src/Environment/ExtensionDownload/Handlers/QITHandler.php b/src/src/Environment/ExtensionDownload/Handlers/QITHandler.php index 969cf5c9..cf18aabf 100644 --- a/src/src/Environment/ExtensionDownload/Handlers/QITHandler.php +++ b/src/src/Environment/ExtensionDownload/Handlers/QITHandler.php @@ -30,7 +30,9 @@ public function populate_extension_versions( array $extensions ): void { 'extensions' => $extensions_to_download, ] ) ->request(); - $output->writeln( sprintf( 'Fetched versions for %d extensions from QIT in %f seconds.', count( $extensions ), microtime( true ) - $start ) ); + if ( $output->isVerbose() ) { + $output->writeln( sprintf( 'Fetched versions for %d extensions from QIT in %f seconds.', count( $extensions ), microtime( true ) - $start ) ); + } /** * @param $download_urls array{ diff --git a/src/src/RequestBuilder.php b/src/src/RequestBuilder.php index 71b1872b..894e47ae 100644 --- a/src/src/RequestBuilder.php +++ b/src/src/RequestBuilder.php @@ -175,7 +175,7 @@ public function request(): string { } } - if ( App::make( Output::class )->isVeryVerbose() ) { + if ( getenv( 'QIT_DEBUG_REQUESTS' ) ) { $curl_parameters[ CURLOPT_VERBOSE ] = true; }