From 3e9ea2fa87805110d7bf9356d85d351392cad6a8 Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Tue, 14 Jul 2026 10:10:19 +0100 Subject: [PATCH 1/2] feature: migrate db and install client side deps on create closes #88 closes #91 closes #83 --- src/Command/CreateCommand.php | 202 +++++++++++++++++++++++++++++++--- 1 file changed, 185 insertions(+), 17 deletions(-) diff --git a/src/Command/CreateCommand.php b/src/Command/CreateCommand.php index 2505aed..0de0e4a 100644 --- a/src/Command/CreateCommand.php +++ b/src/Command/CreateCommand.php @@ -7,9 +7,15 @@ use Gt\Cli\Parameter\NamedParameter; use Gt\Cli\Parameter\Parameter; use Gt\Cli\Stream; +use GT\Daemon\CommandNotFoundException; +use GT\Daemon\Process; use GT\GtCommand\Blueprint\BlueprintCollection; +/** @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class CreateCommand extends Command { + const MIGRATION_DIRECTORY_NOT_FOUND_EXCEPTION + = "GT\Database\Migration\MigrationDirectoryNotFoundException"; + /** * TODO: Simplify this function * @SuppressWarnings(PHPMD.CyclomaticComplexity) @@ -33,30 +39,19 @@ public function run(?ArgumentValueList $arguments = null):int { sleep(1); $process = $selectedBlueprint->createProject($name); - $process->exec(); - - do { - $this->write($process->getOutput()); - $this->write($process->getErrorOutput(), Stream::ERROR); - usleep(100_000); - } - while($process->isRunning()); + $this->execAndStreamProcess($process); $process = $selectedBlueprint->updateDependencies($name); - $process->exec(); + $code = $this->execAndStreamProcess($process); - do { - $this->write($process->getOutput()); - $this->write($process->getErrorOutput(), Stream::ERROR); - usleep(100_000); - } - while($process->isRunning()); - - if($code = $process->getExitCode()) { + if($code) { $this->writeLine("There was an error installing the blueprint (exit code $code)"); exit($code); // phpcs:ignore } + $this->runMigration($name); + $this->runNpmInstall($name); + $this->writeLine(); $this->writeLine("Your new application is created!"); $this->writeLine("Would you like to run it now? (Y/N)"); @@ -242,5 +237,178 @@ private function readNamespaceForArguments(ArgumentValueList $arguments):string return $this->readValidNamespace($arguments->get("namespace", "")); } + private function runMigration(string $dir):void { + $this->writeLine(); + $this->writeLine("Running database migrations..."); + + $process = new Process("gt", "migrate"); + $process->setExecCwd($dir); + $code = $this->execAndStreamProcess( + $process, + self::MIGRATION_DIRECTORY_NOT_FOUND_EXCEPTION + ); + + if($code) { + $this->writeLine("There was an error running migrations (exit code $code)"); + exit($code); // phpcs:ignore + } + } + + private function runNpmInstall(string $dir):void { + if(!is_file("$dir/package.json")) { + return; + } + + if(!$this->isCommandAvailable("npm")) { + return; + } + + $this->writeLine(); + $this->writeLine("Installing npm dependencies..."); + + $process = new Process("npm", "install"); + $process->setExecCwd($dir); + $code = $this->execAndStreamProcess($process); + + if($code) { + $this->writeLine("There was an error installing npm dependencies (exit code $code)"); + exit($code); // phpcs:ignore + } + } + + private function isCommandAvailable(string $command):bool { + try { + $process = new Process($command, "--version"); + $process->exec(); + } + catch(CommandNotFoundException) { + return false; + } + + do { + $process->getOutput(); + $process->getErrorOutput(); + usleep(100_000); + } + while($process->isRunning()); + + return $process->getExitCode() === 0; + } + + private function execAndStreamProcess( + Process $process, + ?string $suppressErrorWhenFirstLineContains = null + ):int { + $errorBuffer = ""; + $suppressError = null; + $process->exec(); + + do { + $this->write($process->getOutput()); + $this->writeErrorOutput( + $process->getErrorOutput(), + $suppressErrorWhenFirstLineContains, + $errorBuffer, + $suppressError + ); + usleep(100_000); + } + while($process->isRunning()); + + $this->write($process->getOutput()); + $this->writeErrorOutput( + $process->getErrorOutput(), + $suppressErrorWhenFirstLineContains, + $errorBuffer, + $suppressError + ); + + $this->flushErrorBuffer( + $suppressErrorWhenFirstLineContains, + $errorBuffer, + $suppressError + ); + + if($suppressError) { + return 0; + } + + return $process->getExitCode() ?? 127; + } + + private function writeErrorOutput( + string $errorOutput, + ?string $suppressErrorWhenFirstLineContains, + string &$errorBuffer, + ?bool &$suppressError + ):void { + if($errorOutput === "") { + return; + } + + if(!$suppressErrorWhenFirstLineContains || $suppressError === false) { + $this->write($errorOutput, Stream::ERROR); + return; + } + + if($suppressError === true) { + return; + } + + $errorBuffer .= $errorOutput; + if(!str_contains($errorBuffer, PHP_EOL)) { + return; + } + + $suppressError = $this->shouldSuppressErrorOutput( + $errorBuffer, + $suppressErrorWhenFirstLineContains + ); + + if(!$suppressError) { + $this->write($errorBuffer, Stream::ERROR); + } + + $errorBuffer = ""; + } + + private function flushErrorBuffer( + ?string $suppressErrorWhenFirstLineContains, + string &$errorBuffer, + ?bool &$suppressError + ):void { + if($errorBuffer === "") { + return; + } + + if(!$suppressErrorWhenFirstLineContains) { + $this->write($errorBuffer, Stream::ERROR); + $errorBuffer = ""; + return; + } + + $suppressError ??= $this->shouldSuppressErrorOutput( + $errorBuffer, + $suppressErrorWhenFirstLineContains + ); + + if(!$suppressError) { + $this->write($errorBuffer, Stream::ERROR); + } + + $errorBuffer = ""; + } + + private function shouldSuppressErrorOutput( + string $errorBuffer, + string $suppressErrorWhenFirstLineContains + ):bool { + $firstLine = strtok($errorBuffer, PHP_EOL); + + return str_contains( + $firstLine ?: "", + $suppressErrorWhenFirstLineContains + ); + } } From a885be2b50886b09a1f45f9ddb7dccc66f81fc2e Mon Sep 17 00:00:00 2001 From: Greg Bowler Date: Mon, 31 Aug 2026 15:57:12 +0100 Subject: [PATCH 2/2] tweak: add phpmd quotes --- src/Command/CreateCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Command/CreateCommand.php b/src/Command/CreateCommand.php index 49ec4bb..48c6a78 100644 --- a/src/Command/CreateCommand.php +++ b/src/Command/CreateCommand.php @@ -11,7 +11,7 @@ use GT\Daemon\Process; use GT\GtCommand\Blueprint\BlueprintCollection; -/** @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ +/** @SuppressWarnings("PHPMD.ExcessiveClassComplexity") */ class CreateCommand extends Command { const MIGRATION_DIRECTORY_NOT_FOUND_EXCEPTION = "GT\Database\Migration\MigrationDirectoryNotFoundException";