From 29c235083ce6cc3855fa990744ec8db385365f20 Mon Sep 17 00:00:00 2001 From: Stefan Kruppa Date: Thu, 9 Dec 2021 15:58:22 +0100 Subject: [PATCH 1/4] Add standard Symfony commands --- bref | 260 ++---------------------------- composer.json | 2 +- demo/console.php | 59 ++++--- src/Console/Command/Cli.php | 71 ++++++++ src/Console/Command/Dashboard.php | 147 +++++++++++++++++ src/Console/Command/Init.php | 84 ++++++++++ src/Console/Command/Layers.php | 47 ++++++ src/Console/Command/Local.php | 26 ++- 8 files changed, 426 insertions(+), 270 deletions(-) create mode 100644 src/Console/Command/Cli.php create mode 100644 src/Console/Command/Dashboard.php create mode 100644 src/Console/Command/Init.php create mode 100644 src/Console/Command/Layers.php diff --git a/bref b/bref index a5e83f209..0fa00f06d 100755 --- a/bref +++ b/bref @@ -2,15 +2,12 @@ command('init', function (SymfonyStyle $io) { - $exeFinder = new ExecutableFinder(); - if (! $exeFinder->find('serverless')) { - $io->error( - 'The `serverless` command is not installed.' . PHP_EOL . - 'Please follow the instructions at https://bref.sh/docs/installation.html' - ); - - return 1; - } - - if (file_exists('serverless.yml') || file_exists('index.php')) { - $io->error('The directory already contains a `serverless.yml` and/or `index.php` file.'); - - return 1; - } - - $choice = $io->choice( - 'What kind of lambda do you want to create? (you will be able to add more functions later by editing `serverless.yml`)', - [ - 'Web application', - 'Event-driven function', - ], - 'Web application', - ); - $templateDirectory = [ - 'Web application' => 'http', - 'Event-driven function' => 'function', - ][$choice]; - - $fs = new Filesystem; - $rootPath = __DIR__ . "/template/$templateDirectory"; - - $io->writeln("Creating index.php"); - $fs->copy("$rootPath/index.php", 'index.php'); - - $io->writeln("Creating serverless.yml"); - - $template = file_get_contents("$rootPath/serverless.yml"); - - $template = str_replace('PHP_VERSION', PHP_MAJOR_VERSION . PHP_MINOR_VERSION, $template); - - file_put_contents('serverless.yml', $template); - - $filesToGitAdd = ['index.php', 'serverless.yml']; - - /* - * We check if this is a git repository to automatically add files to git. - */ - if ((new Process(['git', 'rev-parse', '--is-inside-work-tree']))->run() === 0) { - foreach ($filesToGitAdd as $file) { - (new Process(['git', 'add', $file]))->run(); - } - $io->success([ - 'Project initialized and ready to test or deploy.', - 'The files created were automatically added to git.', - ]); - } else { - $io->success('Project initialized and ready to test or deploy.'); - } - - return 0; -}); - -/** - * Run a CLI command in the remote environment. - */ -$app->command('cli function [--region=] [--profile=] [arguments]*', function (string $function, ?string $region, ?string $profile, array $arguments, SymfonyStyle $io) { - $lambda = new SimpleLambdaClient( - $region ?: getenv('AWS_DEFAULT_REGION') ?: 'us-east-1', - $profile ?: getenv('AWS_PROFILE') ?: 'default', - 15 * 60 // maximum duration on Lambda - ); - - // Because arguments may contain spaces, and are going to be executed remotely - // as a separate process, we need to escape all arguments. - $arguments = array_map(static function (string $arg): string { - return escapeshellarg($arg); - }, $arguments); - - try { - $result = $lambda->invoke($function, json_encode(implode(' ', $arguments))); - } catch (InvocationFailed $e) { - $io->getErrorStyle()->writeln('' . $e->getInvocationLogs() . ''); - $io->error($e->getMessage()); - return 1; - } - - $payload = $result->getPayload(); - if (isset($payload['output'])) { - $io->writeln($payload['output']); - } else { - $io->error('The command did not return a valid response.'); - $io->writeln('Logs:'); - $io->write('' . $result->getLogs() . ''); - $io->writeln('Lambda result payload:'); - $io->writeln(json_encode($payload, JSON_PRETTY_PRINT)); - return 1; - } - - return (int) ($payload['exitCode'] ?? 1); -}); - -/** - * Invoke a function locally - */ -$app->command(Local::SIGNATURE, new Local); - -$app->command('dashboard [--host=] [--port=] [--profile=] [--stage=]', function (SymfonyStyle $io, string $host = 'localhost', int $port = 8000, string $profile = null, string $stage = null) { - $io->info('The Bref Dashboard is also available as an application: https://dashboard.bref.sh'); - if ($host === 'localhost') { - $host = '127.0.0.1'; - } - if ($profile === null) { - $profile = getenv('AWS_PROFILE') ?: 'default'; - } - - if (! file_exists('serverless.yml')) { - $io->error('No `serverless.yml` file found.'); - - return 1; - } - - $exeFinder = new ExecutableFinder(); - if (! $exeFinder->find('docker')) { - $io->error( - 'The `docker` command is not installed.' . PHP_EOL . - 'Please follow the instructions at https://docs.docker.com/install/' - ); - - return 1; - } - - if (! $exeFinder->find('serverless')) { - $io->error( - 'The `serverless` command is not installed.' . PHP_EOL . - 'Please follow the instructions at https://bref.sh/docs/installation.html' - ); - - return 1; - } - - $serverlessInfo = new Process(['serverless', 'info', '--stage', $stage, '--aws-profile', $profile]); - $serverlessInfo->start(); - $animation = new LoadingAnimation($io); - do { - $animation->tick('Retrieving the stack'); - usleep(100*1000); - } while ($serverlessInfo->isRunning()); - $animation->clear(); - - if (!$serverlessInfo->isSuccessful()) { - $io->error('The command `serverless info` failed' . PHP_EOL . $serverlessInfo->getOutput()); - - return 1; - } - - $serverlessInfoOutput = $serverlessInfo->getOutput(); - - $region = []; - preg_match('/region: ([a-z0-9-]*)/', $serverlessInfoOutput, $region); - $region = $region[1]; - - $stack = []; - preg_match('/stack: ([a-zA-Z0-9-]*)/', $serverlessInfoOutput, $stack); - $stack = $stack[1]; - - $io->writeln("Stack: $stack ($region)"); - - $dockerPull = new Process(['docker', 'pull', 'bref/dashboard']); - $dockerPull->setTimeout(null); - $dockerPull->start(); - do { - $animation->tick('Retrieving the latest version of the dashboard'); - usleep(100*1000); - } while ($dockerPull->isRunning()); - $animation->clear(); - if (! $dockerPull->isSuccessful()) { - $io->error([ - 'The command `docker pull bref/dashboard` failed', - $dockerPull->getErrorOutput(), - ]); - - return 1; - } - - $process = new Process(['docker', 'run', '--rm', '-p', $host . ':' . $port.':8000', '-v', getenv('HOME').'/.aws:/root/.aws:ro', '--env', 'STACKNAME='.$stack, '--env', 'REGION='.$region, '--env', 'AWS_PROFILE='.$profile, 'bref/dashboard']); - $process->setTimeout(null); - $process->start(); - do { - $animation->tick('Starting the dashboard'); - usleep(100*1000); - $serverOutput = $process->getOutput() . $process->getErrorOutput(); - $hasStarted = (strpos($serverOutput, 'Development Server') !== false); - } while ($process->isRunning() && !$hasStarted); - $animation->clear(); - if (!$process->isRunning()) { - $io->error([ - 'The dashboard failed to start', - $process->getErrorOutput(), - ]); - - return 1; - } - $url = "http://$host:$port"; - $io->writeln("Dashboard started: $url"); - OpenUrl::open($url); - $process->wait(function ($type, $buffer) { - if (Process::ERR === $type) { - echo 'ERR > '.$buffer; - } else { - echo 'OUT > '.$buffer; - } - }); - - return $process->getExitCode(); -})->descriptions('Start the dashboard'); - -$app->command('layers region', function (string $region, SymfonyStyle $io) { - $layers = json_decode(file_get_contents(__DIR__ . '/layers.json'), true); - $io->title("Layers for the $region region"); - - $array = []; - foreach ($layers as $layer => $versions) { - $version = $versions[$region]; - $array[] = [ - $layer, - $version, - "arn:aws:lambda:$region:416566615250:layer:$layer:$version", - ]; - } - $io->table([ - 'Layer', - 'Version', - 'ARN', - ], $array); - - return 0; -})->descriptions('Displays the versions of the Bref layers'); +$app = new Application('Deploy serverless PHP applications'); +$app->add(new Init()); +$app->add(new Cli()); +$app->add(new Local()); +$app->add(new Dashboard()); +$app->add(new Layers()); $app->run(); diff --git a/composer.json b/composer.json index f962caba2..ed9d41d15 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ "php": ">=7.3.0", "ext-curl": "*", "ext-json": "*", - "mnapoli/silly": "^1.7", + "symfony/console": "^3.0|^4.0|^5.0|^6.0", "symfony/filesystem": "^3.1|^4.0|^5.0|^6.0", "symfony/process": "^4.2|^5.0|^6.0", "psr/http-message": "^1.0", diff --git a/demo/console.php b/demo/console.php index b723076ea..9aaa009e2 100644 --- a/demo/console.php +++ b/demo/console.php @@ -1,27 +1,50 @@ command('hello [name]', function (string $name = 'World!', OutputInterface $output) { - $output->writeln('Hello ' . $name); -}); -$silly->command('phpinfo', function (OutputInterface $output) { - ob_start(); - phpinfo(); - $phpinfo = ob_get_clean(); - $output->write($phpinfo); -}); -$silly->command('error', function (OutputInterface $output) { - $output->writeln('There was an error!'); - return 1; -}); -$silly->command('sleep', function () { - sleep(120); +$app->add(new class('hello') extends Command { + protected function configure(): void + { + $this->addArgument('name', InputArgument::OPTIONAL, '', 'World!'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $output->writeln('Hello, ' . $input->getArgument('name')); + return 0; + } }); -$silly->run(); +$app->add((new class('phpinfo') extends Command {})->setCode( + function (InputInterface $input, OutputInterface $output) { + ob_start(); + phpinfo(); + $phpinfo = ob_get_clean(); + $output->write($phpinfo); + return 0; + } +)); + +$app->add((new class('error') extends Command {})->setCode( + function (InputInterface $input, OutputInterface $output) { + $output->writeln('There was an error!'); + return 1; + } +)); + +$app->add((new class('sleep') extends Command {})->setCode( + function (InputInterface $input, OutputInterface $output) { + sleep(120); + return 0; + } +)); + +$app->run(); diff --git a/src/Console/Command/Cli.php b/src/Console/Command/Cli.php new file mode 100644 index 000000000..75fe5536e --- /dev/null +++ b/src/Console/Command/Cli.php @@ -0,0 +1,71 @@ +addArgument('function', InputArgument::REQUIRED); + $this->addArgument('arguments', InputArgument::OPTIONAL); + $this->addOption('region', 'r'); + $this->addOption('profile', 'p'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + $function = $input->getArgument('function'); + $arguments = $input->getArgument('arguments'); + $region = $input->getOption('region'); + $profile = $input->getOption('profile'); + + $lambda = new SimpleLambdaClient( + $region ?: getenv('AWS_DEFAULT_REGION') ?: 'us-east-1', + $profile ?: getenv('AWS_PROFILE') ?: 'default', + 15 * 60 // maximum duration on Lambda + ); + + // Because arguments may contain spaces, and are going to be executed remotely + // as a separate process, we need to escape all arguments. + $arguments = array_map(static function (string $arg): string { + return escapeshellarg($arg); + }, $arguments); + + try { + $result = $lambda->invoke($function, json_encode(implode(' ', $arguments))); + } catch (InvocationFailed $e) { + $io->getErrorStyle()->writeln('' . $e->getInvocationLogs() . ''); + $io->error($e->getMessage()); + return 1; + } + + $payload = $result->getPayload(); + if (isset($payload['output'])) { + $io->writeln($payload['output']); + } else { + $io->error('The command did not return a valid response.'); + $io->writeln('Logs:'); + $io->write('' . $result->getLogs() . ''); + $io->writeln('Lambda result payload:'); + $io->writeln(json_encode($payload, JSON_PRETTY_PRINT)); + return 1; + } + + return (int) ($payload['exitCode'] ?? 1); + } +} diff --git a/src/Console/Command/Dashboard.php b/src/Console/Command/Dashboard.php new file mode 100644 index 000000000..b863ef46b --- /dev/null +++ b/src/Console/Command/Dashboard.php @@ -0,0 +1,147 @@ +addOption('host', null, InputOption::VALUE_REQUIRED); + $this->addOption('port', null, InputOption::VALUE_REQUIRED); + $this->addOption('profile', null, InputOption::VALUE_REQUIRED); + $this->addOption('stage', null, InputOption::VALUE_REQUIRED); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + $host = $input->getOption('host'); + $port = $input->getOption('port'); + $profile = $input->getOption('profile'); + $stage = $input->getOption('stage'); + + $io->info('The Bref Dashboard is also available as an application: https://dashboard.bref.sh'); + if ($host === 'localhost') { + $host = '127.0.0.1'; + } + if ($profile === null) { + $profile = getenv('AWS_PROFILE') ?: 'default'; + } + + if (! file_exists('serverless.yml')) { + $io->error('No `serverless.yml` file found.'); + + return 1; + } + + $exeFinder = new ExecutableFinder(); + if (! $exeFinder->find('docker')) { + $io->error( + 'The `docker` command is not installed.' . PHP_EOL . + 'Please follow the instructions at https://docs.docker.com/install/' + ); + + return 1; + } + + if (! $exeFinder->find('serverless')) { + $io->error( + 'The `serverless` command is not installed.' . PHP_EOL . + 'Please follow the instructions at https://bref.sh/docs/installation.html' + ); + + return 1; + } + + $serverlessInfo = new Process(['serverless', 'info', '--stage', $stage, '--aws-profile', $profile]); + $serverlessInfo->start(); + $animation = new LoadingAnimation($io); + do { + $animation->tick('Retrieving the stack'); + usleep(100*1000); + } while ($serverlessInfo->isRunning()); + $animation->clear(); + + if (!$serverlessInfo->isSuccessful()) { + $io->error('The command `serverless info` failed' . PHP_EOL . $serverlessInfo->getOutput()); + + return 1; + } + + $serverlessInfoOutput = $serverlessInfo->getOutput(); + + $region = []; + preg_match('/region: ([a-z0-9-]*)/', $serverlessInfoOutput, $region); + $region = $region[1]; + + $stack = []; + preg_match('/stack: ([a-zA-Z0-9-]*)/', $serverlessInfoOutput, $stack); + $stack = $stack[1]; + + $io->writeln("Stack: $stack ($region)"); + + $dockerPull = new Process(['docker', 'pull', 'bref/dashboard']); + $dockerPull->setTimeout(null); + $dockerPull->start(); + do { + $animation->tick('Retrieving the latest version of the dashboard'); + usleep(100*1000); + } while ($dockerPull->isRunning()); + $animation->clear(); + if (! $dockerPull->isSuccessful()) { + $io->error([ + 'The command `docker pull bref/dashboard` failed', + $dockerPull->getErrorOutput(), + ]); + + return 1; + } + + $process = new Process(['docker', 'run', '--rm', '-p', $host . ':' . $port.':8000', '-v', getenv('HOME').'/.aws:/root/.aws:ro', '--env', 'STACKNAME='.$stack, '--env', 'REGION='.$region, '--env', 'AWS_PROFILE='.$profile, 'bref/dashboard']); + $process->setTimeout(null); + $process->start(); + do { + $animation->tick('Starting the dashboard'); + usleep(100*1000); + $serverOutput = $process->getOutput() . $process->getErrorOutput(); + $hasStarted = (strpos($serverOutput, 'Development Server') !== false); + } while ($process->isRunning() && !$hasStarted); + $animation->clear(); + if (!$process->isRunning()) { + $io->error([ + 'The dashboard failed to start', + $process->getErrorOutput(), + ]); + + return 1; + } + $url = "http://$host:$port"; + $io->writeln("Dashboard started: $url"); + OpenUrl::open($url); + $process->wait(function ($type, $buffer) { + if (Process::ERR === $type) { + echo 'ERR > '.$buffer; + } else { + echo 'OUT > '.$buffer; + } + }); + + return $process->getExitCode(); + } + +} diff --git a/src/Console/Command/Init.php b/src/Console/Command/Init.php new file mode 100644 index 000000000..0fffe113b --- /dev/null +++ b/src/Console/Command/Init.php @@ -0,0 +1,84 @@ +find('serverless')) { + $io->error( + 'The `serverless` command is not installed.' . PHP_EOL . + 'Please follow the instructions at https://bref.sh/docs/installation.html' + ); + + return 1; + } + + if (file_exists('serverless.yml') || file_exists('index.php')) { + $io->error('The directory already contains a `serverless.yml` and/or `index.php` file.'); + + return 1; + } + + $choice = $io->choice( + 'What kind of lambda do you want to create? (you will be able to add more functions later by editing `serverless.yml`)', + [ + 'Web application', + 'Event-driven function', + ], + 'Web application', + ); + $templateDirectory = [ + 'Web application' => 'http', + 'Event-driven function' => 'function', + ][$choice]; + + $fs = new Filesystem(); + $rootPath = __DIR__ . "/template/$templateDirectory"; + + $io->writeln("Creating index.php"); + $fs->copy("$rootPath/index.php", 'index.php'); + + $io->writeln("Creating serverless.yml"); + + $template = file_get_contents("$rootPath/serverless.yml"); + + $template = str_replace('PHP_VERSION', PHP_MAJOR_VERSION . PHP_MINOR_VERSION, $template); + + file_put_contents('serverless.yml', $template); + + $filesToGitAdd = ['index.php', 'serverless.yml']; + + /* + * We check if this is a git repository to automatically add files to git. + */ + if ((new Process(['git', 'rev-parse', '--is-inside-work-tree']))->run() === 0) { + foreach ($filesToGitAdd as $file) { + (new Process(['git', 'add', $file]))->run(); + } + $io->success([ + 'Project initialized and ready to test or deploy.', + 'The files created were automatically added to git.', + ]); + } else { + $io->success('Project initialized and ready to test or deploy.'); + } + + return 0; + } +} diff --git a/src/Console/Command/Layers.php b/src/Console/Command/Layers.php new file mode 100644 index 000000000..fc510c3f6 --- /dev/null +++ b/src/Console/Command/Layers.php @@ -0,0 +1,47 @@ +addArgument('region'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + $region = $input->getArgument('region'); + + $layers = json_decode(file_get_contents(__DIR__ . '/layers.json'), true); + $io->title("Layers for the $region region"); + + $array = []; + foreach ($layers as $layer => $versions) { + $version = $versions[$region]; + $array[] = [ + $layer, + $version, + "arn:aws:lambda:$region:416566615250:layer:$layer:$version", + ]; + } + $io->table([ + 'Layer', + 'Version', + 'ARN', + ], $array); + + return 0; + } +} diff --git a/src/Console/Command/Local.php b/src/Console/Command/Local.php index 3bef1085b..89789184a 100644 --- a/src/Console/Command/Local.php +++ b/src/Console/Command/Local.php @@ -8,6 +8,10 @@ use Exception; use JsonException; use Psr\Container\NotFoundExceptionInterface; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Yaml\Yaml; use Throwable; @@ -15,12 +19,30 @@ /** * Local function invocation. */ -class Local +class Local extends Command { + protected static $defaultName = 'local'; + public const SIGNATURE = 'local [function] [data] [--file=] [--handler=] [--config=]'; - public function __invoke(?string $function, ?string $data, ?string $file, ?string $handler, ?string $config, SymfonyStyle $io): int + protected function configure(): void { + $this->addArgument('function', null, '', null); + $this->addArgument('data'); + $this->addOption('file', 'f', InputOption::VALUE_REQUIRED); + $this->addOption('handler', 'h', InputOption::VALUE_REQUIRED); + $this->addOption('config', 'c', InputOption::VALUE_REQUIRED); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + $function = $input->getArgument('function'); + $data = $input->getArgument('data'); + $file = $input->getOption('file'); + $handler = $input->getOption('handler'); + $config = $input->getOption('config'); + if ($function === null && $handler === null) { throw new Exception('Please provide a function name or the --handler= option.'); } From 1a1e625e93ed5a638278a956d00fb3530c0b45b5 Mon Sep 17 00:00:00 2001 From: Stefan Kruppa Date: Thu, 9 Dec 2021 22:37:00 +0100 Subject: [PATCH 2/4] Improve and fix new commands --- demo/console.php | 47 ++++++++++++++----------------- src/Console/Command/Cli.php | 23 +++++++-------- src/Console/Command/Dashboard.php | 34 +++++++++++----------- src/Console/Command/Init.php | 21 +++++++------- src/Console/Command/Layers.php | 14 ++++----- src/Console/Command/Local.php | 20 +++++++------ 6 files changed, 79 insertions(+), 80 deletions(-) diff --git a/demo/console.php b/demo/console.php index 9aaa009e2..a4723ca0e 100644 --- a/demo/console.php +++ b/demo/console.php @@ -10,41 +10,36 @@ $app = new Application(); -$app->add(new class('hello') extends Command { - protected function configure(): void - { - $this->addArgument('name', InputArgument::OPTIONAL, '', 'World!'); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { +$app->register('hello') + ->addArgument('name', InputArgument::OPTIONAL, '', 'World!') + ->setCode(function (InputInterface $input, OutputInterface $output): int { $output->writeln('Hello, ' . $input->getArgument('name')); - return 0; - } -}); + return Command::SUCCESS; + }) +; -$app->add((new class('phpinfo') extends Command {})->setCode( - function (InputInterface $input, OutputInterface $output) { +$app->register('phpinfo') + ->setCode(function (InputInterface $input, OutputInterface $output): int { ob_start(); phpinfo(); $phpinfo = ob_get_clean(); $output->write($phpinfo); - return 0; - } -)); + return Command::SUCCESS; + }) +; -$app->add((new class('error') extends Command {})->setCode( - function (InputInterface $input, OutputInterface $output) { +$app->register('error') + ->setCode(function (InputInterface $input, OutputInterface $output): int { $output->writeln('There was an error!'); - return 1; - } -)); + return Command::FAILURE; + }) +; -$app->add((new class('sleep') extends Command {})->setCode( - function (InputInterface $input, OutputInterface $output) { +$app->register('sleep') + ->setCode(function (InputInterface $input, OutputInterface $output): int { sleep(120); - return 0; - } -)); + return Command::SUCCESS; + }) +; $app->run(); diff --git a/src/Console/Command/Cli.php b/src/Console/Command/Cli.php index 75fe5536e..be661c984 100644 --- a/src/Console/Command/Cli.php +++ b/src/Console/Command/Cli.php @@ -1,6 +1,4 @@ -addArgument('function', InputArgument::REQUIRED); - $this->addArgument('arguments', InputArgument::OPTIONAL); - $this->addOption('region', 'r'); - $this->addOption('profile', 'p'); + $this + ->addArgument('function', InputArgument::REQUIRED) + ->addArgument('arguments', InputArgument::OPTIONAL) + ->addOption('region', 'r') + ->addOption('profile', 'p') + ; } protected function execute(InputInterface $input, OutputInterface $output): int @@ -51,7 +52,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } catch (InvocationFailed $e) { $io->getErrorStyle()->writeln('' . $e->getInvocationLogs() . ''); $io->error($e->getMessage()); - return 1; + return Command::FAILURE; } $payload = $result->getPayload(); @@ -63,9 +64,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int $io->write('' . $result->getLogs() . ''); $io->writeln('Lambda result payload:'); $io->writeln(json_encode($payload, JSON_PRETTY_PRINT)); - return 1; + return Command::FAILURE; } - return (int) ($payload['exitCode'] ?? 1); + return (int) ($payload['exitCode'] ?? Command::FAILURE); } } diff --git a/src/Console/Command/Dashboard.php b/src/Console/Command/Dashboard.php index b863ef46b..a540a4c57 100644 --- a/src/Console/Command/Dashboard.php +++ b/src/Console/Command/Dashboard.php @@ -1,6 +1,4 @@ -addOption('host', null, InputOption::VALUE_REQUIRED); - $this->addOption('port', null, InputOption::VALUE_REQUIRED); - $this->addOption('profile', null, InputOption::VALUE_REQUIRED); - $this->addOption('stage', null, InputOption::VALUE_REQUIRED); + $this + ->addOption('host', null, InputOption::VALUE_REQUIRED, '', 'localhost') + ->addOption('port', null, InputOption::VALUE_REQUIRED, '', '8000') + ->addOption('profile', null, InputOption::VALUE_REQUIRED) + ->addOption('stage', null, InputOption::VALUE_REQUIRED) + ; } - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); $host = $input->getOption('host'); - $port = $input->getOption('port'); + $port = (int) $input->getOption('port'); $profile = $input->getOption('profile'); $stage = $input->getOption('stage'); @@ -46,7 +48,7 @@ protected function execute(InputInterface $input, OutputInterface $output) if (! file_exists('serverless.yml')) { $io->error('No `serverless.yml` file found.'); - return 1; + return Command::FAILURE; } $exeFinder = new ExecutableFinder(); @@ -56,7 +58,7 @@ protected function execute(InputInterface $input, OutputInterface $output) 'Please follow the instructions at https://docs.docker.com/install/' ); - return 1; + return Command::FAILURE; } if (! $exeFinder->find('serverless')) { @@ -65,7 +67,7 @@ protected function execute(InputInterface $input, OutputInterface $output) 'Please follow the instructions at https://bref.sh/docs/installation.html' ); - return 1; + return Command::FAILURE; } $serverlessInfo = new Process(['serverless', 'info', '--stage', $stage, '--aws-profile', $profile]); @@ -80,7 +82,7 @@ protected function execute(InputInterface $input, OutputInterface $output) if (!$serverlessInfo->isSuccessful()) { $io->error('The command `serverless info` failed' . PHP_EOL . $serverlessInfo->getOutput()); - return 1; + return Command::FAILURE; } $serverlessInfoOutput = $serverlessInfo->getOutput(); @@ -109,7 +111,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $dockerPull->getErrorOutput(), ]); - return 1; + return Command::FAILURE; } $process = new Process(['docker', 'run', '--rm', '-p', $host . ':' . $port.':8000', '-v', getenv('HOME').'/.aws:/root/.aws:ro', '--env', 'STACKNAME='.$stack, '--env', 'REGION='.$region, '--env', 'AWS_PROFILE='.$profile, 'bref/dashboard']); @@ -128,7 +130,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $process->getErrorOutput(), ]); - return 1; + return Command::FAILURE; } $url = "http://$host:$port"; $io->writeln("Dashboard started: $url"); diff --git a/src/Console/Command/Init.php b/src/Console/Command/Init.php index 0fffe113b..abf7f3f15 100644 --- a/src/Console/Command/Init.php +++ b/src/Console/Command/Init.php @@ -1,6 +1,4 @@ -find('serverless')) { $io->error( 'The `serverless` command is not installed.' . PHP_EOL . 'Please follow the instructions at https://bref.sh/docs/installation.html' ); - return 1; + return Command::FAILURE; } if (file_exists('serverless.yml') || file_exists('index.php')) { $io->error('The directory already contains a `serverless.yml` and/or `index.php` file.'); - return 1; + return Command::FAILURE; } $choice = $io->choice( @@ -48,13 +47,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int 'Event-driven function' => 'function', ][$choice]; - $fs = new Filesystem(); - $rootPath = __DIR__ . "/template/$templateDirectory"; + $fs = new Filesystem; + $rootPath = dirname(__DIR__, 3) . "/template/$templateDirectory"; - $io->writeln("Creating index.php"); + $io->writeln('Creating index.php'); $fs->copy("$rootPath/index.php", 'index.php'); - $io->writeln("Creating serverless.yml"); + $io->writeln('Creating serverless.yml'); $template = file_get_contents("$rootPath/serverless.yml"); @@ -79,6 +78,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int $io->success('Project initialized and ready to test or deploy.'); } - return 0; + return Command::SUCCESS; } } diff --git a/src/Console/Command/Layers.php b/src/Console/Command/Layers.php index fc510c3f6..a509a65c7 100644 --- a/src/Console/Command/Layers.php +++ b/src/Console/Command/Layers.php @@ -1,6 +1,4 @@ -addArgument('region'); } - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); $region = $input->getArgument('region'); - $layers = json_decode(file_get_contents(__DIR__ . '/layers.json'), true); + $layers = json_decode(file_get_contents(dirname(__DIR__, 3) . '/layers.json'), true); $io->title("Layers for the $region region"); $array = []; @@ -42,6 +42,6 @@ protected function execute(InputInterface $input, OutputInterface $output) 'ARN', ], $array); - return 0; + return Command::SUCCESS; } } diff --git a/src/Console/Command/Local.php b/src/Console/Command/Local.php index 89789184a..01919fa36 100644 --- a/src/Console/Command/Local.php +++ b/src/Console/Command/Local.php @@ -9,6 +9,7 @@ use JsonException; use Psr\Container\NotFoundExceptionInterface; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -21,17 +22,18 @@ */ class Local extends Command { + /** @var string */ protected static $defaultName = 'local'; - public const SIGNATURE = 'local [function] [data] [--file=] [--handler=] [--config=]'; - protected function configure(): void { - $this->addArgument('function', null, '', null); - $this->addArgument('data'); - $this->addOption('file', 'f', InputOption::VALUE_REQUIRED); - $this->addOption('handler', 'h', InputOption::VALUE_REQUIRED); - $this->addOption('config', 'c', InputOption::VALUE_REQUIRED); + $this + ->addArgument('function', InputArgument::OPTIONAL) + ->addArgument('data', InputArgument::OPTIONAL) + ->addOption('file', 'f', InputOption::VALUE_REQUIRED) + ->addOption('handler', null, InputOption::VALUE_REQUIRED) + ->addOption('config', 'c', InputOption::VALUE_REQUIRED) + ; } protected function execute(InputInterface $input, OutputInterface $output): int @@ -101,14 +103,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int $e->getTraceAsString(), ]); $io->error($e->getMessage()); - return 1; + return Command::FAILURE; } $this->logEnd($startTime, $io, $requestId); // Show the invocation result $io->block(json_encode($result, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT), null, 'fg=black;bg=green', '', true); - return 0; + return Command::SUCCESS; } private function handlerFromServerlessYml(string $function, ?string $config): string From b35d14dab1594359c0ba34b021da59af3acf4160 Mon Sep 17 00:00:00 2001 From: Stefan Kruppa Date: Fri, 10 Dec 2021 12:56:54 +0100 Subject: [PATCH 3/4] Ensure compatibility with symfony/console 3.x --- src/Console/Command/Cli.php | 7 ++----- src/Console/Command/Dashboard.php | 7 ++----- src/Console/Command/Init.php | 6 ++++-- src/Console/Command/Layers.php | 11 +++++------ src/Console/Command/Local.php | 4 +--- 5 files changed, 14 insertions(+), 21 deletions(-) diff --git a/src/Console/Command/Cli.php b/src/Console/Command/Cli.php index be661c984..3bb6e4f85 100644 --- a/src/Console/Command/Cli.php +++ b/src/Console/Command/Cli.php @@ -12,14 +12,11 @@ final class Cli extends Command { - /** @var string */ - protected static $defaultName = 'cli'; - /** @var string */ - protected static $defaultDescription = 'Run a CLI command in the remote environment.'; - protected function configure(): void { $this + ->setName('cli') + ->setDescription('Runs a CLI command in the remote environment') ->addArgument('function', InputArgument::REQUIRED) ->addArgument('arguments', InputArgument::OPTIONAL) ->addOption('region', 'r') diff --git a/src/Console/Command/Dashboard.php b/src/Console/Command/Dashboard.php index a540a4c57..ad872d007 100644 --- a/src/Console/Command/Dashboard.php +++ b/src/Console/Command/Dashboard.php @@ -14,14 +14,11 @@ final class Dashboard extends Command { - /** @var string */ - protected static $defaultName = 'dashboard'; - /** @var string */ - protected static $defaultDescription = 'Start the dashboard'; - protected function configure(): void { $this + ->setName('dashboard') + ->setDescription('Starts the dashboard') ->addOption('host', null, InputOption::VALUE_REQUIRED, '', 'localhost') ->addOption('port', null, InputOption::VALUE_REQUIRED, '', '8000') ->addOption('profile', null, InputOption::VALUE_REQUIRED) diff --git a/src/Console/Command/Init.php b/src/Console/Command/Init.php index abf7f3f15..f0d52a786 100644 --- a/src/Console/Command/Init.php +++ b/src/Console/Command/Init.php @@ -12,8 +12,10 @@ final class Init extends Command { - /** @var string */ - protected static $defaultName = 'init'; + protected function configure() + { + $this->setName('init'); + } protected function execute(InputInterface $input, OutputInterface $output): int { diff --git a/src/Console/Command/Layers.php b/src/Console/Command/Layers.php index a509a65c7..f59530acb 100644 --- a/src/Console/Command/Layers.php +++ b/src/Console/Command/Layers.php @@ -9,14 +9,13 @@ final class Layers extends Command { - /** @var string */ - protected static $defaultName = 'layers'; - /** @var string */ - protected static $defaultDescription = 'Display the versions of the Bref layers'; - protected function configure(): void { - $this->addArgument('region'); + $this + ->setName('layers') + ->setDescription('Displays the versions of the Bref layers') + ->addArgument('region') + ; } protected function execute(InputInterface $input, OutputInterface $output): int diff --git a/src/Console/Command/Local.php b/src/Console/Command/Local.php index 01919fa36..91218791b 100644 --- a/src/Console/Command/Local.php +++ b/src/Console/Command/Local.php @@ -22,12 +22,10 @@ */ class Local extends Command { - /** @var string */ - protected static $defaultName = 'local'; - protected function configure(): void { $this + ->setName('local') ->addArgument('function', InputArgument::OPTIONAL) ->addArgument('data', InputArgument::OPTIONAL) ->addOption('file', 'f', InputOption::VALUE_REQUIRED) From 713d658da2bf580bed792d8ded299f10cee8138c Mon Sep 17 00:00:00 2001 From: Stefan Kruppa Date: Mon, 13 Dec 2021 22:07:26 +0100 Subject: [PATCH 4/4] Fix CS --- src/Console/Command/Cli.php | 3 +-- src/Console/Command/Dashboard.php | 28 +++++++++++++--------------- src/Console/Command/Init.php | 2 +- src/Console/Command/Layers.php | 3 +-- src/Console/Command/Local.php | 3 +-- 5 files changed, 17 insertions(+), 22 deletions(-) diff --git a/src/Console/Command/Cli.php b/src/Console/Command/Cli.php index 3bb6e4f85..77dcbdf03 100644 --- a/src/Console/Command/Cli.php +++ b/src/Console/Command/Cli.php @@ -20,8 +20,7 @@ protected function configure(): void ->addArgument('function', InputArgument::REQUIRED) ->addArgument('arguments', InputArgument::OPTIONAL) ->addOption('region', 'r') - ->addOption('profile', 'p') - ; + ->addOption('profile', 'p'); } protected function execute(InputInterface $input, OutputInterface $output): int diff --git a/src/Console/Command/Dashboard.php b/src/Console/Command/Dashboard.php index ad872d007..1f8bec496 100644 --- a/src/Console/Command/Dashboard.php +++ b/src/Console/Command/Dashboard.php @@ -22,8 +22,7 @@ protected function configure(): void ->addOption('host', null, InputOption::VALUE_REQUIRED, '', 'localhost') ->addOption('port', null, InputOption::VALUE_REQUIRED, '', '8000') ->addOption('profile', null, InputOption::VALUE_REQUIRED) - ->addOption('stage', null, InputOption::VALUE_REQUIRED) - ; + ->addOption('stage', null, InputOption::VALUE_REQUIRED); } protected function execute(InputInterface $input, OutputInterface $output): int @@ -48,7 +47,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int return Command::FAILURE; } - $exeFinder = new ExecutableFinder(); + $exeFinder = new ExecutableFinder; if (! $exeFinder->find('docker')) { $io->error( 'The `docker` command is not installed.' . PHP_EOL . @@ -72,11 +71,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int $animation = new LoadingAnimation($io); do { $animation->tick('Retrieving the stack'); - usleep(100*1000); + usleep(100 * 1000); } while ($serverlessInfo->isRunning()); $animation->clear(); - if (!$serverlessInfo->isSuccessful()) { + if (! $serverlessInfo->isSuccessful()) { $io->error('The command `serverless info` failed' . PHP_EOL . $serverlessInfo->getOutput()); return Command::FAILURE; @@ -99,7 +98,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $dockerPull->start(); do { $animation->tick('Retrieving the latest version of the dashboard'); - usleep(100*1000); + usleep(100 * 1000); } while ($dockerPull->isRunning()); $animation->clear(); if (! $dockerPull->isSuccessful()) { @@ -111,17 +110,17 @@ protected function execute(InputInterface $input, OutputInterface $output): int return Command::FAILURE; } - $process = new Process(['docker', 'run', '--rm', '-p', $host . ':' . $port.':8000', '-v', getenv('HOME').'/.aws:/root/.aws:ro', '--env', 'STACKNAME='.$stack, '--env', 'REGION='.$region, '--env', 'AWS_PROFILE='.$profile, 'bref/dashboard']); + $process = new Process(['docker', 'run', '--rm', '-p', $host . ':' . $port . ':8000', '-v', getenv('HOME') . '/.aws:/root/.aws:ro', '--env', 'STACKNAME=' . $stack, '--env', 'REGION=' . $region, '--env', 'AWS_PROFILE=' . $profile, 'bref/dashboard']); $process->setTimeout(null); $process->start(); do { $animation->tick('Starting the dashboard'); - usleep(100*1000); + usleep(100 * 1000); $serverOutput = $process->getOutput() . $process->getErrorOutput(); $hasStarted = (strpos($serverOutput, 'Development Server') !== false); - } while ($process->isRunning() && !$hasStarted); + } while ($process->isRunning() && ! $hasStarted); $animation->clear(); - if (!$process->isRunning()) { + if (! $process->isRunning()) { $io->error([ 'The dashboard failed to start', $process->getErrorOutput(), @@ -132,15 +131,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int $url = "http://$host:$port"; $io->writeln("Dashboard started: $url"); OpenUrl::open($url); - $process->wait(function ($type, $buffer) { - if (Process::ERR === $type) { - echo 'ERR > '.$buffer; + $process->wait(function ($type, $buffer): void { + if ($type === Process::ERR) { + echo 'ERR > ' . $buffer; } else { - echo 'OUT > '.$buffer; + echo 'OUT > ' . $buffer; } }); return $process->getExitCode(); } - } diff --git a/src/Console/Command/Init.php b/src/Console/Command/Init.php index f0d52a786..d55f7a39b 100644 --- a/src/Console/Command/Init.php +++ b/src/Console/Command/Init.php @@ -12,7 +12,7 @@ final class Init extends Command { - protected function configure() + protected function configure(): void { $this->setName('init'); } diff --git a/src/Console/Command/Layers.php b/src/Console/Command/Layers.php index f59530acb..465e187a0 100644 --- a/src/Console/Command/Layers.php +++ b/src/Console/Command/Layers.php @@ -14,8 +14,7 @@ protected function configure(): void $this ->setName('layers') ->setDescription('Displays the versions of the Bref layers') - ->addArgument('region') - ; + ->addArgument('region'); } protected function execute(InputInterface $input, OutputInterface $output): int diff --git a/src/Console/Command/Local.php b/src/Console/Command/Local.php index 91218791b..3b6397955 100644 --- a/src/Console/Command/Local.php +++ b/src/Console/Command/Local.php @@ -30,8 +30,7 @@ protected function configure(): void ->addArgument('data', InputArgument::OPTIONAL) ->addOption('file', 'f', InputOption::VALUE_REQUIRED) ->addOption('handler', null, InputOption::VALUE_REQUIRED) - ->addOption('config', 'c', InputOption::VALUE_REQUIRED) - ; + ->addOption('config', 'c', InputOption::VALUE_REQUIRED); } protected function execute(InputInterface $input, OutputInterface $output): int