From 939de9569e6eebe4d8fc74f10e9eb76e311382c4 Mon Sep 17 00:00:00 2001 From: Alex Rock Ancelet Date: Tue, 20 Nov 2018 11:42:44 +0100 Subject: [PATCH] Implement "sync-recipes --force" option --- .gitignore | 1 + composer.json | 3 +- phpunit.xml.dist | 2 +- src/Command/SyncRecipesCommand.php | 16 +++--- src/Configurator.php | 4 +- src/Configurator/AbstractConfigurator.php | 2 +- src/Configurator/BundlesConfigurator.php | 2 +- .../ComposerScriptsConfigurator.php | 2 +- src/Configurator/ContainerConfigurator.php | 2 +- .../CopyFromPackageConfigurator.php | 20 ++++--- .../CopyFromRecipeConfigurator.php | 18 +++---- src/Configurator/EnvConfigurator.php | 2 +- src/Configurator/GitignoreConfigurator.php | 2 +- src/Configurator/MakefileConfigurator.php | 2 +- src/Event/UpdateEvent.php | 29 ++++++++++ src/Flex.php | 42 ++++++--------- src/Options.php | 54 ++++++++++++++++++- .../Configurator/BundlesConfiguratorTest.php | 6 +-- .../ContainerConfiguratorTest.php | 18 +++---- ...pyDirectoryFromPackageConfiguratorTest.php | 12 ++--- .../CopyFromPackageConfiguratorTest.php | 39 +++++++++++--- .../CopyFromRecipeConfiguratorTest.php | 29 ++++++++-- tests/Configurator/EnvConfiguratorTest.php | 10 ++-- .../GitignoreConfiguratorTest.php | 4 +- .../Configurator/MakefileConfiguratorTest.php | 4 +- tests/Configurator/TmpDirMock.php | 8 --- tests/FlexTest.php | 46 ---------------- tests/bootstrap.php | 30 +++++++++++ 28 files changed, 242 insertions(+), 167 deletions(-) create mode 100644 src/Event/UpdateEvent.php delete mode 100644 tests/Configurator/TmpDirMock.php create mode 100644 tests/bootstrap.php diff --git a/.gitignore b/.gitignore index c9dec9a2d..394cd7088 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /vendor/ +/build/ .php_cs.cache composer.lock diff --git a/composer.json b/composer.json index a1d4101da..a02deb241 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,8 @@ }, "require-dev": { "composer/composer": "^1.0.2", - "symfony/phpunit-bridge": "^3.4.19|^4.1.8" + "symfony/phpunit-bridge": "^3.4.19|^4.1.8", + "symfony/process": "^2.7|^3.0|^4.0" }, "autoload": { "psr-4": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index ec44bd574..be4197f1e 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -9,7 +9,7 @@ processIsolation="false" stopOnFailure="false" syntaxCheck="false" - bootstrap="vendor/autoload.php" + bootstrap="tests/bootstrap.php" > diff --git a/src/Command/SyncRecipesCommand.php b/src/Command/SyncRecipesCommand.php index 4f87f0114..a2712e7e1 100644 --- a/src/Command/SyncRecipesCommand.php +++ b/src/Command/SyncRecipesCommand.php @@ -14,9 +14,10 @@ use Composer\Command\BaseCommand; use Composer\DependencyResolver\Operation\InstallOperation; use Composer\Factory; -use Composer\Script\Event; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Flex\Event\UpdateEvent; use Symfony\Flex\Lock; class SyncRecipesCommand extends BaseCommand @@ -35,11 +36,14 @@ protected function configure() $this->setName('symfony:sync-recipes') ->setAliases(['sync-recipes', 'fix-recipes']) ->setDescription('Installs or reinstalls recipes for already installed packages.') + ->addOption('force', null, InputOption::VALUE_NONE, 'Ignore the "symfony.lock" file and overwrite existing files') ; } protected function execute(InputInterface $input, OutputInterface $output) { + $force = $input->getOption('force'); + $symfonyLock = new Lock(getenv('SYMFONY_LOCKFILE') ?: str_replace('composer.json', 'symfony.lock', Factory::getComposerFile())); $composer = $this->getComposer(); $locker = $composer->getLocker(); @@ -47,12 +51,12 @@ protected function execute(InputInterface $input, OutputInterface $output) $packages = []; foreach ($lockData['packages'] as $pkg) { - if (!$symfonyLock->has($pkg['name'])) { + if ($force || !$symfonyLock->has($pkg['name'])) { $packages[] = $pkg['name']; } } foreach ($lockData['packages-dev'] as $pkg) { - if (!$symfonyLock->has($pkg['name'])) { + if ($force || !$symfonyLock->has($pkg['name'])) { $packages[] = $pkg['name']; } } @@ -76,10 +80,6 @@ protected function execute(InputInterface $input, OutputInterface $output) $operations[] = new InstallOperation($pkg); } - $this->flex->update(new class() extends Event { - public function __construct() - { - } - }, $operations); + $this->flex->update(new UpdateEvent($force), $operations); } } diff --git a/src/Configurator.php b/src/Configurator.php index d163c1d13..e9f43357d 100644 --- a/src/Configurator.php +++ b/src/Configurator.php @@ -44,12 +44,12 @@ public function __construct(Composer $composer, IOInterface $io, Options $option ]; } - public function install(Recipe $recipe) + public function install(Recipe $recipe, array $options = []) { $manifest = $recipe->getManifest(); foreach (array_keys($this->configurators) as $key) { if (isset($manifest[$key])) { - $this->get($key)->configure($recipe, $manifest[$key]); + $this->get($key)->configure($recipe, $manifest[$key], $options); } } } diff --git a/src/Configurator/AbstractConfigurator.php b/src/Configurator/AbstractConfigurator.php index 90fa1e0b0..c61b4aeea 100644 --- a/src/Configurator/AbstractConfigurator.php +++ b/src/Configurator/AbstractConfigurator.php @@ -35,7 +35,7 @@ public function __construct(Composer $composer, IOInterface $io, Options $option $this->path = new Path(getcwd()); } - abstract public function configure(Recipe $recipe, $config); + abstract public function configure(Recipe $recipe, $config, array $options = []); abstract public function unconfigure(Recipe $recipe, $config); diff --git a/src/Configurator/BundlesConfigurator.php b/src/Configurator/BundlesConfigurator.php index cb14287d1..4c94371d4 100644 --- a/src/Configurator/BundlesConfigurator.php +++ b/src/Configurator/BundlesConfigurator.php @@ -18,7 +18,7 @@ */ class BundlesConfigurator extends AbstractConfigurator { - public function configure(Recipe $recipe, $bundles) + public function configure(Recipe $recipe, $bundles, array $options = []) { $this->write('Enabling the package as a Symfony bundle'); $file = $this->getConfFile(); diff --git a/src/Configurator/ComposerScriptsConfigurator.php b/src/Configurator/ComposerScriptsConfigurator.php index 16c81e65c..3d26b1f2a 100644 --- a/src/Configurator/ComposerScriptsConfigurator.php +++ b/src/Configurator/ComposerScriptsConfigurator.php @@ -21,7 +21,7 @@ */ class ComposerScriptsConfigurator extends AbstractConfigurator { - public function configure(Recipe $recipe, $scripts) + public function configure(Recipe $recipe, $scripts, array $options = []) { $json = new JsonFile(Factory::getComposerFile()); diff --git a/src/Configurator/ContainerConfigurator.php b/src/Configurator/ContainerConfigurator.php index f63204afa..8e80b5822 100644 --- a/src/Configurator/ContainerConfigurator.php +++ b/src/Configurator/ContainerConfigurator.php @@ -18,7 +18,7 @@ */ class ContainerConfigurator extends AbstractConfigurator { - public function configure(Recipe $recipe, $parameters) + public function configure(Recipe $recipe, $parameters, array $options = []) { $this->write('Setting parameters'); $this->addParameters($parameters); diff --git a/src/Configurator/CopyFromPackageConfigurator.php b/src/Configurator/CopyFromPackageConfigurator.php index e4a8d347e..debee4b77 100644 --- a/src/Configurator/CopyFromPackageConfigurator.php +++ b/src/Configurator/CopyFromPackageConfigurator.php @@ -19,11 +19,11 @@ */ class CopyFromPackageConfigurator extends AbstractConfigurator { - public function configure(Recipe $recipe, $config) + public function configure(Recipe $recipe, $config, array $options = []) { $this->write('Setting configuration and copying files'); $packageDir = $this->composer->getInstallationManager()->getInstallPath($recipe->getPackage()); - $this->copyFiles($config, $packageDir, getcwd()); + $this->copyFiles($config, $packageDir, getcwd(), $options['force'] ?? false); } public function unconfigure(Recipe $recipe, $config) @@ -33,12 +33,12 @@ public function unconfigure(Recipe $recipe, $config) $this->removeFiles($config, $packageDir, getcwd()); } - private function copyFiles(array $manifest, string $from, string $to) + private function copyFiles(array $manifest, string $from, string $to, bool $overwrite = false) { 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])); + $this->copyDir($this->path->concatenate([$from, $source]), $this->path->concatenate([$to, $target]), $overwrite); } else { $targetPath = $this->path->concatenate([$to, $target]); if (!is_dir(\dirname($targetPath))) { @@ -46,9 +46,7 @@ private function copyFiles(array $manifest, string $from, string $to) $this->write(sprintf('Created "%s"', $this->path->relativize(\dirname($targetPath)))); } - if (!file_exists($targetPath)) { - $this->copyFile($this->path->concatenate([$from, $source]), $targetPath); - } + $this->copyFile($this->path->concatenate([$from, $source]), $targetPath, $overwrite); } } } @@ -69,7 +67,7 @@ private function removeFiles(array $manifest, string $from, string $to) } } - private function copyDir(string $source, string $target) + private function copyDir(string $source, string $target, bool $overwrite) { if (!is_dir($target)) { mkdir($target, 0777, true); @@ -84,14 +82,14 @@ private function copyDir(string $source, string $target) $this->write(sprintf('Created "%s"', $this->path->relativize($targetPath))); } } elseif (!file_exists($targetPath)) { - $this->copyFile($item, $targetPath); + $this->copyFile($item, $targetPath, $overwrite); } } } - public function copyFile(string $source, string $target) + public function copyFile(string $source, string $target, bool $overwrite = false) { - if (file_exists($target)) { + if (!$this->options->shouldWriteFile($target, $overwrite)) { return; } diff --git a/src/Configurator/CopyFromRecipeConfigurator.php b/src/Configurator/CopyFromRecipeConfigurator.php index 1cd8e7434..b8d103ce0 100644 --- a/src/Configurator/CopyFromRecipeConfigurator.php +++ b/src/Configurator/CopyFromRecipeConfigurator.php @@ -18,10 +18,10 @@ */ class CopyFromRecipeConfigurator extends AbstractConfigurator { - public function configure(Recipe $recipe, $config) + public function configure(Recipe $recipe, $config, array $options = []) { $this->write('Setting configuration and copying files'); - $this->copyFiles($config, $recipe->getFiles(), getcwd()); + $this->copyFiles($config, $recipe->getFiles(), getcwd(), $options['force'] ?? false); } public function unconfigure(Recipe $recipe, $config) @@ -30,31 +30,31 @@ public function unconfigure(Recipe $recipe, $config) $this->removeFiles($config, $recipe->getFiles(), getcwd()); } - private function copyFiles(array $manifest, array $files, string $to) + private function copyFiles(array $manifest, array $files, string $to, bool $overwrite = false) { foreach ($manifest as $source => $target) { $target = $this->options->expandTargetDir($target); if ('/' === substr($source, -1)) { - $this->copyDir($source, $this->path->concatenate([$to, $target]), $files); + $this->copyDir($source, $this->path->concatenate([$to, $target]), $files, $overwrite); } else { - $this->copyFile($this->path->concatenate([$to, $target]), $files[$source]['contents'], $files[$source]['executable']); + $this->copyFile($this->path->concatenate([$to, $target]), $files[$source]['contents'], $files[$source]['executable'], $overwrite); } } } - private function copyDir(string $source, string $target, array $files) + private function copyDir(string $source, string $target, array $files, bool $overwrite = false) { 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']); + $this->copyFile($file, $data['contents'], $data['executable'], $overwrite); } } } - private function copyFile(string $to, string $contents, bool $executable) + private function copyFile(string $to, string $contents, bool $executable, bool $overwrite = false) { - if (file_exists($to)) { + if (!$this->options->shouldWriteFile($to, $overwrite)) { return; } diff --git a/src/Configurator/EnvConfigurator.php b/src/Configurator/EnvConfigurator.php index 9ceacade1..e6a334562 100644 --- a/src/Configurator/EnvConfigurator.php +++ b/src/Configurator/EnvConfigurator.php @@ -18,7 +18,7 @@ */ class EnvConfigurator extends AbstractConfigurator { - public function configure(Recipe $recipe, $vars) + public function configure(Recipe $recipe, $vars, array $options = []) { $this->write('Added environment variable defaults'); diff --git a/src/Configurator/GitignoreConfigurator.php b/src/Configurator/GitignoreConfigurator.php index 654975107..9787bcfa1 100644 --- a/src/Configurator/GitignoreConfigurator.php +++ b/src/Configurator/GitignoreConfigurator.php @@ -18,7 +18,7 @@ */ class GitignoreConfigurator extends AbstractConfigurator { - public function configure(Recipe $recipe, $vars) + public function configure(Recipe $recipe, $vars, array $options = []) { $this->write('Added entries to .gitignore'); diff --git a/src/Configurator/MakefileConfigurator.php b/src/Configurator/MakefileConfigurator.php index ff467cf78..9963cfe3e 100644 --- a/src/Configurator/MakefileConfigurator.php +++ b/src/Configurator/MakefileConfigurator.php @@ -18,7 +18,7 @@ */ class MakefileConfigurator extends AbstractConfigurator { - public function configure(Recipe $recipe, $definitions) + public function configure(Recipe $recipe, $definitions, array $options = []) { $this->write('Added Makefile entries'); diff --git a/src/Event/UpdateEvent.php b/src/Event/UpdateEvent.php new file mode 100644 index 000000000..1702af702 --- /dev/null +++ b/src/Event/UpdateEvent.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Flex\Event; + +use Composer\Script\Event; + +class UpdateEvent extends Event +{ + private $force; + + public function __construct(bool $force) + { + $this->force = $force; + } + + public function force(): bool + { + return $this->force; + } +} diff --git a/src/Flex.php b/src/Flex.php index 12b68056d..b4053f97d 100644 --- a/src/Flex.php +++ b/src/Flex.php @@ -43,6 +43,7 @@ use Composer\Script\Event; use Composer\Script\ScriptEvents; use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Flex\Event\UpdateEvent; use Symfony\Thanks\Thanks; /** @@ -276,27 +277,8 @@ public function configureProject(Event $event) public function record(PackageEvent $event) { - if (!$this->shouldRecordOperation($event)) { - return; - } - - $operation = $event->getOperation(); - if ($operation instanceof InstallOperation && \in_array($packageName = $operation->getPackage()->getName(), ['symfony/framework-bundle', 'symfony/flex'])) { - if ('symfony/flex' === $packageName) { - array_unshift($this->operations, $operation); - } else { - if ($this->operations && $this->operations[0] instanceof InstallOperation && 'symfony/flex' === $this->operations[0]->getPackage()->getName()) { - // framework-bundle should be *after* flex - $flexOperation = $this->operations[0]; - unset($this->operations[0]); - array_unshift($this->operations, $operation); - array_unshift($this->operations, $flexOperation); - } else { - array_unshift($this->operations, $operation); - } - } - } else { - $this->operations[] = $operation; + if ($this->shouldRecordOperation($event)) { + $this->operations[] = $event->getOperation(); } } @@ -393,7 +375,9 @@ 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, [ + 'force' => $event instanceof UpdateEvent && $event->force(), + ]); $manifest = $recipe->getManifest(); if (isset($manifest['post-install-output'])) { foreach ($manifest['post-install-output'] as $line) { @@ -595,7 +579,11 @@ private function fetchRecipes(): array $data = $this->downloader->getRecipes($this->operations); $manifests = $data['manifests'] ?? []; $locks = $data['locks'] ?? []; - $recipes = []; + // symfony/flex and symfony/framework-bundle recipes should always be applied first + $recipes = [ + 'symfony/flex' => null, + 'symfony/framework-bundle' => null, + ]; foreach ($this->operations as $i => $operation) { if ($operation instanceof UpdateOperation) { $package = $operation->getTargetPackage(); @@ -614,7 +602,7 @@ private function fetchRecipes(): array } if (isset($manifests[$name])) { - $recipes[] = new Recipe($package, $name, $job, $manifests[$name]); + $recipes[$name] = new Recipe($package, $name, $job, $manifests[$name]); } $noRecipe = !isset($manifests[$name]) || (isset($manifests[$name]['not_installable']) && $manifests[$name]['not_installable']); @@ -630,13 +618,13 @@ private function fetchRecipes(): array } if ($manifest) { $manifest['origin'] = sprintf('%s:%s@auto-generated recipe', $name, $package->getPrettyVersion()); - $recipes[] = new Recipe($package, $name, $job, $manifest); + $recipes[$name] = new Recipe($package, $name, $job, $manifest); } } } $this->operations = []; - return [$recipes, $data['vulnerabilities'] ?? []]; + return [array_filter($recipes), $data['vulnerabilities'] ?? []]; } private function initOptions(): Options @@ -650,7 +638,7 @@ private function initOptions(): Options 'public-dir' => 'public', ], $this->composer->getPackage()->getExtra()); - return new Options($options); + return new Options($options, $this->io); } private function getFlexId() diff --git a/src/Options.php b/src/Options.php index e570e54ed..ad2fcb533 100644 --- a/src/Options.php +++ b/src/Options.php @@ -11,16 +11,22 @@ namespace Symfony\Flex; +use Composer\IO\IOInterface; +use Composer\Util\ProcessExecutor; + /** * @author Fabien Potencier */ class Options { private $options; + private $writtenFiles = []; + private $io; - public function __construct(array $options = []) + public function __construct(array $options = [], IOInterface $io = null) { $this->options = $options; + $this->io = $io; } public function get(string $name) @@ -39,4 +45,50 @@ public function expandTargetDir(string $target): string return rtrim($this->options[$option], '/'); }, $target); } + + public function shouldWriteFile(string $file, bool $overwrite): bool + { + if (isset($this->writtenFiles[$file])) { + return false; + } + $this->writtenFiles[$file] = true; + + if (!file_exists($file)) { + return true; + } + + if (!$overwrite) { + return false; + } + + if ($this->gitExists()) { + exec('git status --short --ignored -- '.ProcessExecutor::escape($file).' 2>&1', $output, $status); + } else { + $status = 1; + } + + if (0 !== $status) { + return (bool) $this->io && $this->io->askConfirmation(\sprintf('Cannot determine the state of the "%s" file, overwrite anyway? [y/N] ', $file), false); + } + + if (empty($output[0]) || preg_match('/^[ AMDRCU][ D][ \t]/', $output[0])) { + return true; + } + + $name = basename($file); + $name = \strlen($output[0]) - \strlen($name) === strrpos($output[0], $name) ? substr($output[0], 3) : $name; + + return (bool) $this->io && $this->io->askConfirmation(\sprintf('File "%s" has uncommitted changes, overwrite? [y/N] ', $name), false); + } + + private function gitExists(): bool + { + static $gitExists; + + if (\is_bool($gitExists)) { + return $gitExists; + } + + return $gitExists = @is_executable(strtok(exec('\\' === \DIRECTORY_SEPARATOR ? 'where git' : 'command -v git'), PHP_EOL)); + } } diff --git a/tests/Configurator/BundlesConfiguratorTest.php b/tests/Configurator/BundlesConfiguratorTest.php index 999feb9dd..5a921565a 100644 --- a/tests/Configurator/BundlesConfiguratorTest.php +++ b/tests/Configurator/BundlesConfiguratorTest.php @@ -11,8 +11,6 @@ namespace Symfony\Flex\Tests\Configurator; -require_once __DIR__.'/TmpDirMock.php'; - use PHPUnit\Framework\TestCase; use Symfony\Flex\Configurator\BundlesConfigurator; use Symfony\Flex\Options; @@ -22,12 +20,12 @@ class BundlesConfiguratorTest extends TestCase { public function testConfigure() { - $config = sys_get_temp_dir().'/config/bundles.php'; + $config = getcwd().'/config/bundles.php'; $configurator = new BundlesConfigurator( $this->getMockBuilder('Composer\Composer')->getMock(), $this->getMockBuilder('Composer\IO\IOInterface')->getMock(), - new Options(['config-dir' => dirname($config)]) + new Options(['config-dir' => \dirname($config)]) ); $recipe = $this->getMockBuilder(Recipe::class)->disableOriginalConstructor()->getMock(); diff --git a/tests/Configurator/ContainerConfiguratorTest.php b/tests/Configurator/ContainerConfiguratorTest.php index 309a2f75f..2f9c316d4 100644 --- a/tests/Configurator/ContainerConfiguratorTest.php +++ b/tests/Configurator/ContainerConfiguratorTest.php @@ -11,8 +11,6 @@ namespace Symfony\Flex\Tests\Configurator; -require_once __DIR__.'/TmpDirMock.php'; - use Composer\Composer; use Composer\IO\IOInterface; use PHPUnit\Framework\TestCase; @@ -25,7 +23,7 @@ class ContainerConfiguratorTest extends TestCase public function testConfigure() { $recipe = $this->getMockBuilder(Recipe::class)->disableOriginalConstructor()->getMock(); - $config = sys_get_temp_dir().'/config/services.yaml'; + $config = getcwd().'/config/services.yaml'; file_put_contents( $config, <<getMockBuilder(Composer::class)->getMock(), $this->getMockBuilder(IOInterface::class)->getMock(), - new Options(['config-dir' => dirname($config)]) + new Options(['config-dir' => \dirname($config)]) ); $configurator->configure($recipe, ['locale' => 'en']); $this->assertEquals(<<getMockBuilder(Recipe::class)->disableOriginalConstructor()->getMock(); - $config = sys_get_temp_dir().'/config/services.yaml'; + $config = getcwd().'/config/services.yaml'; file_put_contents( $config, <<getMockBuilder(Composer::class)->getMock(), $this->getMockBuilder(IOInterface::class)->getMock(), - new Options(['config-dir' => dirname($config)]) + new Options(['config-dir' => \dirname($config)]) ); $configurator->configure($recipe, ['locale' => 'en']); $this->assertEquals(<<getMockBuilder(Recipe::class)->disableOriginalConstructor()->getMock(); - $config = sys_get_temp_dir().'/config/services.yaml'; + $config = getcwd().'/config/services.yaml'; file_put_contents( $config, <<getMockBuilder(Composer::class)->getMock(), $this->getMockBuilder(IOInterface::class)->getMock(), - new Options(['config-dir' => dirname($config)]) + new Options(['config-dir' => \dirname($config)]) ); $configurator->configure($recipe, ['locale' => 'en']); $this->assertEquals(<<getMockBuilder(Recipe::class)->disableOriginalConstructor()->getMock(); - $config = sys_get_temp_dir().'/config/services.yaml'; + $config = getcwd().'/config/services.yaml'; file_put_contents( $config, <<getMockBuilder(Composer::class)->getMock(), $this->getMockBuilder(IOInterface::class)->getMock(), - new Options(['config-dir' => dirname($config)]) + new Options(['config-dir' => \dirname($config)]) ); $configurator->configure($recipe, ['locale' => 'en', 'foobar' => 'baz']); $this->assertEquals(<<sourceDirectory = sys_get_temp_dir().'/package/files'; + $this->sourceDirectory = getcwd().'/package/files'; $this->sourceFileRelativePath = 'package/files/'; $this->sourceFiles = [ $this->sourceDirectory.'/file1', $this->sourceDirectory.'/file2', ]; - $this->targetDirectory = sys_get_temp_dir().'/public/files'; + $this->targetDirectory = getcwd().'/public/files'; $this->targetFileRelativePath = 'public/files/'; $this->targetFiles = [ $this->targetDirectory.'/file1', @@ -81,7 +79,7 @@ protected function setUp() $installationManager->expects($this->exactly(1)) ->method('getInstallPath') ->with($package) - ->willReturn(sys_get_temp_dir()) + ->willReturn(getcwd()) ; $this->composer = $this->getMockBuilder(Composer::class)->getMock(); $this->composer->expects($this->exactly(1)) @@ -109,8 +107,8 @@ private function createConfigurator(): CopyFromPackageConfigurator private function cleanUpTargetFiles() { - $this->rrmdir(sys_get_temp_dir().'/package'); - $this->rrmdir(sys_get_temp_dir().'/public'); + $this->rrmdir(getcwd().'/package'); + $this->rrmdir(getcwd().'/public'); } /** diff --git a/tests/Configurator/CopyFromPackageConfiguratorTest.php b/tests/Configurator/CopyFromPackageConfiguratorTest.php index 05c8cded9..f9deb59e4 100644 --- a/tests/Configurator/CopyFromPackageConfiguratorTest.php +++ b/tests/Configurator/CopyFromPackageConfiguratorTest.php @@ -11,8 +11,6 @@ namespace Symfony\Flex\Tests\Configurator; -require_once __DIR__.'/TmpDirMock.php'; - use Composer\Composer; use Composer\Installer\InstallationManager; use Composer\IO\IOInterface; @@ -44,6 +42,31 @@ public function testNoFilesCopied() $this->createConfigurator()->configure($this->recipe, [$this->sourceFileRelativePath => $this->targetFileRelativePath]); } + public function testConfigureAndOverwriteFiles() + { + if (!file_exists($this->targetDirectory)) { + mkdir($this->targetDirectory); + } + if (!file_exists($this->sourceDirectory)) { + mkdir($this->sourceDirectory); + } + file_put_contents($this->sourceFile, 'somecontent'); + file_put_contents($this->targetFile, ''); + + $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"']); + $this->io->method('askConfirmation')->with('File "build/public/file" has uncommitted changes, overwrite? [y/N] ')->willReturn(true); + + $this->assertFileExists($this->targetFile); + $this->createConfigurator()->configure( + $this->recipe, + [$this->sourceFileRelativePath => $this->targetFileRelativePath], + ['force' => true] + ); + $this->assertFileExists($this->targetFile); + $this->assertFileEquals($this->sourceFile, $this->targetFile); + } + public function testSourceFileNotExist() { $this->io->expects($this->at(0))->method('writeError')->with([' Setting configuration and copying files']); @@ -96,11 +119,11 @@ protected function setUp() { parent::setUp(); - $this->sourceDirectory = sys_get_temp_dir().'/package'; + $this->sourceDirectory = getcwd().'/package'; $this->sourceFileRelativePath = 'package/file'; $this->sourceFile = $this->sourceDirectory.'/file'; - $this->targetDirectory = sys_get_temp_dir().'/public'; + $this->targetDirectory = getcwd().'/public'; $this->targetFileRelativePath = 'public/file'; $this->targetFile = $this->targetDirectory.'/file'; @@ -114,7 +137,7 @@ protected function setUp() $installationManager->expects($this->exactly(1)) ->method('getInstallPath') ->with($package) - ->willReturn(sys_get_temp_dir()) + ->willReturn(getcwd()) ; $this->composer = $this->getMockBuilder(Composer::class)->getMock(); $this->composer->expects($this->exactly(1)) @@ -135,13 +158,13 @@ protected function tearDown() private function createConfigurator(): CopyFromPackageConfigurator { - return new CopyFromPackageConfigurator($this->composer, $this->io, new Options()); + return new CopyFromPackageConfigurator($this->composer, $this->io, new Options([], $this->io)); } private function cleanUpTargetFiles() { @unlink($this->targetFile); - @rmdir(sys_get_temp_dir().'/package'); - @rmdir(sys_get_temp_dir().'/public'); + @rmdir(getcwd().'/package'); + @rmdir(getcwd().'/public'); } } diff --git a/tests/Configurator/CopyFromRecipeConfiguratorTest.php b/tests/Configurator/CopyFromRecipeConfiguratorTest.php index 82296b065..1944a484d 100644 --- a/tests/Configurator/CopyFromRecipeConfiguratorTest.php +++ b/tests/Configurator/CopyFromRecipeConfiguratorTest.php @@ -11,8 +11,6 @@ namespace Symfony\Flex\Tests\Configurator; -require_once __DIR__.'/TmpDirMock.php'; - use Composer\Composer; use Composer\IO\IOInterface; use PHPUnit\Framework\TestCase; @@ -41,6 +39,27 @@ public function testNoFilesCopied() $this->createConfigurator()->configure($this->recipe, [$this->sourceFileRelativePath => $this->targetFileRelativePath]); } + public function testConfigureAndOverwriteFiles() + { + if (!file_exists($this->targetDirectory)) { + mkdir($this->targetDirectory); + } + file_put_contents($this->targetFile, ''); + + $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"']); + $this->io->method('askConfirmation')->with('File "build/config/file" has uncommitted changes, overwrite? [y/N] ')->willReturn(true); + + $this->assertFileExists($this->targetFile); + $this->createConfigurator()->configure( + $this->recipe, + [$this->sourceFileRelativePath => $this->targetFileRelativePath], + ['force' => true] + ); + $this->assertFileExists($this->targetFile); + $this->assertSame('somecontent', file_get_contents($this->targetFile)); + } + public function testConfigure() { $this->io->expects($this->at(0))->method('writeError')->with([' Setting configuration and copying files']); @@ -79,11 +98,11 @@ protected function setUp() { parent::setUp(); - $this->sourceDirectory = sys_get_temp_dir().'/source'; + $this->sourceDirectory = getcwd().'/source'; $this->sourceFileRelativePath = 'source/file'; $this->sourceFile = $this->targetDirectory.'/file'; - $this->targetDirectory = sys_get_temp_dir().'/config'; + $this->targetDirectory = getcwd().'/config'; $this->targetFileRelativePath = 'config/file'; $this->targetFile = $this->targetDirectory.'/file'; @@ -108,7 +127,7 @@ protected function tearDown() private function createConfigurator(): CopyFromRecipeConfigurator { - return new CopyFromRecipeConfigurator($this->getMockBuilder(Composer::class)->getMock(), $this->io, new Options()); + return new CopyFromRecipeConfigurator($this->getMockBuilder(Composer::class)->getMock(), $this->io, new Options([], $this->io)); } private function cleanUpTargetFiles() diff --git a/tests/Configurator/EnvConfiguratorTest.php b/tests/Configurator/EnvConfiguratorTest.php index c44526194..e2f901f3d 100644 --- a/tests/Configurator/EnvConfiguratorTest.php +++ b/tests/Configurator/EnvConfiguratorTest.php @@ -11,8 +11,6 @@ namespace Symfony\Flex\Tests\Configurator; -require_once __DIR__.'/TmpDirMock.php'; - use Composer\Composer; use Composer\IO\IOInterface; use PHPUnit\Framework\TestCase; @@ -33,11 +31,11 @@ public function testConfigure() $recipe = $this->getMockBuilder(Recipe::class)->disableOriginalConstructor()->getMock(); $recipe->expects($this->any())->method('getName')->will($this->returnValue('FooBundle')); - $env = sys_get_temp_dir().'/.env.dist'; + $env = getcwd().'/.env.dist'; @unlink($env); touch($env); - $phpunit = sys_get_temp_dir().'/phpunit.xml'; + $phpunit = getcwd().'/phpunit.xml'; $phpunitDist = $phpunit.'.dist'; @unlink($phpunit); @unlink($phpunitDist); @@ -160,10 +158,10 @@ public function testConfigureGeneratedSecret() $recipe = $this->getMockBuilder(Recipe::class)->disableOriginalConstructor()->getMock(); $recipe->expects($this->any())->method('getName')->will($this->returnValue('FooBundle')); - $env = sys_get_temp_dir().'/.env.dist'; + $env = getcwd().'/.env.dist'; @unlink($env); touch($env); - $phpunit = sys_get_temp_dir().'/phpunit.xml'; + $phpunit = getcwd().'/phpunit.xml'; $phpunitDist = $phpunit.'.dist'; @unlink($phpunit); @unlink($phpunitDist); diff --git a/tests/Configurator/GitignoreConfiguratorTest.php b/tests/Configurator/GitignoreConfiguratorTest.php index 4898fea35..0bde2c79e 100644 --- a/tests/Configurator/GitignoreConfiguratorTest.php +++ b/tests/Configurator/GitignoreConfiguratorTest.php @@ -11,8 +11,6 @@ namespace Symfony\Flex\Tests\Configurator; -require_once __DIR__.'/TmpDirMock.php'; - use Composer\Composer; use Composer\IO\IOInterface; use PHPUnit\Framework\TestCase; @@ -36,7 +34,7 @@ public function testConfigure() $recipe2 = $this->getMockBuilder(Recipe::class)->disableOriginalConstructor()->getMock(); $recipe2->expects($this->any())->method('getName')->will($this->returnValue('BarBundle')); - $gitignore = sys_get_temp_dir().'/.gitignore'; + $gitignore = getcwd().'/.gitignore'; @unlink($gitignore); touch($gitignore); diff --git a/tests/Configurator/MakefileConfiguratorTest.php b/tests/Configurator/MakefileConfiguratorTest.php index 099f2554f..cc62645a2 100644 --- a/tests/Configurator/MakefileConfiguratorTest.php +++ b/tests/Configurator/MakefileConfiguratorTest.php @@ -11,8 +11,6 @@ namespace Symfony\Flex\Tests\Configurator; -require_once __DIR__.'/TmpDirMock.php'; - use Composer\Composer; use Composer\IO\IOInterface; use PHPUnit\Framework\TestCase; @@ -36,7 +34,7 @@ public function testConfigure() $recipe2 = $this->getMockBuilder(Recipe::class)->disableOriginalConstructor()->getMock(); $recipe2->expects($this->any())->method('getName')->will($this->returnValue('BarBundle')); - $makefile = sys_get_temp_dir().'/Makefile'; + $makefile = getcwd().'/Makefile'; @unlink($makefile); touch($makefile); diff --git a/tests/Configurator/TmpDirMock.php b/tests/Configurator/TmpDirMock.php deleted file mode 100644 index c09f63a84..000000000 --- a/tests/Configurator/TmpDirMock.php +++ /dev/null @@ -1,8 +0,0 @@ -getMockBuilder(Lock::class)->disableOriginalConstructor()->getMock(); - $lock->expects($this->any())->method('has')->will($this->returnValue(false)); - - $flex = \Closure::bind(function () use ($lock) { - $flex = new Flex(); - $flex->lock = $lock; - - return $flex; - }, null, Flex::class)->__invoke(); - - /** @var InstallOperation[] $actualInstallOperations */ - foreach ($actualInstallOperations as $operation) { - $event = $this->getMockBuilder(PackageEvent::class)->disableOriginalConstructor()->getMock(); - $event->expects($this->any())->method('getOperation')->willReturn($operation); - - $flex->record($event); - } - - $this->assertAttributeEquals($expectedFinalOperators, 'operations', $flex); - } - - public function getRecordTests() - { - $operationFoo = new InstallOperation(new Package('vendor/foo', '1.0.0', '1.0.0')); - $operationFB = new InstallOperation(new Package('symfony/framework-bundle', '1.0.0', '1.0.0')); - $operationFlex = new InstallOperation(new Package('symfony/flex', '1.0.0', '1.0.0')); - - return [ - [ - // install order - [$operationFoo, $operationFB, $operationFlex], - // expected final order - [$operationFlex, $operationFB, $operationFoo], - ], - [ - [$operationFoo, $operationFlex, $operationFB], - [$operationFlex, $operationFB, $operationFoo], - ], - ]; - } - public function testPostInstall() { $package = new Package('dummy/dummy', '1.0.0', '1.0.0'); diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 000000000..de90d20e9 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,30 @@ +isDir() ? 'rmdir' : 'unlink')($fileinfo->getRealPath()); + } + + rmdir($buildDir); +}