diff --git a/src/Configurator.php b/src/Configurator.php index e9f43357d..ba69cb485 100644 --- a/src/Configurator.php +++ b/src/Configurator.php @@ -44,22 +44,22 @@ public function __construct(Composer $composer, IOInterface $io, Options $option ]; } - public function install(Recipe $recipe, array $options = []) + public function install(Recipe $recipe, Lock $lock, array $options = []) { $manifest = $recipe->getManifest(); foreach (array_keys($this->configurators) as $key) { if (isset($manifest[$key])) { - $this->get($key)->configure($recipe, $manifest[$key], $options); + $this->get($key)->configure($recipe, $manifest[$key], $lock, $options); } } } - public function unconfigure(Recipe $recipe) + public function unconfigure(Recipe $recipe, Lock $lock) { $manifest = $recipe->getManifest(); foreach (array_keys($this->configurators) as $key) { if (isset($manifest[$key])) { - $this->get($key)->unconfigure($recipe, $manifest[$key]); + $this->get($key)->unconfigure($recipe, $manifest[$key], $lock); } } } diff --git a/src/Configurator/AbstractConfigurator.php b/src/Configurator/AbstractConfigurator.php index 7f47fa9b0..ec05dacf0 100644 --- a/src/Configurator/AbstractConfigurator.php +++ b/src/Configurator/AbstractConfigurator.php @@ -13,6 +13,7 @@ use Composer\Composer; use Composer\IO\IOInterface; +use Symfony\Flex\Lock; use Symfony\Flex\Options; use Symfony\Flex\Path; use Symfony\Flex\Recipe; @@ -35,9 +36,9 @@ public function __construct(Composer $composer, IOInterface $io, Options $option $this->path = new Path($options->get('root-dir')); } - abstract public function configure(Recipe $recipe, $config, array $options = []); + abstract public function configure(Recipe $recipe, $config, Lock $lock, array $options = []); - abstract public function unconfigure(Recipe $recipe, $config); + abstract public function unconfigure(Recipe $recipe, $config, Lock $lock); protected function write($messages) { diff --git a/src/Configurator/BundlesConfigurator.php b/src/Configurator/BundlesConfigurator.php index ac7a30fba..db3fc7d48 100644 --- a/src/Configurator/BundlesConfigurator.php +++ b/src/Configurator/BundlesConfigurator.php @@ -11,6 +11,7 @@ namespace Symfony\Flex\Configurator; +use Symfony\Flex\Lock; use Symfony\Flex\Recipe; /** @@ -18,7 +19,7 @@ */ class BundlesConfigurator extends AbstractConfigurator { - public function configure(Recipe $recipe, $bundles, array $options = []) + public function configure(Recipe $recipe, $bundles, Lock $lock, array $options = []) { $this->write('Enabling the package as a Symfony bundle'); $file = $this->getConfFile(); @@ -38,7 +39,7 @@ public function configure(Recipe $recipe, $bundles, array $options = []) $this->dump($file, $registered); } - public function unconfigure(Recipe $recipe, $bundles) + public function unconfigure(Recipe $recipe, $bundles, Lock $lock) { $this->write('Disabling the Symfony bundle'); $file = $this->getConfFile(); diff --git a/src/Configurator/ComposerScriptsConfigurator.php b/src/Configurator/ComposerScriptsConfigurator.php index 3d26b1f2a..0ce5a7210 100644 --- a/src/Configurator/ComposerScriptsConfigurator.php +++ b/src/Configurator/ComposerScriptsConfigurator.php @@ -14,6 +14,7 @@ use Composer\Factory; use Composer\Json\JsonFile; use Composer\Json\JsonManipulator; +use Symfony\Flex\Lock; use Symfony\Flex\Recipe; /** @@ -21,7 +22,7 @@ */ class ComposerScriptsConfigurator extends AbstractConfigurator { - public function configure(Recipe $recipe, $scripts, array $options = []) + public function configure(Recipe $recipe, $scripts, Lock $lock, array $options = []) { $json = new JsonFile(Factory::getComposerFile()); @@ -35,7 +36,7 @@ public function configure(Recipe $recipe, $scripts, array $options = []) file_put_contents($json->getPath(), $manipulator->getContents()); } - public function unconfigure(Recipe $recipe, $scripts) + public function unconfigure(Recipe $recipe, $scripts, Lock $lock) { $json = new JsonFile(Factory::getComposerFile()); diff --git a/src/Configurator/ContainerConfigurator.php b/src/Configurator/ContainerConfigurator.php index 5d57a2896..22ce207e2 100644 --- a/src/Configurator/ContainerConfigurator.php +++ b/src/Configurator/ContainerConfigurator.php @@ -11,6 +11,7 @@ namespace Symfony\Flex\Configurator; +use Symfony\Flex\Lock; use Symfony\Flex\Recipe; /** @@ -18,13 +19,13 @@ */ class ContainerConfigurator extends AbstractConfigurator { - public function configure(Recipe $recipe, $parameters, array $options = []) + public function configure(Recipe $recipe, $parameters, Lock $lock, array $options = []) { $this->write('Setting parameters'); $this->addParameters($parameters); } - public function unconfigure(Recipe $recipe, $parameters) + public function unconfigure(Recipe $recipe, $parameters, Lock $lock) { $this->write('Unsetting parameters'); $target = $this->options->get('root-dir').'/'.$this->options->expandTargetDir('%CONFIG_DIR%/services.yaml'); diff --git a/src/Configurator/CopyFromPackageConfigurator.php b/src/Configurator/CopyFromPackageConfigurator.php index 4813bef2e..3df308f10 100644 --- a/src/Configurator/CopyFromPackageConfigurator.php +++ b/src/Configurator/CopyFromPackageConfigurator.php @@ -12,6 +12,7 @@ namespace Symfony\Flex\Configurator; use LogicException; +use Symfony\Flex\Lock; use Symfony\Flex\Recipe; /** @@ -19,26 +20,29 @@ */ class CopyFromPackageConfigurator extends AbstractConfigurator { - public function configure(Recipe $recipe, $config, array $options = []) + public function configure(Recipe $recipe, $config, Lock $lock, array $options = []) { $this->write('Setting configuration and copying files'); $packageDir = $this->composer->getInstallationManager()->getInstallPath($recipe->getPackage()); - $this->copyFiles($config, $packageDir, $this->options->get('root-dir'), $options['force'] ?? false); + $options = array_merge($this->options->toArray(), $options); + + $this->copyFiles($config, $packageDir, $options); } - public function unconfigure(Recipe $recipe, $config) + public function unconfigure(Recipe $recipe, $config, Lock $lock) { $this->write('Removing configuration and files'); $packageDir = $this->composer->getInstallationManager()->getInstallPath($recipe->getPackage()); $this->removeFiles($config, $packageDir, $this->options->get('root-dir')); } - private function copyFiles(array $manifest, string $from, string $to, bool $overwrite = false) + private function copyFiles(array $manifest, string $from, array $options) { + $to = $options['root-dir'] ?? '.'; foreach ($manifest as $source => $target) { $target = $this->options->expandTargetDir($target); if ('/' === substr($source, -1)) { - $this->copyDir($this->path->concatenate([$from, $source]), $this->path->concatenate([$to, $target]), $overwrite); + $this->copyDir($this->path->concatenate([$from, $source]), $this->path->concatenate([$to, $target]), $options); } else { $targetPath = $this->path->concatenate([$to, $target]); if (!is_dir(\dirname($targetPath))) { @@ -46,7 +50,7 @@ private function copyFiles(array $manifest, string $from, string $to, bool $over $this->write(sprintf('Created "%s"', $this->path->relativize(\dirname($targetPath)))); } - $this->copyFile($this->path->concatenate([$from, $source]), $targetPath, $overwrite); + $this->copyFile($this->path->concatenate([$from, $source]), $targetPath, $options); } } } @@ -67,7 +71,7 @@ private function removeFiles(array $manifest, string $from, string $to) } } - private function copyDir(string $source, string $target, bool $overwrite) + private function copyDir(string $source, string $target, array $options) { if (!is_dir($target)) { mkdir($target, 0777, true); @@ -82,13 +86,14 @@ private function copyDir(string $source, string $target, bool $overwrite) $this->write(sprintf('Created "%s"', $this->path->relativize($targetPath))); } } elseif (!file_exists($targetPath)) { - $this->copyFile($item, $targetPath, $overwrite); + $this->copyFile($item, $targetPath, $options); } } } - public function copyFile(string $source, string $target, bool $overwrite = false) + public function copyFile(string $source, string $target, array $options) { + $overwrite = $options['force'] ?? false; if (!$this->options->shouldWriteFile($target, $overwrite)) { return; } diff --git a/src/Configurator/CopyFromRecipeConfigurator.php b/src/Configurator/CopyFromRecipeConfigurator.php index ce246f3f7..322f80466 100644 --- a/src/Configurator/CopyFromRecipeConfigurator.php +++ b/src/Configurator/CopyFromRecipeConfigurator.php @@ -11,6 +11,7 @@ namespace Symfony\Flex\Configurator; +use Symfony\Flex\Lock; use Symfony\Flex\Recipe; /** @@ -18,44 +19,83 @@ */ class CopyFromRecipeConfigurator extends AbstractConfigurator { - public function configure(Recipe $recipe, $config, array $options = []) + public function configure(Recipe $recipe, $config, Lock $lock, array $options = []) { $this->write('Setting configuration and copying files'); - $this->copyFiles($config, $recipe->getFiles(), $this->options->get('root-dir'), $options['force'] ?? false); + $options = array_merge($this->options->toArray(), $options); + + $lock->add($recipe->getName(), ['files' => $this->copyFiles($config, $recipe->getFiles(), $options)]); } - public function unconfigure(Recipe $recipe, $config) + public function unconfigure(Recipe $recipe, $config, Lock $lock) { $this->write('Removing configuration and files'); - $this->removeFiles($config, $recipe->getFiles(), $this->options->get('root-dir')); + $this->removeFiles($config, $this->getRemovableFilesFromRecipeAndLock($recipe, $lock), $this->options->get('root-dir')); + } + + private function getRemovableFilesFromRecipeAndLock(Recipe $recipe, Lock $lock): array + { + $lockedFiles = array_unique( + array_reduce( + array_column($lock->all(), 'files'), + function (array $carry, array $package) { + return array_merge($carry, $package); + }, + [] + ) + ); + + $removableFiles = $recipe->getFiles(); + foreach ($lockedFiles as $file) { + if (isset($removableFiles[$file])) { + unset($removableFiles[$file]); + } + } + + return $removableFiles; } - private function copyFiles(array $manifest, array $files, string $to, bool $overwrite = false) + private function copyFiles(array $manifest, array $files, array $options): array { + $copiedFiles = []; + $to = $options['root-dir'] ?? '.'; + foreach ($manifest as $source => $target) { $target = $this->options->expandTargetDir($target); if ('/' === substr($source, -1)) { - $this->copyDir($source, $this->path->concatenate([$to, $target]), $files, $overwrite); + $copiedFiles = array_merge( + $copiedFiles, + $this->copyDir($source, $this->path->concatenate([$to, $target]), $files, $options) + ); } else { - $this->copyFile($this->path->concatenate([$to, $target]), $files[$source]['contents'], $files[$source]['executable'], $overwrite); + $copiedFiles[] = $this->copyFile($this->path->concatenate([$to, $target]), $files[$source]['contents'], $files[$source]['executable'], $options); } } + + return $copiedFiles; } - private function copyDir(string $source, string $target, array $files, bool $overwrite = false) + private function copyDir(string $source, string $target, array $files, array $options): array { + $copiedFiles = []; foreach ($files as $file => $data) { if (0 === strpos($file, $source)) { $file = $this->path->concatenate([$target, substr($file, \strlen($source))]); - $this->copyFile($file, $data['contents'], $data['executable'], $overwrite); + $copiedFiles[] = $this->copyFile($file, $data['contents'], $data['executable'], $options); } } + + return $copiedFiles; } - private function copyFile(string $to, string $contents, bool $executable, bool $overwrite = false) + private function copyFile(string $to, string $contents, bool $executable, array $options): string { + $overwrite = $options['force'] ?? false; + $basePath = $options['root-dir'] ?? '.'; + $copiedFile = str_replace($basePath.\DIRECTORY_SEPARATOR, '', $to); + if (!$this->options->shouldWriteFile($to, $overwrite)) { - return; + return $copiedFile; } if (!is_dir(\dirname($to))) { @@ -68,6 +108,8 @@ private function copyFile(string $to, string $contents, bool $executable, bool $ } $this->write(sprintf('Created "%s"', $this->path->relativize($to))); + + return $copiedFile; } private function removeFiles(array $manifest, array $files, string $to) diff --git a/src/Configurator/EnvConfigurator.php b/src/Configurator/EnvConfigurator.php index 1542a5a29..4ff74899a 100644 --- a/src/Configurator/EnvConfigurator.php +++ b/src/Configurator/EnvConfigurator.php @@ -11,6 +11,7 @@ namespace Symfony\Flex\Configurator; +use Symfony\Flex\Lock; use Symfony\Flex\Recipe; /** @@ -18,7 +19,7 @@ */ class EnvConfigurator extends AbstractConfigurator { - public function configure(Recipe $recipe, $vars, array $options = []) + public function configure(Recipe $recipe, $vars, Lock $lock, array $options = []) { $this->write('Added environment variable defaults'); @@ -28,7 +29,7 @@ public function configure(Recipe $recipe, $vars, array $options = []) } } - public function unconfigure(Recipe $recipe, $vars) + public function unconfigure(Recipe $recipe, $vars, Lock $lock) { $this->unconfigureEnvFiles($recipe, $vars); $this->unconfigurePhpUnit($recipe, $vars); diff --git a/src/Configurator/GitignoreConfigurator.php b/src/Configurator/GitignoreConfigurator.php index d02c6584d..db57f28ea 100644 --- a/src/Configurator/GitignoreConfigurator.php +++ b/src/Configurator/GitignoreConfigurator.php @@ -11,6 +11,7 @@ namespace Symfony\Flex\Configurator; +use Symfony\Flex\Lock; use Symfony\Flex\Recipe; /** @@ -18,7 +19,7 @@ */ class GitignoreConfigurator extends AbstractConfigurator { - public function configure(Recipe $recipe, $vars, array $options = []) + public function configure(Recipe $recipe, $vars, Lock $lock, array $options = []) { $this->write('Added entries to .gitignore'); @@ -39,7 +40,7 @@ public function configure(Recipe $recipe, $vars, array $options = []) } } - public function unconfigure(Recipe $recipe, $vars) + public function unconfigure(Recipe $recipe, $vars, Lock $lock) { $file = $this->options->get('root-dir').'/.gitignore'; if (!file_exists($file)) { diff --git a/src/Configurator/MakefileConfigurator.php b/src/Configurator/MakefileConfigurator.php index 9011bcc47..932881fd4 100644 --- a/src/Configurator/MakefileConfigurator.php +++ b/src/Configurator/MakefileConfigurator.php @@ -11,6 +11,7 @@ namespace Symfony\Flex\Configurator; +use Symfony\Flex\Lock; use Symfony\Flex\Recipe; /** @@ -18,7 +19,7 @@ */ class MakefileConfigurator extends AbstractConfigurator { - public function configure(Recipe $recipe, $definitions, array $options = []) + public function configure(Recipe $recipe, $definitions, Lock $lock, array $options = []) { $this->write('Added Makefile entries'); @@ -53,7 +54,7 @@ public function configure(Recipe $recipe, $definitions, array $options = []) } } - public function unconfigure(Recipe $recipe, $vars) + public function unconfigure(Recipe $recipe, $vars, Lock $lock) { if (!file_exists($makefile = $this->options->get('root-dir').'/Makefile')) { return; diff --git a/src/Flex.php b/src/Flex.php index 3b2f1e236..2cd78ac2f 100644 --- a/src/Flex.php +++ b/src/Flex.php @@ -368,7 +368,7 @@ function ($value) { switch ($recipe->getJob()) { case 'install': $this->io->writeError(sprintf(' - Configuring %s', $this->formatOrigin($recipe->getOrigin()))); - $this->configurator->install($recipe, [ + $this->configurator->install($recipe, $this->lock, [ 'force' => $event instanceof UpdateEvent && $event->force(), ]); $manifest = $recipe->getManifest(); @@ -383,7 +383,7 @@ function ($value) { break; case 'uninstall': $this->io->writeError(sprintf(' - Unconfiguring %s', $this->formatOrigin($recipe->getOrigin()))); - $this->configurator->unconfigure($recipe); + $this->configurator->unconfigure($recipe, $this->lock); break; } } @@ -605,7 +605,7 @@ private function fetchRecipes(): array if ($ref && ($locks[$name]['recipe']['ref'] ?? null) === $ref) { continue; } - $this->lock->add($name, $locks[$name]); + $this->lock->set($name, $locks[$name]); } elseif ($operation instanceof UninstallOperation) { if (!$this->lock->has($name)) { continue; diff --git a/src/Lock.php b/src/Lock.php index 4f4a17bfa..f7e2993b7 100644 --- a/src/Lock.php +++ b/src/Lock.php @@ -34,12 +34,18 @@ public function has($name): bool return array_key_exists($name, $this->lock); } + public function add($name, $data) + { + $current = $this->lock[$name] ?? []; + $this->lock[$name] = array_merge($current, $data); + } + public function get($name) { return $this->lock[$name] ?? null; } - public function add($name, $data) + public function set($name, $data) { $this->lock[$name] = $data; } @@ -58,4 +64,9 @@ public function write() @unlink($this->json->getPath()); } } + + public function all(): array + { + return $this->lock; + } } diff --git a/src/Options.php b/src/Options.php index c8bd0e079..6aedf81de 100644 --- a/src/Options.php +++ b/src/Options.php @@ -80,4 +80,9 @@ public function shouldWriteFile(string $file, bool $overwrite): bool return (bool) $this->io && $this->io->askConfirmation(\sprintf('File "%s" has uncommitted changes, overwrite? [y/N] ', $name), false); } + + public function toArray(): array + { + return $this->options; + } } diff --git a/tests/Configurator/BundlesConfiguratorTest.php b/tests/Configurator/BundlesConfiguratorTest.php index 1313db53e..08d9e2d73 100644 --- a/tests/Configurator/BundlesConfiguratorTest.php +++ b/tests/Configurator/BundlesConfiguratorTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Flex\Configurator\BundlesConfigurator; +use Symfony\Flex\Lock; use Symfony\Flex\Options; use Symfony\Flex\Recipe; @@ -29,12 +30,13 @@ public function testConfigure() ); $recipe = $this->getMockBuilder(Recipe::class)->disableOriginalConstructor()->getMock(); + $lock = $this->getMockBuilder(Lock::class)->disableOriginalConstructor()->getMock(); @unlink($config); $configurator->configure($recipe, [ 'FooBundle' => ['dev', 'test'], 'Symfony\Bundle\FrameworkBundle\FrameworkBundle' => ['all'], - ]); + ], $lock); $this->assertEquals(<<getMockBuilder(Recipe::class)->disableOriginalConstructor()->getMock(); + $lock = $this->getMockBuilder(Lock::class)->disableOriginalConstructor()->getMock(); $config = FLEX_TEST_DIR.'/config/services.yaml'; file_put_contents( $config, @@ -39,7 +41,7 @@ public function testConfigure() $this->getMockBuilder(IOInterface::class)->getMock(), new Options(['config-dir' => 'config', 'root-dir' => FLEX_TEST_DIR]) ); - $configurator->configure($recipe, ['locale' => 'en']); + $configurator->configure($recipe, ['locale' => 'en'], $lock); $this->assertEquals(<<unconfigure($recipe, ['locale' => 'en']); + $configurator->unconfigure($recipe, ['locale' => 'en'], $lock); $this->assertEquals(<<getMockBuilder(Recipe::class)->disableOriginalConstructor()->getMock(); + $lock = $this->getMockBuilder(Lock::class)->disableOriginalConstructor()->getMock(); $config = FLEX_TEST_DIR.'/config/services.yaml'; file_put_contents( $config, @@ -77,7 +80,7 @@ public function testConfigureWithoutParametersKey() $this->getMockBuilder(IOInterface::class)->getMock(), new Options(['config-dir' => 'config', 'root-dir' => FLEX_TEST_DIR]) ); - $configurator->configure($recipe, ['locale' => 'en']); + $configurator->configure($recipe, ['locale' => 'en'], $lock); $this->assertEquals(<<unconfigure($recipe, ['locale' => 'en']); + $configurator->unconfigure($recipe, ['locale' => 'en'], $lock); $this->assertEquals(<<getMockBuilder(Recipe::class)->disableOriginalConstructor()->getMock(); + $lock = $this->getMockBuilder(Lock::class)->disableOriginalConstructor()->getMock(); $config = FLEX_TEST_DIR.'/config/services.yaml'; file_put_contents( $config, @@ -116,7 +120,7 @@ public function testConfigureWithoutDuplicated() $this->getMockBuilder(IOInterface::class)->getMock(), new Options(['config-dir' => 'config', 'root-dir' => FLEX_TEST_DIR]) ); - $configurator->configure($recipe, ['locale' => 'en']); + $configurator->configure($recipe, ['locale' => 'en'], $lock); $this->assertEquals(<<unconfigure($recipe, ['locale' => 'en']); + $configurator->unconfigure($recipe, ['locale' => 'en'], $lock); $this->assertEquals(<<getMockBuilder(Recipe::class)->disableOriginalConstructor()->getMock(); + $lock = $this->getMockBuilder(Lock::class)->disableOriginalConstructor()->getMock(); $config = FLEX_TEST_DIR.'/config/services.yaml'; file_put_contents( $config, @@ -159,7 +164,7 @@ public function testConfigureWithComplexContent() $this->getMockBuilder(IOInterface::class)->getMock(), new Options(['config-dir' => 'config', 'root-dir' => FLEX_TEST_DIR]) ); - $configurator->configure($recipe, ['locale' => 'en', 'foobar' => 'baz']); + $configurator->configure($recipe, ['locale' => 'en', 'foobar' => 'baz'], $lock); $this->assertEquals(<<unconfigure($recipe, ['locale' => 'en', 'foobar' => 'baz']); + $configurator->unconfigure($recipe, ['locale' => 'en', 'foobar' => 'baz'], $lock); $this->assertEquals(<<targetFiles as $targetFile) { $this->assertFileNotExists($targetFile); } - $this->createConfigurator()->configure($this->recipe, [$this->sourceFileRelativePath => $this->targetFileRelativePath]); + $lock = $this->getMockBuilder(Lock::class)->disableOriginalConstructor()->getMock(); + $this->createConfigurator()->configure($this->recipe, [ + $this->sourceFileRelativePath => $this->targetFileRelativePath, + ], $lock); foreach ($this->targetFiles as $targetFile) { $this->assertFileExists($targetFile); } diff --git a/tests/Configurator/CopyFromPackageConfiguratorTest.php b/tests/Configurator/CopyFromPackageConfiguratorTest.php index 6b27b9574..bd5004714 100644 --- a/tests/Configurator/CopyFromPackageConfiguratorTest.php +++ b/tests/Configurator/CopyFromPackageConfiguratorTest.php @@ -18,6 +18,7 @@ use LogicException; use PHPUnit\Framework\TestCase; use Symfony\Flex\Configurator\CopyFromPackageConfigurator; +use Symfony\Flex\Lock; use Symfony\Flex\Options; use Symfony\Flex\Recipe; @@ -39,7 +40,8 @@ public function testNoFilesCopied() } file_put_contents($this->targetFile, ''); $this->io->expects($this->exactly(1))->method('writeError')->with([' Setting configuration and copying files']); - $this->createConfigurator()->configure($this->recipe, [$this->sourceFileRelativePath => $this->targetFileRelativePath]); + $lock = $this->getMockBuilder(Lock::class)->disableOriginalConstructor()->getMock(); + $this->createConfigurator()->configure($this->recipe, [$this->sourceFileRelativePath => $this->targetFileRelativePath], $lock); } public function testConfigureAndOverwriteFiles() @@ -52,6 +54,7 @@ public function testConfigureAndOverwriteFiles() } file_put_contents($this->sourceFile, 'somecontent'); file_put_contents($this->targetFile, '-'); + $lock = $this->getMockBuilder(Lock::class)->disableOriginalConstructor()->getMock(); $this->io->expects($this->at(0))->method('writeError')->with([' Setting configuration and copying files']); $this->io->expects($this->at(2))->method('writeError')->with([' Created "./public/file"']); @@ -61,6 +64,7 @@ public function testConfigureAndOverwriteFiles() $this->createConfigurator()->configure( $this->recipe, [$this->sourceFileRelativePath => $this->targetFileRelativePath], + $lock, ['force' => true] ); $this->assertFileExists($this->targetFile); @@ -73,7 +77,8 @@ public function testSourceFileNotExist() $this->io->expects($this->at(1))->method('writeError')->with([' Created "./public/"']); $this->expectException(LogicException::class); $this->expectExceptionMessage(sprintf('File "%s" does not exist!', $this->sourceFile)); - $this->createConfigurator()->configure($this->recipe, [$this->sourceFileRelativePath => $this->targetFileRelativePath]); + $lock = $this->getMockBuilder(Lock::class)->disableOriginalConstructor()->getMock(); + $this->createConfigurator()->configure($this->recipe, [$this->sourceFileRelativePath => $this->targetFileRelativePath], $lock); } public function testConfigure() @@ -90,7 +95,8 @@ public function testConfigure() $this->io->expects($this->at(2))->method('writeError')->with([' Created "./public/file"']); $this->assertFileNotExists($this->targetFile); - $this->createConfigurator()->configure($this->recipe, [$this->sourceFileRelativePath => $this->targetFileRelativePath]); + $lock = $this->getMockBuilder(Lock::class)->disableOriginalConstructor()->getMock(); + $this->createConfigurator()->configure($this->recipe, [$this->sourceFileRelativePath => $this->targetFileRelativePath], $lock); $this->assertFileExists($this->targetFile); } @@ -104,7 +110,8 @@ public function testUnconfigure() } file_put_contents($this->targetFile, ''); $this->assertFileExists($this->targetFile); - $this->createConfigurator()->unconfigure($this->recipe, [$this->sourceFileRelativePath => $this->targetFileRelativePath]); + $lock = $this->getMockBuilder(Lock::class)->disableOriginalConstructor()->getMock(); + $this->createConfigurator()->unconfigure($this->recipe, [$this->sourceFileRelativePath => $this->targetFileRelativePath], $lock); $this->assertFileNotExists($this->targetFile); } @@ -112,7 +119,8 @@ public function testNoFilesRemoved() { $this->assertFileNotExists($this->targetFile); $this->io->expects($this->exactly(1))->method('writeError')->with([' Removing configuration and files']); - $this->createConfigurator()->unconfigure($this->recipe, [$this->sourceFileRelativePath => $this->targetFileRelativePath]); + $lock = $this->getMockBuilder(Lock::class)->disableOriginalConstructor()->getMock(); + $this->createConfigurator()->unconfigure($this->recipe, [$this->sourceFileRelativePath => $this->targetFileRelativePath], $lock); } protected function setUp() diff --git a/tests/Configurator/CopyFromRecipeConfiguratorTest.php b/tests/Configurator/CopyFromRecipeConfiguratorTest.php index 87251d29f..49c63372a 100644 --- a/tests/Configurator/CopyFromRecipeConfiguratorTest.php +++ b/tests/Configurator/CopyFromRecipeConfiguratorTest.php @@ -15,6 +15,7 @@ use Composer\IO\IOInterface; use PHPUnit\Framework\TestCase; use Symfony\Flex\Configurator\CopyFromRecipeConfigurator; +use Symfony\Flex\Lock; use Symfony\Flex\Options; use Symfony\Flex\Recipe; @@ -36,7 +37,24 @@ public function testNoFilesCopied() } file_put_contents($this->targetFile, ''); $this->io->expects($this->exactly(1))->method('writeError')->with([' Setting configuration and copying files']); - $this->createConfigurator()->configure($this->recipe, [$this->sourceFileRelativePath => $this->targetFileRelativePath]); + $lock = $this->getMockBuilder(Lock::class)->disableOriginalConstructor()->getMock(); + $this->createConfigurator()->configure($this->recipe, [$this->sourceFileRelativePath => $this->targetFileRelativePath], $lock); + } + + public function testConfigureLocksFiles() + { + $this->recipe->method('getName')->willReturn('test-recipe'); + $lock = new Lock($this->targetDirectory.'/symfony.lock'); + + $this->createConfigurator()->configure( + $this->recipe, + [$this->sourceFileRelativePath => $this->targetFileRelativePath], + $lock + ); + $lockedRecipe = $lock->get('test-recipe'); + + $this->assertArrayHasKey('files', $lockedRecipe); + $this->assertSame($this->targetFileRelativePath, $lockedRecipe['files'][0]); } public function testConfigureAndOverwriteFiles() @@ -45,6 +63,7 @@ public function testConfigureAndOverwriteFiles() mkdir($this->targetDirectory); } file_put_contents($this->targetFile, '-'); + $lock = $this->getMockBuilder(Lock::class)->disableOriginalConstructor()->getMock(); $this->io->expects($this->at(0))->method('writeError')->with([' Setting configuration and copying files']); $this->io->expects($this->at(2))->method('writeError')->with([' Created "./config/file"']); @@ -54,6 +73,7 @@ public function testConfigureAndOverwriteFiles() $this->createConfigurator()->configure( $this->recipe, [$this->sourceFileRelativePath => $this->targetFileRelativePath], + $lock, ['force' => true] ); $this->assertFileExists($this->targetFile); @@ -66,13 +86,31 @@ public function testConfigure() $this->io->expects($this->at(1))->method('writeError')->with([' Created "./config/file"']); $this->assertFileNotExists($this->targetFile); + $lock = $this->getMockBuilder(Lock::class)->disableOriginalConstructor()->getMock(); $this->createConfigurator()->configure( $this->recipe, - [$this->sourceFileRelativePath => $this->targetFileRelativePath] + [$this->sourceFileRelativePath => $this->targetFileRelativePath], + $lock ); $this->assertFileExists($this->targetFile); } + public function testUnconfigureKeepsLockedFiles() + { + if (!file_exists($this->sourceDirectory)) { + mkdir($this->sourceDirectory); + } + file_put_contents($this->sourceFile, '-'); + $this->assertFileExists($this->sourceFile); + + $lock = new Lock(FLEX_TEST_DIR.'/test.lock'); + $lock->set('other-recipe', ['files' => [$this->targetFileRelativePath]]); + + $this->createConfigurator()->unconfigure($this->recipe, [$this->targetFileRelativePath], $lock); + + $this->assertFileExists($this->sourceFile); + } + public function testUnconfigure() { $this->io->expects($this->at(0))->method('writeError')->with([' Removing configuration and files']); @@ -83,7 +121,8 @@ public function testUnconfigure() } file_put_contents($this->targetFile, ''); $this->assertFileExists($this->targetFile); - $this->createConfigurator()->unconfigure($this->recipe, [$this->targetFileRelativePath]); + $lock = $this->getMockBuilder(Lock::class)->disableOriginalConstructor()->getMock(); + $this->createConfigurator()->unconfigure($this->recipe, [$this->targetFileRelativePath], $lock); $this->assertFileNotExists($this->targetFile); } @@ -91,7 +130,8 @@ public function testNoFilesRemoved() { $this->assertFileNotExists($this->targetFile); $this->io->expects($this->exactly(1))->method('writeError')->with([' Removing configuration and files']); - $this->createConfigurator()->unconfigure($this->recipe, [$this->sourceFileRelativePath => $this->targetFileRelativePath]); + $lock = $this->getMockBuilder(Lock::class)->disableOriginalConstructor()->getMock(); + $this->createConfigurator()->unconfigure($this->recipe, [$this->sourceFileRelativePath => $this->targetFileRelativePath], $lock); } protected function setUp() @@ -100,7 +140,7 @@ protected function setUp() $this->sourceDirectory = FLEX_TEST_DIR.'/source'; $this->sourceFileRelativePath = 'source/file'; - $this->sourceFile = $this->targetDirectory.'/file'; + $this->sourceFile = $this->sourceDirectory.'/file'; $this->targetDirectory = FLEX_TEST_DIR.'/config'; $this->targetFileRelativePath = 'config/file'; diff --git a/tests/Configurator/EnvConfiguratorTest.php b/tests/Configurator/EnvConfiguratorTest.php index 6dd1a3a34..a4416c804 100644 --- a/tests/Configurator/EnvConfiguratorTest.php +++ b/tests/Configurator/EnvConfiguratorTest.php @@ -15,6 +15,7 @@ use Composer\IO\IOInterface; use PHPUnit\Framework\TestCase; use Symfony\Flex\Configurator\EnvConfigurator; +use Symfony\Flex\Lock; use Symfony\Flex\Options; use Symfony\Flex\Recipe; @@ -27,6 +28,7 @@ public function testConfigure() $this->getMockBuilder(IOInterface::class)->getMock(), new Options(['root-dir' => FLEX_TEST_DIR]) ); + $lock = $this->getMockBuilder(Lock::class)->disableOriginalConstructor()->getMock(); $recipe = $this->getMockBuilder(Recipe::class)->disableOriginalConstructor()->getMock(); $recipe->expects($this->any())->method('getName')->will($this->returnValue('FooBundle')); @@ -52,7 +54,7 @@ public function testConfigure() '#2' => 'Comment 3', '#TRUSTED_SECRET' => 's3cretf0rt3st"<>', 'APP_SECRET' => 's3cretf0rt3st"<>', - ]); + ], $lock); $envContents = << 'Comment 3', '#TRUSTED_SECRET' => 's3cretf0rt3st', 'APP_SECRET' => 's3cretf0rt3st', - ]); + ], $lock); $this->assertStringEqualsFile($env, $envContents); $this->assertStringEqualsFile($phpunitDist, $xmlContents); @@ -131,7 +133,7 @@ public function testConfigure() '#2' => 'Comment 3', '#TRUSTED_SECRET' => 's3cretf0rt3st', 'APP_SECRET' => 's3cretf0rt3st', - ]); + ], $lock); $this->assertStringEqualsFile( $env, @@ -155,6 +157,7 @@ public function testConfigureGeneratedSecret() $this->getMockBuilder(IOInterface::class)->getMock(), new Options(['root-dir' => FLEX_TEST_DIR]) ); + $lock = $this->getMockBuilder(Lock::class)->disableOriginalConstructor()->getMock(); $recipe = $this->getMockBuilder(Recipe::class)->disableOriginalConstructor()->getMock(); $recipe->expects($this->any())->method('getName')->will($this->returnValue('FooBundle')); @@ -175,7 +178,7 @@ public function testConfigureGeneratedSecret() '#TRUSTED_SECRET_2' => '%generate(secret, 32)%', '#TRUSTED_SECRET_3' => '%generate(secret, 32)%', 'APP_SECRET' => '%generate(secret)%', - ]); + ], $lock); $envContents = file_get_contents($env); $this->assertRegExp('/#TRUSTED_SECRET_1=[a-z0-9]{64}/', $envContents); @@ -298,9 +301,11 @@ public function testConfigureForce() EOT; + $lock = $this->getMockBuilder(Lock::class)->disableOriginalConstructor()->getMock(); + $configurator->configure($recipe, [ 'FOO' => 'bar', - ]); + ], $lock); file_put_contents($env, "\n# new content\n", \FILE_APPEND); file_put_contents($phpunit, str_replace( @@ -321,7 +326,7 @@ public function testConfigureForce() $configurator->configure($recipe, [ 'FOO' => 'bar', 'OOF' => 'rab', - ], [ + ], $lock, [ 'force' => true, ]); diff --git a/tests/Configurator/GitignoreConfiguratorTest.php b/tests/Configurator/GitignoreConfiguratorTest.php index d1d4db0f5..edae7a300 100644 --- a/tests/Configurator/GitignoreConfiguratorTest.php +++ b/tests/Configurator/GitignoreConfiguratorTest.php @@ -15,6 +15,7 @@ use Composer\IO\IOInterface; use PHPUnit\Framework\TestCase; use Symfony\Flex\Configurator\GitignoreConfigurator; +use Symfony\Flex\Lock; use Symfony\Flex\Options; use Symfony\Flex\Recipe; @@ -27,6 +28,7 @@ public function testConfigure() $this->getMockBuilder(IOInterface::class)->getMock(), new Options(['public-dir' => 'public', 'root-dir' => FLEX_TEST_DIR]) ); + $lock = $this->getMockBuilder(Lock::class)->disableOriginalConstructor()->getMock(); $recipe1 = $this->getMockBuilder(Recipe::class)->disableOriginalConstructor()->getMock(); $recipe1->expects($this->any())->method('getName')->will($this->returnValue('FooBundle')); @@ -60,20 +62,20 @@ public function testConfigure() ###< BarBundle ### EOF; - $configurator->configure($recipe1, $vars1); + $configurator->configure($recipe1, $vars1, $lock); $this->assertStringEqualsFile($gitignore, "\n".$gitignoreContents1."\n"); - $configurator->configure($recipe2, $vars2); + $configurator->configure($recipe2, $vars2, $lock); $this->assertStringEqualsFile($gitignore, "\n".$gitignoreContents1."\n\n".$gitignoreContents2."\n"); - $configurator->configure($recipe1, $vars1); - $configurator->configure($recipe2, $vars2); + $configurator->configure($recipe1, $vars1, $lock); + $configurator->configure($recipe2, $vars2, $lock); $this->assertStringEqualsFile($gitignore, "\n".$gitignoreContents1."\n\n".$gitignoreContents2."\n"); - $configurator->unconfigure($recipe1, $vars1); + $configurator->unconfigure($recipe1, $vars1, $lock); $this->assertStringEqualsFile($gitignore, $gitignoreContents2."\n"); - $configurator->unconfigure($recipe2, $vars2); + $configurator->unconfigure($recipe2, $vars2, $lock); $this->assertStringEqualsFile($gitignore, ''); @unlink($gitignore); @@ -115,16 +117,17 @@ public function testConfigureForce() # new content EOF; + $lock = $this->getMockBuilder(Lock::class)->disableOriginalConstructor()->getMock(); $configurator->configure($recipe, [ '.env', - ]); + ], $lock); file_put_contents($gitignore, "\n# new content", \FILE_APPEND); $this->assertStringEqualsFile($gitignore, $contentsConfigure); $configurator->configure($recipe, [ '.env', '.env.test', - ], [ + ], $lock, [ 'force' => true, ]); $this->assertStringEqualsFile($gitignore, $contentsForce); diff --git a/tests/Configurator/MakefileConfiguratorTest.php b/tests/Configurator/MakefileConfiguratorTest.php index 74eecabcb..c6834ad20 100644 --- a/tests/Configurator/MakefileConfiguratorTest.php +++ b/tests/Configurator/MakefileConfiguratorTest.php @@ -15,6 +15,7 @@ use Composer\IO\IOInterface; use PHPUnit\Framework\TestCase; use Symfony\Flex\Configurator\MakefileConfigurator; +use Symfony\Flex\Lock; use Symfony\Flex\Options; use Symfony\Flex\Recipe; @@ -27,6 +28,7 @@ public function testConfigure() $this->getMockBuilder(IOInterface::class)->getMock(), new Options(['root-dir' => FLEX_TEST_DIR]) ); + $lock = $this->getMockBuilder(Lock::class)->disableOriginalConstructor()->getMock(); $recipe1 = $this->getMockBuilder(Recipe::class)->disableOriginalConstructor()->getMock(); $recipe1->expects($this->any())->method('getName')->will($this->returnValue('FooBundle')); @@ -64,20 +66,20 @@ public function testConfigure() $makefileContents1 = "###> FooBundle ###\n".implode("\n", $makefile1)."\n###< FooBundle ###"; $makefileContents2 = "###> BarBundle ###\n".implode("\n", $makefile2)."\n###< BarBundle ###"; - $configurator->configure($recipe1, $makefile1); + $configurator->configure($recipe1, $makefile1, $lock); $this->assertStringEqualsFile($makefile, "\n".$makefileContents1."\n"); - $configurator->configure($recipe2, $makefile2); + $configurator->configure($recipe2, $makefile2, $lock); $this->assertStringEqualsFile($makefile, "\n".$makefileContents1."\n\n".$makefileContents2."\n"); - $configurator->configure($recipe1, $makefile1); - $configurator->configure($recipe2, $makefile2); + $configurator->configure($recipe1, $makefile1, $lock); + $configurator->configure($recipe2, $makefile2, $lock); $this->assertStringEqualsFile($makefile, "\n".$makefileContents1."\n\n".$makefileContents2."\n"); - $configurator->unconfigure($recipe1, $makefile1); + $configurator->unconfigure($recipe1, $makefile1, $lock); $this->assertStringEqualsFile($makefile, $makefileContents2."\n"); - $configurator->unconfigure($recipe2, $makefile2); + $configurator->unconfigure($recipe2, $makefile2, $lock); $this->assertFalse(is_file($makefile)); } @@ -112,11 +114,13 @@ public function testConfigureForce() ["###< FooBundle ###\n\n# new content"] )); - $configurator->configure($recipe, $bundleLinesConfigure); + $lock = $this->getMockBuilder(Lock::class)->disableOriginalConstructor()->getMock(); + + $configurator->configure($recipe, $bundleLinesConfigure, $lock); file_put_contents($makefile, "\n# new content", \FILE_APPEND); $this->assertStringEqualsFile($makefile, $contentConfigure); - $configurator->configure($recipe, $bundleLinesForce, [ + $configurator->configure($recipe, $bundleLinesForce, $lock, [ 'force' => true, ]); $this->assertStringEqualsFile($makefile, $contentForce);