From f7e086aea40eb8d3598d5cb4740eccfb29fe3580 Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Tue, 16 Aug 2022 11:11:55 +1000 Subject: [PATCH 1/6] Add option to build containers on install --- src/Console/InstallCommand.php | 56 ++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/src/Console/InstallCommand.php b/src/Console/InstallCommand.php index a12fe9a4..7e175d56 100644 --- a/src/Console/InstallCommand.php +++ b/src/Console/InstallCommand.php @@ -3,6 +3,8 @@ namespace Laravel\Sail\Console; use Illuminate\Console\Command; +use RuntimeException; +use Symfony\Component\Process\Process; class InstallCommand extends Command { @@ -13,7 +15,8 @@ class InstallCommand extends Command */ protected $signature = 'sail:install {--with= : The services that should be included in the installation} - {--devcontainer : Create a .devcontainer configuration directory}'; + {--devcontainer : Create a .devcontainer configuration directory} + {--prepare : Prepare the installation by building and pulling necessary images}'; /** * The console command description. @@ -25,7 +28,7 @@ class InstallCommand extends Command /** * Execute the console command. * - * @return void + * @return int|null */ public function handle() { @@ -46,6 +49,10 @@ public function handle() } $this->info('Sail scaffolding installed successfully.'); + + if ($this->option('prepare')) { + return $this->prepareInstallation($services); + } } /** @@ -191,4 +198,49 @@ protected function installDevContainer() file_put_contents($this->laravel->basePath('.env'), $environment); } + + /** + * Prepare the installation by building and pulling necessary images. + * + * @param array $services + * return int|null + */ + protected function prepareInstallation($services) + { + $status = $this->runCommands([ + './vendor/bin/sail pull '.implode(' ', $services), + './vendor/bin/sail build', + ]); + + if ($status !== 0) { + $this->warn('Unable to build and pull images. Is Docker installed and running?'); + + return 1; + } + + $this->info('Sail images installed successfully.'); + } + + /** + * Run the given commands. + * + * @param array $commands + * @return int + */ + protected function runCommands($commands) + { + $process = Process::fromShellCommandline(implode(' && ', $commands), null, null, null, null); + + if ('\\' !== DIRECTORY_SEPARATOR && file_exists('/dev/tty') && is_readable('/dev/tty')) { + try { + $process->setTty(true); + } catch (RuntimeException $e) { + $this->output->writeln(' WARN '.$e->getMessage().PHP_EOL); + } + } + + $process->run(function ($type, $line) { + $this->output->write(' '.$line); + }); + } } From f23b81614f0abcdfc17b20b074e3f56fd3538a9a Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Tue, 16 Aug 2022 16:04:46 +1000 Subject: [PATCH 2/6] Protect against command injection --- src/Console/InstallCommand.php | 37 +++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/src/Console/InstallCommand.php b/src/Console/InstallCommand.php index 7e175d56..2bbb9e84 100644 --- a/src/Console/InstallCommand.php +++ b/src/Console/InstallCommand.php @@ -25,6 +25,23 @@ class InstallCommand extends Command */ protected $description = 'Install Laravel Sail\'s default Docker Compose file'; + /** + * The available services that may be installed + * + * @var array + */ + protected $services = [ + 'mysql', + 'pgsql', + 'mariadb', + 'redis', + 'memcached', + 'meilisearch', + 'minio', + 'mailhog', + 'selenium', + ]; + /** * Execute the console command. * @@ -40,6 +57,12 @@ public function handle() $services = $this->gatherServicesWithSymfonyMenu(); } + if ($invalidServices = array_diff($services, $this->services)) { + $this->error('Invalid services ['.implode(',', $invalidServices).'].'); + + return 1; + } + $this->buildDockerCompose($services); $this->replaceEnvVariables($services); $this->configurePhpUnit(); @@ -62,17 +85,7 @@ public function handle() */ protected function gatherServicesWithSymfonyMenu() { - return $this->choice('Which services would you like to install?', [ - 'mysql', - 'pgsql', - 'mariadb', - 'redis', - 'memcached', - 'meilisearch', - 'minio', - 'mailhog', - 'selenium', - ], 0, null, true); + return $this->choice('Which services would you like to install?', $this->services, 0, null, true); } /** @@ -85,7 +98,7 @@ protected function buildDockerCompose(array $services) { $depends = collect($services) ->filter(function ($service) { - return in_array($service, ['mysql', 'pgsql', 'mariadb', 'redis', 'meilisearch', 'minio', 'selenium']); + return in_array($service, $this->services); })->map(function ($service) { return " - {$service}"; })->whenNotEmpty(function ($collection) { From 04631b87db29ecef31396701e3c36c0601880d84 Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Tue, 16 Aug 2022 16:31:35 +1000 Subject: [PATCH 3/6] Always prepare the installation --- src/Console/InstallCommand.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Console/InstallCommand.php b/src/Console/InstallCommand.php index 2bbb9e84..e43231b8 100644 --- a/src/Console/InstallCommand.php +++ b/src/Console/InstallCommand.php @@ -15,8 +15,7 @@ class InstallCommand extends Command */ protected $signature = 'sail:install {--with= : The services that should be included in the installation} - {--devcontainer : Create a .devcontainer configuration directory} - {--prepare : Prepare the installation by building and pulling necessary images}'; + {--devcontainer : Create a .devcontainer configuration directory}'; /** * The console command description. @@ -73,9 +72,7 @@ public function handle() $this->info('Sail scaffolding installed successfully.'); - if ($this->option('prepare')) { - return $this->prepareInstallation($services); - } + return $this->prepareInstallation($services); } /** From 2e14ad3c00ecfb4729d3315dfad54aadc1d697cd Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Tue, 16 Aug 2022 16:43:49 +1000 Subject: [PATCH 4/6] Formatting --- src/Console/InstallCommand.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Console/InstallCommand.php b/src/Console/InstallCommand.php index e43231b8..374b7613 100644 --- a/src/Console/InstallCommand.php +++ b/src/Console/InstallCommand.php @@ -25,7 +25,7 @@ class InstallCommand extends Command protected $description = 'Install Laravel Sail\'s default Docker Compose file'; /** - * The available services that may be installed + * The available services that may be installed. * * @var array */ @@ -210,10 +210,10 @@ protected function installDevContainer() } /** - * Prepare the installation by building and pulling necessary images. + * Prepare the installation by pulling and building any necessary images. * * @param array $services - * return int|null + * @return int|null */ protected function prepareInstallation($services) { @@ -223,7 +223,7 @@ protected function prepareInstallation($services) ]); if ($status !== 0) { - $this->warn('Unable to build and pull images. Is Docker installed and running?'); + $this->warn('Unable to download and build your Sail images. Is Docker installed and running?'); return 1; } From c33e4b86c0e97bfa098367465e215f7db17ccd84 Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Tue, 16 Aug 2022 16:47:02 +1000 Subject: [PATCH 5/6] Fix status reporting --- src/Console/InstallCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/InstallCommand.php b/src/Console/InstallCommand.php index 374b7613..17b01b7c 100644 --- a/src/Console/InstallCommand.php +++ b/src/Console/InstallCommand.php @@ -249,7 +249,7 @@ protected function runCommands($commands) } } - $process->run(function ($type, $line) { + return $process->run(function ($type, $line) { $this->output->write(' '.$line); }); } From 4797c45e008d9178faca28d80fbcd698833d18fb Mon Sep 17 00:00:00 2001 From: Jess Archer Date: Tue, 16 Aug 2022 18:07:20 +1000 Subject: [PATCH 6/6] Remove unnecessary filter --- src/Console/InstallCommand.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Console/InstallCommand.php b/src/Console/InstallCommand.php index 17b01b7c..ff027c66 100644 --- a/src/Console/InstallCommand.php +++ b/src/Console/InstallCommand.php @@ -94,9 +94,7 @@ protected function gatherServicesWithSymfonyMenu() protected function buildDockerCompose(array $services) { $depends = collect($services) - ->filter(function ($service) { - return in_array($service, $this->services); - })->map(function ($service) { + ->map(function ($service) { return " - {$service}"; })->whenNotEmpty(function ($collection) { return $collection->prepend('depends_on:');