From 0e363a59efeb4cfbcc3d0ebd0fe45ec70e2ef5ce Mon Sep 17 00:00:00 2001 From: Tac Tacelosky Date: Fri, 27 May 2022 12:06:16 -0400 Subject: [PATCH 1/5] check for AbstractBundle --- src/SymfonyBundle.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/SymfonyBundle.php b/src/SymfonyBundle.php index 5c1cd1a62..7d1d8a1c3 100644 --- a/src/SymfonyBundle.php +++ b/src/SymfonyBundle.php @@ -107,6 +107,9 @@ private function isBundleClass(string $class, string $path, bool $isPsr4): bool } // heuristic that should work in almost all cases - return false !== strpos(file_get_contents($classPath), 'Symfony\Component\HttpKernel\Bundle\Bundle'); + $classContents = file_get_contents($classPath); + + return (false !== strpos($classContents, 'Symfony\Component\HttpKernel\Bundle\Bundle')) + || (false !== strpos($classContents, 'Symfony\Component\HttpKernel\Bundle\AbstractBundle')); } } From 676f7e2af7de1eb61703f8d07cd0ddad4a42faca Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 1 Jun 2022 17:52:22 +0200 Subject: [PATCH 2/5] Prevent recipes bound to packs from being uninstalled when unpacking --- src/Flex.php | 23 ++++++++++++++++++----- tests/FlexTest.php | 2 ++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/Flex.php b/src/Flex.php index e1d345ab7..ba038c579 100644 --- a/src/Flex.php +++ b/src/Flex.php @@ -306,6 +306,14 @@ public function configureInstaller() foreach ($backtrace as $trace) { if (isset($trace['object']) && $trace['object'] instanceof Installer) { $this->installer = $trace['object']->setSuggestedPackagesReporter(new SuggestedPackagesReporter(new NullIO())); + + $updateAllowList = \Closure::bind(function () { + return $this->updateWhitelist ?? $this->updateAllowList; + }, $this->installer, $this->installer)(); + + if (['php' => 0] === $updateAllowList) { + $this->dryRun = true; // prevent recipes from being uninstalled when removing a pack + } } if (isset($trace['object']) && $trace['object'] instanceof GlobalCommand) { @@ -754,6 +762,7 @@ public function fetchRecipes(array $operations, bool $reset): array $recipes = [ 'symfony/framework-bundle' => null, ]; + $packRecipes = []; $metaRecipes = []; foreach ($operations as $operation) { @@ -803,12 +812,16 @@ public function fetchRecipes(array $operations, bool $reset): array } if (isset($manifests[$name])) { - if ('metapackage' === $package->getType()) { - $metaRecipes[$name] = new Recipe($package, $name, $job, $manifests[$name], $locks[$name] ?? []); + $recipe = new Recipe($package, $name, $job, $manifests[$name], $locks[$name] ?? []); + + if ('symfony-pack' === $package->getType()) { + $packRecipes[$name] = $recipe; + } elseif ('metapackage' === $package->getType()) { + $metaRecipes[$name] = $recipe; } elseif ('symfony/flex' === $name) { - $flexRecipe = [$name => new Recipe($package, $name, $job, $manifests[$name], $locks[$name] ?? [])]; + $flexRecipe = [$name => $recipe]; } else { - $recipes[$name] = new Recipe($package, $name, $job, $manifests[$name], $locks[$name] ?? []); + $recipes[$name] = $recipe; } } @@ -834,7 +847,7 @@ public function fetchRecipes(array $operations, bool $reset): array } } - return array_merge($flexRecipe, $metaRecipes, array_filter($recipes)); + return array_merge($flexRecipe, $packRecipes, $metaRecipes, array_filter($recipes)); } public function truncatePackages(PrePoolCreateEvent $event) diff --git a/tests/FlexTest.php b/tests/FlexTest.php index 26ef496c1..ab8fe4af4 100644 --- a/tests/FlexTest.php +++ b/tests/FlexTest.php @@ -187,6 +187,7 @@ public function testFetchRecipesOrder() ['name' => 'symfony/flex', 'type' => 'composer-plugin'], ['name' => 'symfony/framework-bundle', 'type' => 'library'], ['name' => 'symfony/webapp-meta', 'type' => 'metapackage'], + ['name' => 'symfony/webapp-pack', 'type' => 'symfony-pack'], ]; $io = new BufferIO('', OutputInterface::VERBOSITY_VERBOSE); @@ -209,6 +210,7 @@ public function testFetchRecipesOrder() $this->assertSame([ 'symfony/flex', + 'symfony/webapp-pack', 'symfony/webapp-meta', 'symfony/framework-bundle', 'symfony/console', From cd634ef5fc66cd151d2c58e426cc370c2d1ebbe4 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 2 Jun 2022 18:49:19 +0200 Subject: [PATCH 3/5] Remove overriding native composer commands That doesn't work since Composer v2.1.6 and this commit anyway: https://github.com/composer/composer/commit/9727adf63b0a6a7f082d1da572eff42e52e2dab1 --- src/Command/RemoveCommand.php | 36 -------------- src/Command/RequireCommand.php | 90 ---------------------------------- src/Command/UpdateCommand.php | 41 ---------------- src/Flex.php | 4 -- 4 files changed, 171 deletions(-) delete mode 100644 src/Command/RemoveCommand.php delete mode 100644 src/Command/RequireCommand.php delete mode 100644 src/Command/UpdateCommand.php diff --git a/src/Command/RemoveCommand.php b/src/Command/RemoveCommand.php deleted file mode 100644 index d6cd698ba..000000000 --- a/src/Command/RemoveCommand.php +++ /dev/null @@ -1,36 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Flex\Command; - -use Composer\Command\RemoveCommand as BaseRemoveCommand; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Flex\PackageResolver; - -class RemoveCommand extends BaseRemoveCommand -{ - private $resolver; - - public function __construct(PackageResolver $resolver) - { - $this->resolver = $resolver; - - parent::__construct(); - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - $input->setArgument('packages', $this->resolver->resolve($input->getArgument('packages'))); - - return parent::execute($input, $output); - } -} diff --git a/src/Command/RequireCommand.php b/src/Command/RequireCommand.php deleted file mode 100644 index a92eef093..000000000 --- a/src/Command/RequireCommand.php +++ /dev/null @@ -1,90 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Flex\Command; - -use Composer\Command\RequireCommand as BaseRequireCommand; -use Composer\Factory; -use Composer\Json\JsonFile; -use Composer\Json\JsonManipulator; -use Composer\Plugin\PluginInterface; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Flex\PackageResolver; - -class RequireCommand extends BaseRequireCommand -{ - private $resolver; - private $updateComposerLock; - - public function __construct(PackageResolver $resolver, \Closure $updateComposerLock = null) - { - $this->resolver = $resolver; - $this->updateComposerLock = $updateComposerLock; - - parent::__construct(); - } - - protected function configure() - { - parent::configure(); - $this->addOption('no-unpack', null, InputOption::VALUE_NONE, '[DEPRECATED] Disable unpacking Symfony packs in composer.json.'); - $this->addOption('unpack', null, InputOption::VALUE_NONE, '[DEPRECATED] Unpacking is now enabled by default.'); - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - if ($input->getOption('no-unpack')) { - $this->getIO()->writeError('The "--unpack" command line option is deprecated; unpacking is now enabled by default.'); - } - - if ($input->getOption('unpack')) { - $this->getIO()->writeError('The "--unpack" command line option is deprecated; unpacking is now enabled by default.'); - } - - $packages = $this->resolver->resolve($input->getArgument('packages'), true); - if ($packages) { - $input->setArgument('packages', $this->resolver->resolve($input->getArgument('packages'), true)); - } - - if (version_compare('2.0.0', PluginInterface::PLUGIN_API_VERSION, '>') && $input->hasOption('no-suggest')) { - $input->setOption('no-suggest', true); - } - - $file = Factory::getComposerFile(); - $contents = file_get_contents($file); - $json = JsonFile::parseJson($contents); - - if (\array_key_exists('require-dev', $json) && !$json['require-dev'] && (new JsonManipulator($contents))->removeMainKey('require-dev')) { - $manipulator = new JsonManipulator($contents); - $manipulator->addLink('require-dev', 'php', '*'); - file_put_contents($file, $manipulator->getContents()); - } else { - $file = null; - } - unset($contents, $json, $manipulator); - - try { - return parent::execute($input, $output) ?? 0; - } finally { - if (null !== $file) { - $manipulator = new JsonManipulator(file_get_contents($file)); - $manipulator->removeSubNode('require-dev', 'php'); - file_put_contents($file, $manipulator->getContents()); - - if ($this->updateComposerLock) { - ($this->updateComposerLock)(); - } - } - } - } -} diff --git a/src/Command/UpdateCommand.php b/src/Command/UpdateCommand.php deleted file mode 100644 index 82329f4df..000000000 --- a/src/Command/UpdateCommand.php +++ /dev/null @@ -1,41 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Flex\Command; - -use Composer\Command\UpdateCommand as BaseUpdateCommand; -use Composer\Plugin\PluginInterface; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Flex\PackageResolver; - -class UpdateCommand extends BaseUpdateCommand -{ - private $resolver; - - public function __construct(PackageResolver $resolver) - { - $this->resolver = $resolver; - - parent::__construct(); - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - $input->setArgument('packages', $this->resolver->resolve($input->getArgument('packages'))); - - if (version_compare('2.0.0', PluginInterface::PLUGIN_API_VERSION, '>') && $input->hasOption('no-suggest')) { - $input->setOption('no-suggest', true); - } - - return parent::execute($input, $output); - } -} diff --git a/src/Flex.php b/src/Flex.php index ba038c579..cae35e33e 100644 --- a/src/Flex.php +++ b/src/Flex.php @@ -248,7 +248,6 @@ class_exists(__NAMESPACE__.str_replace('/', '\\', substr($file, \strlen(__DIR__) } if (isset(self::$aliasResolveCommands[$command])) { - // early resolve for BC with Composer 1.0 if ($input->hasArgument('packages')) { $input->setArgument('packages', $resolver->resolve($input->getArgument('packages'), self::$aliasResolveCommands[$command])); } @@ -279,9 +278,6 @@ class_exists(__NAMESPACE__.str_replace('/', '\\', substr($file, \strlen(__DIR__) $this->populateRepoCacheDir(); } - $app->add(new Command\RequireCommand($resolver, \Closure::fromCallable([$this, 'updateComposerLock']))); - $app->add(new Command\UpdateCommand($resolver)); - $app->add(new Command\RemoveCommand($resolver)); $app->add(new Command\UnpackCommand($resolver)); $app->add(new Command\RecipesCommand($this, $this->lock, $rfs)); $app->add(new Command\InstallRecipesCommand($this, $this->options->get('root-dir'), $this->options->get('runtime')['dotenv_path'] ?? '.env')); From 4a1f7a74022396e7fd48e7f442a6f3fabd34da52 Mon Sep 17 00:00:00 2001 From: PabloKowalczyk <11366345+PabloKowalczyk@users.noreply.github.com> Date: Tue, 10 May 2022 15:53:13 +0200 Subject: [PATCH 4/5] Propagate COMPOSER_MEMORY_LIMIT to script executor --- src/ScriptExecutor.php | 4 +++ tests/ScriptExecutorTest.php | 58 ++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 tests/ScriptExecutorTest.php diff --git a/src/ScriptExecutor.php b/src/ScriptExecutor.php index 967aba97c..3acba7270 100644 --- a/src/ScriptExecutor.php +++ b/src/ScriptExecutor.php @@ -128,6 +128,10 @@ private function expandPhpScript(string $cmd): string $arguments[] = '--php-ini='.$ini; } + if ($memoryLimit = (string) getenv('COMPOSER_MEMORY_LIMIT')) { + $arguments[] = "-d memory_limit={$memoryLimit}"; + } + $phpArgs = implode(' ', array_map([ProcessExecutor::class, 'escape'], $arguments)); return ProcessExecutor::escape($php).($phpArgs ? ' '.$phpArgs : '').' '.$cmd; diff --git a/tests/ScriptExecutorTest.php b/tests/ScriptExecutorTest.php new file mode 100644 index 000000000..211910ac4 --- /dev/null +++ b/tests/ScriptExecutorTest.php @@ -0,0 +1,58 @@ + + * + * 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\Composer; +use Composer\IO\NullIO; +use Composer\Util\ProcessExecutor; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Process\PhpExecutableFinder; +use Symfony\Flex\Options; +use Symfony\Flex\ScriptExecutor; + +final class ScriptExecutorTest extends TestCase +{ + /** + * @backupGlobals enabled + */ + public function testMemoryLimit(): void + { + $command = './command.php'; + $memoryLimit = '32M'; + putenv("COMPOSER_MEMORY_LIMIT={$memoryLimit}"); + $executorMock = $this->createMock(ProcessExecutor::class); + $scriptExecutor = new ScriptExecutor(new Composer(), new NullIO(), new Options(), $executorMock); + + $phpFinder = new PhpExecutableFinder(); + if (!$php = $phpFinder->find(false)) { + throw new \RuntimeException('The PHP executable could not be found, add it to your PATH and try again.'); + } + + $arguments = $phpFinder->findArguments(); + $ini = php_ini_loaded_file(); + $arguments[] = "--php-ini={$ini}"; + $arguments[] = "-d memory_limit={$memoryLimit}"; + + $phpArgs = implode(' ', array_map([ProcessExecutor::class, 'escape'], $arguments)); + + $expectedCommand = ProcessExecutor::escape($php).($phpArgs ? ' '.$phpArgs : '').' '.$command; + + $executorMock + ->method('execute') + ->with($expectedCommand) + ->willReturn(0) + ; + $this->expectNotToPerformAssertions(); + + $scriptExecutor->execute('php-script', $command); + } +} From c36d11d1bca0a85dde6ca4d52bd4aceeb54dbd8f Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 3 Jun 2022 09:58:31 +0200 Subject: [PATCH 5/5] Deprecate "flex-require" sections --- src/Flex.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Flex.php b/src/Flex.php index cae35e33e..cdb52c8be 100644 --- a/src/Flex.php +++ b/src/Flex.php @@ -415,6 +415,9 @@ public function update(Event $event, $operations = []) if (!isset($json['flex-'.$type])) { continue; } + + $this->io->writeError(sprintf('Using section "flex-%s" in composer.json is deprecated, use "%1$s" instead.', $type)); + foreach ($json['flex-'.$type] as $package => $constraint) { if ($symfonyVersion && '*' === $constraint && isset($versions['splits'][$package])) { // replace unbounded constraints for symfony/* packages by extra.symfony.require