diff --git a/src/Command/InitCommand.php b/src/Command/InitCommand.php index 0d1cc94..9cabc75 100644 --- a/src/Command/InitCommand.php +++ b/src/Command/InitCommand.php @@ -4,63 +4,37 @@ namespace Shlinkio\Shlink\Installer\Command; -use Shlinkio\Shlink\Installer\Command\Model\InitOption; -use Shlinkio\Shlink\Installer\Model\CLIOption; -use Shlinkio\Shlink\Installer\Model\ShlinkInitConfig; +use Shlinkio\Shlink\Installer\Command\Model\InitCommandInput; use Shlinkio\Shlink\Installer\Service\InstallationCommandsRunnerInterface; use Shlinkio\Shlink\Installer\Util\InstallationCommand; +use Symfony\Component\Console\Attribute\AsCommand; +use Symfony\Component\Console\Attribute\MapInput; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use function array_reduce; +#[AsCommand( + name: InitCommand::NAME, + description: 'Initializes external dependencies required for Shlink to properly work, like DB, cache warmup, ' + . 'initial GeoLite DB download, etc', +)] class InitCommand extends Command { public const string NAME = 'init'; - private readonly CLIOption $skipInitDb; - private readonly CLIOption $clearDbCache; - private readonly CLIOption $initialApiKey; - private readonly CLIOption $downloadRoadRunnerBin; - private readonly CLIOption $skipDownloadGeoLiteDb; - public function __construct(private readonly InstallationCommandsRunnerInterface $commandsRunner) { parent::__construct(); - - $this->initialApiKey = InitOption::INITIAL_API_KEY->toCLIOption($this); - $this->skipInitDb = InitOption::SKIP_INITIALIZE_DB->toCLIOption($this); - $this->clearDbCache = InitOption::CLEAR_DB_CACHE->toCLIOption($this); - $this->downloadRoadRunnerBin = InitOption::DOWNLOAD_RR_BINARY->toCLIOption($this); - $this->skipDownloadGeoLiteDb = InitOption::SKIP_DOWNLOAD_GEOLITE->toCLIOption($this); - } - - protected function configure(): void - { - $this - ->setName(self::NAME) - ->setDescription( - 'Initializes external dependencies required for Shlink to properly work, like DB, cache warmup, ' - . 'initial GeoLite DB download, etc', - ); } - protected function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(SymfonyStyle $io, InputInterface $input, #[MapInput] InitCommandInput $inputData): int { - $config = new ShlinkInitConfig( - initializeDb: ! $this->skipInitDb->get($input), - clearDbCache: $this->clearDbCache->get($input), - downloadRoadrunnerBinary: $this->downloadRoadRunnerBin->get($input), - generateApiKey: $this->initialApiKey->get($input), - downloadGeoLiteDb: ! $this->skipDownloadGeoLiteDb->get($input), - ); - $commands = [...InstallationCommand::resolveCommandsForConfig($config)]; - $io = new SymfonyStyle($input, $output); + $commands = [...$inputData->resolveCommands()]; return array_reduce($commands, function (bool $carry, array $commandInfo) use ($input, $io): bool { - /** @var array{InstallationCommand, string | null} $commandInfo */ + /** @var array{InstallationCommand, string|null} $commandInfo */ [$command, $arg] = $commandInfo; return $this->commandsRunner->execPhpCommand( @@ -69,6 +43,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int interactive: $input->isInteractive(), args: $arg !== null ? [$arg] : [], ) && $carry; - }, initial: true) ? 0 : -1; + }, initial: true) ? self::SUCCESS : self::FAILURE; } } diff --git a/src/Command/Model/InitCommandInput.php b/src/Command/Model/InitCommandInput.php new file mode 100644 index 0000000..1da335f --- /dev/null +++ b/src/Command/Model/InitCommandInput.php @@ -0,0 +1,70 @@ + + */ + public function resolveCommands(): iterable + { + if (! $this->skipInitializeDb) { + yield [InstallationCommand::DB_CREATE_SCHEMA, null]; + } + + yield [InstallationCommand::DB_MIGRATE, null]; + yield [InstallationCommand::ORM_PROXIES, null]; + + if ($this->clearDbCache) { + yield [InstallationCommand::ORM_CLEAR_CACHE, null]; + } + + if (! $this->skipDownloadGeolite) { + yield [InstallationCommand::GEOLITE_DOWNLOAD_DB, null]; + } + + if ($this->initialApiKey === true) { + yield [InstallationCommand::API_KEY_GENERATE, null]; + } elseif (is_string($this->initialApiKey)) { + yield [InstallationCommand::API_KEY_CREATE, $this->initialApiKey]; + } + + if ($this->downloadRrBinary) { + yield [InstallationCommand::ROAD_RUNNER_BINARY_DOWNLOAD, null]; + } + } +} diff --git a/src/Command/Model/InitOption.php b/src/Command/Model/InitOption.php index 492e53b..1e51c14 100644 --- a/src/Command/Model/InitOption.php +++ b/src/Command/Model/InitOption.php @@ -2,10 +2,7 @@ namespace Shlinkio\Shlink\Installer\Command\Model; -use Shlinkio\Shlink\Installer\Model\CLIOption; -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputOption; - +/** @deprecated */ enum InitOption: string { case SKIP_INITIALIZE_DB = 'skip-initialize-db'; @@ -18,42 +15,4 @@ public function asCliFlag(): string { return '--' . $this->value; } - - public function description(): string - { - return match ($this) { - self::SKIP_INITIALIZE_DB => - 'Skip the initial empty database creation. It will make this command fail on a later stage if the ' - . 'database was not created manually.', - self::CLEAR_DB_CACHE => 'Clear the database metadata cache.', - self::INITIAL_API_KEY => - 'Create an initial admin API key. A random one will be generated and printed if no value is provided.', - self::DOWNLOAD_RR_BINARY => - 'Download a RoadRunner binary. Useful only if you plan to serve Shlink with Roadrunner.', - self::SKIP_DOWNLOAD_GEOLITE => - 'Skip downloading the initial GeoLite DB file. Shlink will try to download it the first time it needs ' - . 'to geolocate visits.', - }; - } - - public function valueType(): int - { - return match ($this) { - self::INITIAL_API_KEY => InputOption::VALUE_OPTIONAL, - default => InputOption::VALUE_NONE, - }; - } - - public function defaultValue(): bool|null - { - return match ($this) { - self::INITIAL_API_KEY => false, - default => null, - }; - } - - public function toCLIOption(Command $command): CLIOption - { - return new CLIOption($command, $this); - } } diff --git a/src/Model/CLIOption.php b/src/Model/CLIOption.php deleted file mode 100644 index 0b8876d..0000000 --- a/src/Model/CLIOption.php +++ /dev/null @@ -1,28 +0,0 @@ -addOption( - $initOption->value, - null, - $initOption->valueType(), - $initOption->description(), - $this->initOption->defaultValue(), - ); - } - - public function get(InputInterface $input): mixed - { - return $input->getOption($this->initOption->value); - } -} diff --git a/src/Model/ShlinkInitConfig.php b/src/Model/ShlinkInitConfig.php deleted file mode 100644 index 22c8ead..0000000 --- a/src/Model/ShlinkInitConfig.php +++ /dev/null @@ -1,22 +0,0 @@ - - */ - public static function resolveCommandsForConfig(ShlinkInitConfig $config): iterable - { - if ($config->initializeDb) { - yield [self::DB_CREATE_SCHEMA, null]; - } - - yield [self::DB_MIGRATE, null]; - yield [self::ORM_PROXIES, null]; - - if ($config->clearDbCache) { - yield [self::ORM_CLEAR_CACHE, null]; - } - - if ($config->downloadGeoLiteDb) { - yield [self::GEOLITE_DOWNLOAD_DB, null]; - } - - if ($config->generateApiKey === null) { - yield [self::API_KEY_GENERATE, null]; - } elseif (is_string($config->generateApiKey)) { - yield [self::API_KEY_CREATE, $config->generateApiKey]; - } - - if ($config->downloadRoadrunnerBinary) { - yield [self::ROAD_RUNNER_BINARY_DOWNLOAD, null]; - } - } } diff --git a/test/Command/InitCommandTest.php b/test/Command/InitCommandTest.php index c5d1e07..26b43e0 100644 --- a/test/Command/InitCommandTest.php +++ b/test/Command/InitCommandTest.php @@ -13,6 +13,7 @@ use Shlinkio\Shlink\Installer\Service\InstallationCommandsRunnerInterface; use Shlinkio\Shlink\Installer\Util\InstallationCommand; use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Tester\CommandTester; use function count; @@ -111,7 +112,7 @@ public function properExitCodeIsReturnedBasedOnCommandsExecution(bool $result, i public static function provideExitCodes(): iterable { - yield 'success' => [true, 0]; - yield 'error' => [false, -1]; + yield 'success' => [true, Command::SUCCESS]; + yield 'error' => [false, Command::FAILURE]; } }