From 6bf945017e7921559a3bc8be62818a894f10f00a Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 28 Oct 2022 14:25:18 +0200 Subject: [PATCH] Add interactive prompt for which script/binary to run if run-script/exec is called without arg, fixes #11128 (#11157) --- src/Composer/Command/ExecCommand.php | 20 ++++++++ src/Composer/Command/RunScriptCommand.php | 56 +++++++++++++++++++---- src/Composer/IO/ConsoleIO.php | 5 ++ 3 files changed, 73 insertions(+), 8 deletions(-) diff --git a/src/Composer/Command/ExecCommand.php b/src/Composer/Command/ExecCommand.php index abaa1aff3..905b28b99 100644 --- a/src/Composer/Command/ExecCommand.php +++ b/src/Composer/Command/ExecCommand.php @@ -51,6 +51,26 @@ EOT ; } + protected function interact(InputInterface $input, OutputInterface $output): void + { + $binaries = $this->getBinaries(false); + if (count($binaries) === 0) { + return; + } + + $io = $this->getIO(); + /** @var string $binary */ + $binary = $io->select( + 'Binary to run: ', + $binaries, + '', + 1, + 'Invalid binary name "%s"' + ); + + $input->setArgument('binary', $binaries[$binary]); + } + protected function execute(InputInterface $input, OutputInterface $output) { $composer = $this->requireComposer(); diff --git a/src/Composer/Command/RunScriptCommand.php b/src/Composer/Command/RunScriptCommand.php index 4e1bccd27..20c419500 100644 --- a/src/Composer/Command/RunScriptCommand.php +++ b/src/Composer/Command/RunScriptCommand.php @@ -52,7 +52,7 @@ class RunScriptCommand extends BaseCommand ->setDescription('Runs the scripts defined in composer.json') ->setDefinition([ new InputArgument('script', InputArgument::OPTIONAL, 'Script name to run.', null, function () { - return array_keys($this->requireComposer()->getPackage()->getScripts()); + return array_map(static function ($script) { return $script['name']; }, $this->getScripts()); }), new InputArgument('args', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, ''), new InputOption('timeout', null, InputOption::VALUE_REQUIRED, 'Sets script timeout in seconds, or 0 for never.'), @@ -72,6 +72,29 @@ EOT ; } + protected function interact(InputInterface $input, OutputInterface $output): void + { + $scripts = $this->getScripts(); + if (count($scripts) === 0) { + return; + } + + $options = []; + foreach ($scripts as $script) { + $options[$script['name']] = $script['description']; + } + $io = $this->getIO(); + $script = $io->select( + 'Script to run: ', + $options, + '', + 1, + 'Invalid script name "%s"' + ); + + $input->setArgument('script', $script); + } + protected function execute(InputInterface $input, OutputInterface $output): int { if ($input->getOption('list')) { @@ -114,15 +137,34 @@ EOT protected function listScripts(OutputInterface $output): int { - $scripts = $this->requireComposer()->getPackage()->getScripts(); - - if (!count($scripts)) { + $scripts = $this->getScripts(); + if (count($scripts) === 0) { return 0; } $io = $this->getIO(); $io->writeError('scripts:'); $table = []; + foreach ($scripts as $script) { + $table[] = [' '.$script['name'], $script['description']]; + } + + $this->renderTable($table, $output); + + return 0; + } + + /** + * @return list + */ + private function getScripts(): array + { + $scripts = $this->requireComposer()->getPackage()->getScripts(); + if (count($scripts) === 0) { + return []; + } + + $result = []; foreach ($scripts as $name => $script) { $description = ''; try { @@ -133,11 +175,9 @@ EOT } catch (\Symfony\Component\Console\Exception\CommandNotFoundException $e) { // ignore scripts that have no command associated, like native Composer script listeners } - $table[] = [' '.$name, $description]; + $result[] = ['name' => $name, 'description' => $description]; } - $this->renderTable($table, $output); - - return 0; + return $result; } } diff --git a/src/Composer/IO/ConsoleIO.php b/src/Composer/IO/ConsoleIO.php index 46ba7a4a1..8ecea4268 100644 --- a/src/Composer/IO/ConsoleIO.php +++ b/src/Composer/IO/ConsoleIO.php @@ -310,6 +310,11 @@ class ConsoleIO extends BaseIO $result = $helper->ask($this->input, $this->getErrorOutput(), $question); + $isAssoc = (bool) \count(array_filter(array_keys($choices), 'is_string')); + if ($isAssoc) { + return $result; + } + if (!is_array($result)) { return (string) array_search($result, $choices, true); }