From 4add071f8622c6d54f19ee331a0d313c4f7fed98 Mon Sep 17 00:00:00 2001 From: James Titcumb Date: Thu, 11 Sep 2025 15:03:27 +0100 Subject: [PATCH] Add basic ability to pie install non-interactively --- src/Command/CommandHelper.php | 24 ++++-- .../InstallExtensionsForProjectCommand.php | 73 ++++++++++++++----- 2 files changed, 70 insertions(+), 27 deletions(-) diff --git a/src/Command/CommandHelper.php b/src/Command/CommandHelper.php index 61987fd..9af22ad 100644 --- a/src/Command/CommandHelper.php +++ b/src/Command/CommandHelper.php @@ -51,14 +51,15 @@ use const PHP_VERSION; /** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */ final class CommandHelper { - public const ARG_REQUESTED_PACKAGE_AND_VERSION = 'requested-package-and-version'; - public const OPTION_WITH_PHP_CONFIG = 'with-php-config'; - public const OPTION_WITH_PHP_PATH = 'with-php-path'; - public const OPTION_WITH_PHPIZE_PATH = 'with-phpize-path'; - public const OPTION_WORKING_DIRECTORY = 'working-dir'; - private const OPTION_MAKE_PARALLEL_JOBS = 'make-parallel-jobs'; - private const OPTION_SKIP_ENABLE_EXTENSION = 'skip-enable-extension'; - private const OPTION_FORCE = 'force'; + public const ARG_REQUESTED_PACKAGE_AND_VERSION = 'requested-package-and-version'; + public const OPTION_WITH_PHP_CONFIG = 'with-php-config'; + public const OPTION_WITH_PHP_PATH = 'with-php-path'; + public const OPTION_WITH_PHPIZE_PATH = 'with-phpize-path'; + public const OPTION_WORKING_DIRECTORY = 'working-dir'; + public const OPTION_ALLOW_NON_INTERACTIVE_PROJECT_INSTALL = 'allow-non-interactive-project-install'; + private const OPTION_MAKE_PARALLEL_JOBS = 'make-parallel-jobs'; + private const OPTION_SKIP_ENABLE_EXTENSION = 'skip-enable-extension'; + private const OPTION_FORCE = 'force'; /** @psalm-suppress UnusedConstructor */ private function __construct() @@ -125,6 +126,13 @@ final class CommandHelper self::configurePhpConfigOptions($command); + $command->addOption( + self::OPTION_ALLOW_NON_INTERACTIVE_PROJECT_INSTALL, + null, + InputOption::VALUE_NONE, + 'When installing a PHP project, allow non-interactive project installations. Only used in certain contexts.', + ); + /** * Allows additional options for the `./configure` command to be passed here. * Note, this means you probably need to call {@see self::validateInput()} to validate the input manually... diff --git a/src/Command/InstallExtensionsForProjectCommand.php b/src/Command/InstallExtensionsForProjectCommand.php index 2f9131c..391063b 100644 --- a/src/Command/InstallExtensionsForProjectCommand.php +++ b/src/Command/InstallExtensionsForProjectCommand.php @@ -17,6 +17,7 @@ use Php\Pie\Installing\InstallForPhpProject\DetermineExtensionsRequired; use Php\Pie\Installing\InstallForPhpProject\FindMatchingPackages; use Php\Pie\Installing\InstallForPhpProject\InstallPiePackageFromPath; use Php\Pie\Installing\InstallForPhpProject\InstallSelectedPackage; +use Php\Pie\Platform; use Php\Pie\Platform\InstalledPiePackages; use Php\Pie\Util\Emoji; use Psr\Container\ContainerInterface; @@ -24,6 +25,7 @@ use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\NullOutput; use Symfony\Component\Console\Output\OutputInterface; @@ -123,6 +125,17 @@ final class InstallExtensionsForProjectCommand extends Command return $exit; } + $allowNonInteractive = $input->hasOption(CommandHelper::OPTION_ALLOW_NON_INTERACTIVE_PROJECT_INSTALL) && $input->getOption(CommandHelper::OPTION_ALLOW_NON_INTERACTIVE_PROJECT_INSTALL); + if (! Platform::isInteractive() && ! $allowNonInteractive) { + $output->writeln(sprintf( + 'Aborting! You are not running in interactive mode, and --%s was not specified.', + CommandHelper::OPTION_ALLOW_NON_INTERACTIVE_PROJECT_INSTALL, + )); + // @todo more details + + return Command::FAILURE; + } + $targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $output); $output->writeln(sprintf( @@ -221,37 +234,59 @@ final class InstallExtensionsForProjectCommand extends Command return; } - $choiceQuestion = new ChoiceQuestion( - "\nThe following packages may be suitable, which would you like to install: ", - array_merge( - ['None'], - array_map( - static function (array $match): string { - return sprintf('%s: %s', $match['name'], $match['description'] ?? 'no description available'); - }, - $matches, - ), - ), - 0, - ); - - $selectedPackageAnswer = (string) $helper->ask($input, $output, $choiceQuestion); - - if ($selectedPackageAnswer === 'None') { - $output->writeln('Okay I won\'t install anything for ' . $extension->name()); + if (! Platform::isInteractive() && count($matches) > 1) { $anyErrorsHappened = true; + // @todo Figure out if there is a way to improve this, safely + $output->writeln(sprintf( + "Multiple packages were found for %s:\n %s\n\nThis means you cannot `pie install` this project interactively for now.", + $extension->nameWithExtPrefix(), + implode("\n ", array_column($matches, 'name')), + )); + return; } + if (Platform::isInteractive()) { + $choiceQuestion = new ChoiceQuestion( + "\nThe following packages may be suitable, which would you like to install: ", + array_merge( + ['None'], + array_map( + static function (array $match): string { + return sprintf('%s: %s', $match['name'], $match['description'] ?? 'no description available'); + }, + $matches, + ), + ), + 0, + ); + + $selectedPackageAnswer = (string) $helper->ask($input, $output, $choiceQuestion); + + if ($selectedPackageAnswer === 'None') { + $output->writeln('Okay I won\'t install anything for ' . $extension->name()); + $anyErrorsHappened = true; + + return; + } + $selectedPackageName = substr($selectedPackageAnswer, 0, (int) strpos($selectedPackageAnswer, ':')); + } else { + $selectedPackageName = $matches[0]['name']; + } + $requestInstallConstraint = ''; if ($linkRequiresConstraint !== '*') { $requestInstallConstraint = ':' . $linkRequiresConstraint; } try { + $output->writeln( + sprintf('Invoking pie install of %s%s', $selectedPackageName, $requestInstallConstraint), + OutputInterface::VERBOSITY_VERBOSE, + ); $this->installSelectedPackage->withPieCli( - substr($selectedPackageAnswer, 0, (int) strpos($selectedPackageAnswer, ':')) . $requestInstallConstraint, + $selectedPackageName . $requestInstallConstraint, $input, $output, );