diff --git a/src/Command/InstallRecipesCommand.php b/src/Command/InstallRecipesCommand.php index 49992c889..1f43ece7a 100644 --- a/src/Command/InstallRecipesCommand.php +++ b/src/Command/InstallRecipesCommand.php @@ -46,6 +46,7 @@ protected function configure() ->addArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Recipes that should be installed.') ->addOption('force', null, InputOption::VALUE_NONE, 'Overwrite existing files when a new version of a recipe is available') ->addOption('reset', null, InputOption::VALUE_NONE, 'Reset all recipes back to their initial state (should be combined with --force)') + ->addOption('yes', null, InputOption::VALUE_NONE, "Answer prompt questions with 'yes' for all questions.") ; } @@ -135,7 +136,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } } - $this->flex->update(new UpdateEvent($force, (bool) $input->getOption('reset')), $operations); + $this->flex->update(new UpdateEvent($force, (bool) $input->getOption('reset'), (bool) $input->getOption('yes')), $operations); if ($force) { $output = [ diff --git a/src/Configurator/CopyFromPackageConfigurator.php b/src/Configurator/CopyFromPackageConfigurator.php index e71ff0840..f30355ee7 100644 --- a/src/Configurator/CopyFromPackageConfigurator.php +++ b/src/Configurator/CopyFromPackageConfigurator.php @@ -124,8 +124,7 @@ public function copyFile(string $source, string $target, array $options) return; } - $overwrite = $options['force'] ?? false; - if (!$this->options->shouldWriteFile($target, $overwrite)) { + if (!$this->options->shouldWriteFile($target, $options['force'] ?? false, $options['assumeYesForPrompts'] ?? false)) { return; } diff --git a/src/Configurator/CopyFromRecipeConfigurator.php b/src/Configurator/CopyFromRecipeConfigurator.php index 47a360ea1..004c06538 100644 --- a/src/Configurator/CopyFromRecipeConfigurator.php +++ b/src/Configurator/CopyFromRecipeConfigurator.php @@ -127,11 +127,10 @@ private function copyDir(string $source, string $target, array $files, array $op private function copyFile(string $to, string $contents, bool $executable, array $options): string { - $overwrite = $options['force'] ?? false; $basePath = $options['root-dir'] ?? '.'; $copiedFile = $this->getLocalFilePath($basePath, $to); - if (!$this->options->shouldWriteFile($to, $overwrite)) { + if (!$this->options->shouldWriteFile($to, $options['force'] ?? false, $options['assumeYesForPrompts'] ?? false)) { return $copiedFile; } diff --git a/src/Event/UpdateEvent.php b/src/Event/UpdateEvent.php index 06dbe0c5f..68f7547c8 100644 --- a/src/Event/UpdateEvent.php +++ b/src/Event/UpdateEvent.php @@ -18,12 +18,14 @@ class UpdateEvent extends Event { private $force; private $reset; + private $assumeYesForPrompts; - public function __construct(bool $force, bool $reset) + public function __construct(bool $force, bool $reset, bool $assumeYesForPrompts) { $this->name = ScriptEvents::POST_UPDATE_CMD; $this->force = $force; $this->reset = $reset; + $this->assumeYesForPrompts = $assumeYesForPrompts; } public function force(): bool @@ -35,4 +37,9 @@ public function reset(): bool { return $this->reset; } + + public function assumeYesForPrompts(): bool + { + return $this->assumeYesForPrompts; + } } diff --git a/src/Flex.php b/src/Flex.php index 995a45ee1..b7d102092 100644 --- a/src/Flex.php +++ b/src/Flex.php @@ -447,6 +447,7 @@ function ($value) { $this->io->writeError(\sprintf(' - Configuring %s', $this->formatOrigin($recipe))); $this->configurator->install($recipe, $this->lock, [ 'force' => $event instanceof UpdateEvent && $event->force(), + 'assumeYesForPrompts' => $event instanceof UpdateEvent && $event->assumeYesForPrompts(), ]); $manifest = $recipe->getManifest(); if (isset($manifest['post-install-output'])) { @@ -471,6 +472,7 @@ function ($value) { foreach ($postInstallRecipes as $recipe) { $this->configurator->postInstall($recipe, $this->lock, [ 'force' => $event instanceof UpdateEvent && $event->force(), + 'assumeYesForPrompts' => $event instanceof UpdateEvent && $event->assumeYesForPrompts(), ]); } } diff --git a/src/Options.php b/src/Options.php index 96531f391..ee0bb3b3b 100644 --- a/src/Options.php +++ b/src/Options.php @@ -62,7 +62,7 @@ public function expandTargetDir(string $target): string return file_exists($rootDir.'/'.$otherPhpunitDistFile) ? $otherPhpunitDistFile : $result; } - public function shouldWriteFile(string $file, bool $overwrite): bool + public function shouldWriteFile(string $file, bool $overwrite, bool $skipQuestion): bool { if (isset($this->writtenFiles[$file])) { return false; @@ -81,6 +81,10 @@ public function shouldWriteFile(string $file, bool $overwrite): bool return true; } + if ($skipQuestion) { + return true; + } + exec('git status --short --ignored --untracked-files=all -- '.ProcessExecutor::escape($file).' 2>&1', $output, $status); if (0 !== $status) { diff --git a/tests/Command/InstallRecipesCommandTest.php b/tests/Command/InstallRecipesCommandTest.php new file mode 100644 index 000000000..46216af12 --- /dev/null +++ b/tests/Command/InstallRecipesCommandTest.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Flex\Tests\Command; + +use Composer\Console\Application; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Flex\Command\InstallRecipesCommand; +use Symfony\Flex\Event\UpdateEvent; +use Symfony\Flex\Flex; + +class InstallRecipesCommandTest extends TestCase +{ + public function testCommandFlagsPassedDown() + { + $flex = $this->createMock(Flex::class); + $flex->method('update')->willReturnCallback(function (UpdateEvent $event) { + $this->assertTrue($event->reset()); + $this->assertTrue($event->assumeYesForPrompts()); + }); + + $command = new InstallRecipesCommand($flex, __DIR__); + $application = new Application(); + $application->add($command); + $command = $application->find('symfony:recipes:install'); + + $tester = new CommandTester($command); + $tester->execute([ + '--reset' => true, + '--yes' => true, + ]); + } +} diff --git a/tests/OptionsTest.php b/tests/OptionsTest.php new file mode 100644 index 000000000..1064fcbf7 --- /dev/null +++ b/tests/OptionsTest.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Flex\Tests; + +use Composer\IO\IOInterface; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Process\Process; +use Symfony\Flex\Options; + +class OptionsTest extends TestCase +{ + public function testShouldWrite() + { + @mkdir(FLEX_TEST_DIR); + (new Process(['git', 'init'], FLEX_TEST_DIR))->mustRun(); + (new Process(['git', 'config', 'user.name', 'Unit test'], FLEX_TEST_DIR))->mustRun(); + (new Process(['git', 'config', 'user.email', ''], FLEX_TEST_DIR))->mustRun(); + + $filePath = FLEX_TEST_DIR.'/a.txt'; + file_put_contents($filePath, 'a'); + (new Process(['git', 'add', '-A'], FLEX_TEST_DIR))->mustRun(); + (new Process(['git', 'commit', '-m', 'setup of original files'], FLEX_TEST_DIR))->mustRun(); + + file_put_contents($filePath, 'b'); + + $this->assertTrue((new Options([], null))->shouldWriteFile('non-existing-file.txt', false, false)); + $this->assertFalse((new Options([], null))->shouldWriteFile($filePath, false, false)); + + // We don't have an IO, so we don't write the file + $this->assertFalse((new Options([], null))->shouldWriteFile($filePath, true, false)); + + // We have an IO, and it allowed to write the file + $io = $this->createMock(IOInterface::class); + $io->expects($this->once())->method('askConfirmation')->willReturn(true); + $this->assertTrue((new Options([], $io))->shouldWriteFile($filePath, true, false)); + + // We skip all questions, so we're able to write + $this->assertTrue((new Options([], null))->shouldWriteFile($filePath, true, true)); + } +}