From 750692227fde2d9a9e97d116c8f16365ff641dea Mon Sep 17 00:00:00 2001 From: Dzhuneyt Ahmed Date: Mon, 7 Jan 2019 17:46:33 +0200 Subject: [PATCH 01/17] Added no-cache argument to "composer install" --- src/Composer/Command/InstallCommand.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index cc590d8c9..b2d2e4a54 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -45,6 +45,7 @@ class InstallCommand extends BaseCommand new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'), new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), new InputOption('no-suggest', null, InputOption::VALUE_NONE, 'Do not show package suggestions.'), + new InputOption('no-cache', null, InputOption::VALUE_NONE, 'Do not use the cache directory'), new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'), new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'), new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'), From 8c30b12bd9002de9fc65eafe1b2cbdea764b6857 Mon Sep 17 00:00:00 2001 From: Dzhuneyt Ahmed Date: Mon, 7 Jan 2019 18:36:21 +0200 Subject: [PATCH 02/17] Added no-cache argument to "composer install" and "composer update" --- src/Composer/Command/InstallCommand.php | 15 ++++++++++++++- src/Composer/Command/UpdateCommand.php | 14 ++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index b2d2e4a54..a2e55b6c8 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -15,6 +15,7 @@ namespace Composer\Command; use Composer\Installer; use Composer\Plugin\CommandEvent; use Composer\Plugin\PluginEvents; +use Composer\Util\Platform; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputArgument; @@ -40,12 +41,12 @@ class InstallCommand extends BaseCommand new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'), new InputOption('dev', null, InputOption::VALUE_NONE, 'Enables installation of require-dev packages (enabled by default, only present for BC).'), new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables installation of require-dev packages.'), + new InputOption('no-cache', null, InputOption::VALUE_NONE, 'Do not use the cache directory'), new InputOption('no-custom-installers', null, InputOption::VALUE_NONE, 'DEPRECATED: Use no-plugins instead.'), new InputOption('no-autoloader', null, InputOption::VALUE_NONE, 'Skips autoloader generation'), new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'), new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), new InputOption('no-suggest', null, InputOption::VALUE_NONE, 'Do not show package suggestions.'), - new InputOption('no-cache', null, InputOption::VALUE_NONE, 'Do not use the cache directory'), new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'), new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'), new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'), @@ -94,6 +95,18 @@ EOT $install = Installer::create($io, $composer); $config = $composer->getConfig(); + + if ($input->getOption('no-cache')) { + $io->write('Skipping cache directory'); + $config->merge( + array( + 'config' => array( + 'cache-dir' => Platform::isWindows() ? 'nul' : '/dev/null', + ) + ) + ); + } + list($preferSource, $preferDist) = $this->getPreferredInstallOptions($config, $input); $optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader'); diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index 34420b747..a912cc373 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -17,6 +17,7 @@ use Composer\Installer; use Composer\IO\IOInterface; use Composer\Plugin\CommandEvent; use Composer\Plugin\PluginEvents; +use Composer\Util\Platform; use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -44,6 +45,7 @@ class UpdateCommand extends BaseCommand new InputOption('dev', null, InputOption::VALUE_NONE, 'Enables installation of require-dev packages (enabled by default, only present for BC).'), new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables installation of require-dev packages.'), new InputOption('lock', null, InputOption::VALUE_NONE, 'Only updates the lock file hash to suppress warning about the lock file being out of date.'), + new InputOption('no-cache', null, InputOption::VALUE_NONE, 'Do not use the cache directory'), new InputOption('no-custom-installers', null, InputOption::VALUE_NONE, 'DEPRECATED: Use no-plugins instead.'), new InputOption('no-autoloader', null, InputOption::VALUE_NONE, 'Skips autoloader generation'), new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'), @@ -128,6 +130,18 @@ EOT $install = Installer::create($io, $composer); $config = $composer->getConfig(); + + if ($input->getOption('no-cache')) { + $io->write('Skipping cache directory'); + $config->merge( + array( + 'config' => array( + 'cache-dir' => Platform::isWindows() ? 'nul' : '/dev/null', + ) + ) + ); + } + list($preferSource, $preferDist) = $this->getPreferredInstallOptions($config, $input); $optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader'); From 5b78ea529a90f273684b338132e6b84f46b5209f Mon Sep 17 00:00:00 2001 From: Den Girnyk Date: Thu, 17 Jan 2019 18:11:32 +0200 Subject: [PATCH 03/17] Fix: Keep replaced packages for autoload dumping with --no-dev --- src/Composer/Autoload/AutoloadGenerator.php | 8 +++-- .../Test/Autoload/AutoloadGeneratorTest.php | 34 +++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 0db4015c3..7ea1a3444 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -940,9 +940,13 @@ INITIALIZER; $packageMap, function ($item) use ($include) { $package = $item[0]; - $name = $package->getName(); + foreach ($package->getNames() as $name) { + if (isset($include[$name])) { + return true; + } + } - return isset($include[$name]); + return false; } ); } diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 4d672084e..615a613f9 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -14,6 +14,7 @@ namespace Composer\Test\Autoload; use Composer\Autoload\AutoloadGenerator; use Composer\Package\Link; +use Composer\Semver\Constraint\Constraint; use Composer\Util\Filesystem; use Composer\Package\AliasPackage; use Composer\Package\Package; @@ -419,6 +420,39 @@ class AutoloadGeneratorTest extends TestCase $this->assertFileExists($this->vendorDir.'/composer/autoload_classmap.php', "ClassMap file needs to be generated, even if empty."); } + public function testNonDevAutoloadShouldIncludeReplacedPackages() + { + $package = new Package('a', '1.0', '1.0'); + $package->setRequires(array(new Link('a', 'a/a'))); + + $packages = array(); + $packages[] = $a = new Package('a/a', '1.0', '1.0'); + $packages[] = $b = new Package('b/b', '1.0', '1.0'); + + $a->setRequires(array(new Link('a/a', 'b/c'))); + + $b->setAutoload(array('psr-4' => array('B\\' => 'src/'))); + $b->setReplaces( + array(new Link('b/b', 'b/c', new Constraint('==', '1.0'), 'replaces')) + ); + + $this->repository->expects($this->once()) + ->method('getCanonicalPackages') + ->will($this->returnValue($packages)); + + $this->fs->ensureDirectoryExists($this->vendorDir.'/b/b/src/C'); + file_put_contents($this->vendorDir.'/b/b/src/C/C.php', 'generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_5'); + + $this->assertEquals( + array( + 'B\\C\\C' => $this->vendorDir.'/b/b/src/C/C.php', + ), + include $this->vendorDir.'/composer/autoload_classmap.php' + ); + } + public function testPSRToClassMapIgnoresNonExistingDir() { $package = new Package('a', '1.0', '1.0'); From a9aaa25d4c705219a4c14f2f3bb58395aac6cf24 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 28 Jan 2019 14:38:32 +0100 Subject: [PATCH 04/17] Fix compat with Symfony Process 4.2, fixes #7923 --- src/Composer/Command/InitCommand.php | 7 ++++++- src/Composer/Util/Perforce.php | 8 +++++++- src/Composer/Util/ProcessExecutor.php | 8 +++++++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 02efbf686..abcea73b2 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -557,7 +557,12 @@ EOT $finder = new ExecutableFinder(); $gitBin = $finder->find('git'); - $cmd = new Process(sprintf('%s config -l', ProcessExecutor::escape($gitBin))); + // TODO in v3 always call with an array + if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandline')) { + $cmd = new Process(array($gitBin, 'config', '-l')); + } else { + $cmd = new Process(sprintf('%s config -l', ProcessExecutor::escape($gitBin))); + } $cmd->run(); if ($cmd->isSuccessful()) { diff --git a/src/Composer/Util/Perforce.php b/src/Composer/Util/Perforce.php index b064feec4..31ddeffec 100644 --- a/src/Composer/Util/Perforce.php +++ b/src/Composer/Util/Perforce.php @@ -370,7 +370,13 @@ class Perforce public function windowsLogin($password) { $command = $this->generateP4Command(' login -a'); - $process = new Process($command, null, null, $password); + + // TODO in v3 generate command as an array + if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandline')) { + $process = Process::fromShellCommandline($command, null, null, $password); + } else { + $process = new Process($command, null, null, $password); + } return $process->run(); } diff --git a/src/Composer/Util/ProcessExecutor.php b/src/Composer/Util/ProcessExecutor.php index 2a0742778..f5e1ef610 100644 --- a/src/Composer/Util/ProcessExecutor.php +++ b/src/Composer/Util/ProcessExecutor.php @@ -62,7 +62,13 @@ class ProcessExecutor $this->captureOutput = func_num_args() > 1; $this->errorOutput = null; - $process = new Process($command, $cwd, null, null, static::getTimeout()); + + // TODO in v3, commands should be passed in as arrays of cmd + args + if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandline')) { + $process = Process::fromShellCommandline($command, $cwd, null, null, static::getTimeout()); + } else { + $process = new Process($command, $cwd, null, null, static::getTimeout()); + } $callback = is_callable($output) ? $output : array($this, 'outputHandler'); $process->run($callback); From 02ceb74151eb027cf74c17059dfb80a1af1a088d Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 28 Jan 2019 15:29:37 +0100 Subject: [PATCH 05/17] Tweak --no-cache option to be available globally and to not break VCS drivers relying on it, refs #7880, refs #6650 --- doc/03-cli.md | 2 ++ src/Composer/Cache.php | 7 ++++++- src/Composer/Command/InstallCommand.php | 14 -------------- src/Composer/Command/UpdateCommand.php | 14 -------------- src/Composer/Console/Application.php | 6 ++++++ src/Composer/Downloader/GitDownloader.php | 3 ++- src/Composer/Repository/Vcs/FossilDriver.php | 5 +++++ src/Composer/Repository/Vcs/GitDriver.php | 4 ++++ src/Composer/Repository/Vcs/HgDriver.php | 5 +++++ src/Composer/Repository/Vcs/PerforceDriver.php | 5 +++++ 10 files changed, 35 insertions(+), 30 deletions(-) diff --git a/doc/03-cli.md b/doc/03-cli.md index 300d4837f..6070b8509 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -22,6 +22,8 @@ The following options are available with every command: * **--quiet (-q):** Do not output any message. * **--no-interaction (-n):** Do not ask any interactive question. * **--no-plugins:** Disables plugins. +* **--no-cache:** Disables the use of the cache directory. Same as setting the COMPOSER_CACHE_DIR + env var to /dev/null (or NUL on Windows). * **--working-dir (-d):** If specified, use the given directory as working directory. * **--profile:** Display timing and memory usage information * **--ansi:** Force ANSI output. diff --git a/src/Composer/Cache.php b/src/Composer/Cache.php index 44395c3a2..3f2861797 100644 --- a/src/Composer/Cache.php +++ b/src/Composer/Cache.php @@ -44,7 +44,7 @@ class Cache $this->whitelist = $whitelist; $this->filesystem = $filesystem ?: new Filesystem(); - if (preg_match('{(^|[\\\\/])(\$null|NUL|/dev/null)([\\\\/]|$)}', $cacheDir)) { + if (!self::isUsable($cacheDir)) { $this->enabled = false; return; @@ -59,6 +59,11 @@ class Cache } } + public static function isUsable($path) + { + return !preg_match('{(^|[\\\\/])(\$null|nul|NUL|/dev/null)([\\\\/]|$)}', $path); + } + public function isEnabled() { return $this->enabled; diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index a2e55b6c8..cc590d8c9 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -15,7 +15,6 @@ namespace Composer\Command; use Composer\Installer; use Composer\Plugin\CommandEvent; use Composer\Plugin\PluginEvents; -use Composer\Util\Platform; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputArgument; @@ -41,7 +40,6 @@ class InstallCommand extends BaseCommand new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'), new InputOption('dev', null, InputOption::VALUE_NONE, 'Enables installation of require-dev packages (enabled by default, only present for BC).'), new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables installation of require-dev packages.'), - new InputOption('no-cache', null, InputOption::VALUE_NONE, 'Do not use the cache directory'), new InputOption('no-custom-installers', null, InputOption::VALUE_NONE, 'DEPRECATED: Use no-plugins instead.'), new InputOption('no-autoloader', null, InputOption::VALUE_NONE, 'Skips autoloader generation'), new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'), @@ -95,18 +93,6 @@ EOT $install = Installer::create($io, $composer); $config = $composer->getConfig(); - - if ($input->getOption('no-cache')) { - $io->write('Skipping cache directory'); - $config->merge( - array( - 'config' => array( - 'cache-dir' => Platform::isWindows() ? 'nul' : '/dev/null', - ) - ) - ); - } - list($preferSource, $preferDist) = $this->getPreferredInstallOptions($config, $input); $optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader'); diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index a912cc373..34420b747 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -17,7 +17,6 @@ use Composer\Installer; use Composer\IO\IOInterface; use Composer\Plugin\CommandEvent; use Composer\Plugin\PluginEvents; -use Composer\Util\Platform; use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -45,7 +44,6 @@ class UpdateCommand extends BaseCommand new InputOption('dev', null, InputOption::VALUE_NONE, 'Enables installation of require-dev packages (enabled by default, only present for BC).'), new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables installation of require-dev packages.'), new InputOption('lock', null, InputOption::VALUE_NONE, 'Only updates the lock file hash to suppress warning about the lock file being out of date.'), - new InputOption('no-cache', null, InputOption::VALUE_NONE, 'Do not use the cache directory'), new InputOption('no-custom-installers', null, InputOption::VALUE_NONE, 'DEPRECATED: Use no-plugins instead.'), new InputOption('no-autoloader', null, InputOption::VALUE_NONE, 'Skips autoloader generation'), new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'), @@ -130,18 +128,6 @@ EOT $install = Installer::create($io, $composer); $config = $composer->getConfig(); - - if ($input->getOption('no-cache')) { - $io->write('Skipping cache directory'); - $config->merge( - array( - 'config' => array( - 'cache-dir' => Platform::isWindows() ? 'nul' : '/dev/null', - ) - ) - ); - } - list($preferSource, $preferDist) = $this->getPreferredInstallOptions($config, $input); $optimize = $input->getOption('optimize-autoloader') || $config->get('optimize-autoloader'); diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index c25ee3fe0..96c3ff821 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -118,6 +118,11 @@ class Application extends BaseApplication ))); ErrorHandler::register($io); + if ($input->hasParameterOption('--no-cache')) { + $io->writeError('Disabling cache usage', true, IOInterface::DEBUG); + putenv('COMPOSER_CACHE_DIR='.(Platform::isWindows() ? 'nul' : '/dev/null')); + } + // switch working dir if ($newWorkDir = $this->getNewWorkingDir($input)) { $oldWorkingDir = getcwd(); @@ -451,6 +456,7 @@ class Application extends BaseApplication $definition->addOption(new InputOption('--profile', null, InputOption::VALUE_NONE, 'Display timing and memory usage information')); $definition->addOption(new InputOption('--no-plugins', null, InputOption::VALUE_NONE, 'Whether to disable plugins.')); $definition->addOption(new InputOption('--working-dir', '-d', InputOption::VALUE_REQUIRED, 'If specified, use the given directory as working directory.')); + $definition->addOption(new InputOption('--no-cache', null, InputOption::VALUE_NONE, 'Prevent use of the cache')); return $definition; } diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index 869d5330b..f1657c6c4 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -19,6 +19,7 @@ use Composer\Util\Filesystem; use Composer\Util\Git as GitUtil; use Composer\Util\Platform; use Composer\Util\ProcessExecutor; +use Composer\Cache; /** * @author Jordi Boggiano @@ -51,7 +52,7 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface $msg = "Cloning ".$this->getShortHash($ref); $command = 'git clone --no-checkout %url% %path% && cd '.$flag.'%path% && git remote add composer %url% && git fetch composer'; - if ($gitVersion && version_compare($gitVersion, '2.3.0-rc0', '>=')) { + if ($gitVersion && version_compare($gitVersion, '2.3.0-rc0', '>=') && Cache::isUsable($cachePath)) { $this->io->writeError('', true, IOInterface::DEBUG); $this->io->writeError(sprintf(' Cloning to cache at %s', ProcessExecutor::escape($cachePath)), true, IOInterface::DEBUG); try { diff --git a/src/Composer/Repository/Vcs/FossilDriver.php b/src/Composer/Repository/Vcs/FossilDriver.php index cc872474e..491fafa86 100644 --- a/src/Composer/Repository/Vcs/FossilDriver.php +++ b/src/Composer/Repository/Vcs/FossilDriver.php @@ -12,6 +12,7 @@ namespace Composer\Repository\Vcs; +use Composer\Cache; use Composer\Config; use Composer\Util\ProcessExecutor; use Composer\Util\Filesystem; @@ -45,6 +46,10 @@ class FossilDriver extends VcsDriver if (Filesystem::isLocalPath($this->url) && is_dir($this->url)) { $this->checkoutDir = $this->url; } else { + if (!Cache::isUsable($this->config->get('cache-repo-dir')) || !Cache::isUsable($this->config->get('cache-vcs-dir'))) { + throw new \RuntimeException('FossilDriver requires a usable cache directory, and it looks like you set it to be disabled'); + } + $localName = preg_replace('{[^a-z0-9]}i', '-', $this->url); $this->repoFile = $this->config->get('cache-repo-dir') . '/' . $localName . '.fossil'; $this->checkoutDir = $this->config->get('cache-vcs-dir') . '/' . $localName . '/'; diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index 0269f4721..4a14974fb 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -41,6 +41,10 @@ class GitDriver extends VcsDriver $this->repoDir = $this->url; $cacheUrl = realpath($this->url); } else { + if (!Cache::isUsable($this->config->get('cache-vcs-dir'))) { + throw new \RuntimeException('GitDriver requires a usable cache directory, and it looks like you set it to be disabled'); + } + $this->repoDir = $this->config->get('cache-vcs-dir') . '/' . preg_replace('{[^a-z0-9.]}i', '-', $this->url) . '/'; GitUtil::cleanEnv(); diff --git a/src/Composer/Repository/Vcs/HgDriver.php b/src/Composer/Repository/Vcs/HgDriver.php index 45f13d5fe..373b24af1 100644 --- a/src/Composer/Repository/Vcs/HgDriver.php +++ b/src/Composer/Repository/Vcs/HgDriver.php @@ -13,6 +13,7 @@ namespace Composer\Repository\Vcs; use Composer\Config; +use Composer\Cache; use Composer\Util\Hg as HgUtils; use Composer\Util\ProcessExecutor; use Composer\Util\Filesystem; @@ -37,6 +38,10 @@ class HgDriver extends VcsDriver if (Filesystem::isLocalPath($this->url)) { $this->repoDir = $this->url; } else { + if (!Cache::isUsable($this->config->get('cache-vcs-dir'))) { + throw new \RuntimeException('HgDriver requires a usable cache directory, and it looks like you set it to be disabled'); + } + $cacheDir = $this->config->get('cache-vcs-dir'); $this->repoDir = $cacheDir . '/' . preg_replace('{[^a-z0-9]}i', '-', $this->url) . '/'; diff --git a/src/Composer/Repository/Vcs/PerforceDriver.php b/src/Composer/Repository/Vcs/PerforceDriver.php index 667f914df..09b5d4b16 100644 --- a/src/Composer/Repository/Vcs/PerforceDriver.php +++ b/src/Composer/Repository/Vcs/PerforceDriver.php @@ -13,6 +13,7 @@ namespace Composer\Repository\Vcs; use Composer\Config; +use Composer\Cache; use Composer\IO\IOInterface; use Composer\Util\ProcessExecutor; use Composer\Util\Perforce; @@ -54,6 +55,10 @@ class PerforceDriver extends VcsDriver return; } + if (!Cache::isUsable($this->config->get('cache-vcs-dir'))) { + throw new \RuntimeException('PerforceDriver requires a usable cache directory, and it looks like you set it to be disabled'); + } + $repoDir = $this->config->get('cache-vcs-dir') . '/' . $this->depot; $this->perforce = Perforce::create($repoConfig, $this->getUrl(), $repoDir, $this->process, $this->io); } From b7d1f8784841e8969340721f98168e4a138a775b Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 28 Jan 2019 15:44:21 +0100 Subject: [PATCH 06/17] Fix tests --- tests/Composer/Test/ApplicationTest.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/Composer/Test/ApplicationTest.php b/tests/Composer/Test/ApplicationTest.php index 5f491440b..8739a5004 100644 --- a/tests/Composer/Test/ApplicationTest.php +++ b/tests/Composer/Test/ApplicationTest.php @@ -31,6 +31,11 @@ class ApplicationTest extends TestCase ->with($this->equalTo('--no-plugins')) ->will($this->returnValue(true)); + $inputMock->expects($this->at($index++)) + ->method('hasParameterOption') + ->with($this->equalTo('--no-cache')) + ->will($this->returnValue(false)); + $inputMock->expects($this->at($index++)) ->method('getParameterOption') ->with($this->equalTo(array('--working-dir', '-d'))) @@ -84,6 +89,11 @@ class ApplicationTest extends TestCase ->with($this->equalTo('--no-plugins')) ->will($this->returnValue(true)); + $inputMock->expects($this->at($index++)) + ->method('hasParameterOption') + ->with($this->equalTo('--no-cache')) + ->will($this->returnValue(false)); + $inputMock->expects($this->at($index++)) ->method('getParameterOption') ->with($this->equalTo(array('--working-dir', '-d'))) From abcde190224398da456ae1bc352091f9bce38231 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 28 Jan 2019 16:17:18 +0100 Subject: [PATCH 07/17] Document --no-check-all better, fixes #7889 --- doc/03-cli.md | 2 +- src/Composer/Command/ValidateCommand.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/03-cli.md b/doc/03-cli.md index 74374ec6b..c3faffe34 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -491,7 +491,7 @@ php composer.phar validate ### Options -* **--no-check-all:** Do not emit a warning if requirements in `composer.json` use unbound version constraints. +* **--no-check-all:** Do not emit a warning if requirements in `composer.json` use unbound or overly strict version constraints. * **--no-check-lock:** Do not emit an error if `composer.lock` exists and is not up to date. * **--no-check-publish:** Do not emit an error if `composer.json` is unsuitable for publishing as a package on Packagist but is otherwise valid. * **--with-dependencies:** Also validate the composer.json of all installed dependencies. diff --git a/src/Composer/Command/ValidateCommand.php b/src/Composer/Command/ValidateCommand.php index b86fd92ea..52023e528 100644 --- a/src/Composer/Command/ValidateCommand.php +++ b/src/Composer/Command/ValidateCommand.php @@ -39,7 +39,7 @@ class ValidateCommand extends BaseCommand ->setName('validate') ->setDescription('Validates a composer.json and composer.lock.') ->setDefinition(array( - new InputOption('no-check-all', null, InputOption::VALUE_NONE, 'Do not make a complete validation'), + new InputOption('no-check-all', null, InputOption::VALUE_NONE, 'Do not validate requires for overly strict/loose constraints'), new InputOption('no-check-lock', null, InputOption::VALUE_NONE, 'Do not check if lock file is up to date'), new InputOption('no-check-publish', null, InputOption::VALUE_NONE, 'Do not check for publish errors'), new InputOption('with-dependencies', 'A', InputOption::VALUE_NONE, 'Also validate the composer.json of all installed dependencies'), From ea333aa134ad36e104e2cb45c79093df4df67b6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20M=C3=B6ller?= Date: Mon, 28 Jan 2019 16:46:58 +0100 Subject: [PATCH 08/17] Fix: Remove empty node --- src/Composer/Config/JsonConfigSource.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Composer/Config/JsonConfigSource.php b/src/Composer/Config/JsonConfigSource.php index 128ebf8ec..15d40d200 100644 --- a/src/Composer/Config/JsonConfigSource.php +++ b/src/Composer/Config/JsonConfigSource.php @@ -193,6 +193,10 @@ class JsonConfigSource implements ConfigSourceInterface { $this->manipulateJson('removeSubNode', $type, $name, function (&$config, $type, $name) { unset($config[$type][$name]); + + if (0 === count($config[$type])) { + unset($config[$type]); + } }); } From 3b6b63784fe5e11e5eaf469aebd6fa40e723403a Mon Sep 17 00:00:00 2001 From: Den Girnyk Date: Thu, 17 Jan 2019 18:11:32 +0200 Subject: [PATCH 09/17] Fix: Keep replaced packages for autoload dumping with --no-dev --- src/Composer/Autoload/AutoloadGenerator.php | 8 +++-- .../Test/Autoload/AutoloadGeneratorTest.php | 34 +++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 0db4015c3..7ea1a3444 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -940,9 +940,13 @@ INITIALIZER; $packageMap, function ($item) use ($include) { $package = $item[0]; - $name = $package->getName(); + foreach ($package->getNames() as $name) { + if (isset($include[$name])) { + return true; + } + } - return isset($include[$name]); + return false; } ); } diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 4d672084e..615a613f9 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -14,6 +14,7 @@ namespace Composer\Test\Autoload; use Composer\Autoload\AutoloadGenerator; use Composer\Package\Link; +use Composer\Semver\Constraint\Constraint; use Composer\Util\Filesystem; use Composer\Package\AliasPackage; use Composer\Package\Package; @@ -419,6 +420,39 @@ class AutoloadGeneratorTest extends TestCase $this->assertFileExists($this->vendorDir.'/composer/autoload_classmap.php', "ClassMap file needs to be generated, even if empty."); } + public function testNonDevAutoloadShouldIncludeReplacedPackages() + { + $package = new Package('a', '1.0', '1.0'); + $package->setRequires(array(new Link('a', 'a/a'))); + + $packages = array(); + $packages[] = $a = new Package('a/a', '1.0', '1.0'); + $packages[] = $b = new Package('b/b', '1.0', '1.0'); + + $a->setRequires(array(new Link('a/a', 'b/c'))); + + $b->setAutoload(array('psr-4' => array('B\\' => 'src/'))); + $b->setReplaces( + array(new Link('b/b', 'b/c', new Constraint('==', '1.0'), 'replaces')) + ); + + $this->repository->expects($this->once()) + ->method('getCanonicalPackages') + ->will($this->returnValue($packages)); + + $this->fs->ensureDirectoryExists($this->vendorDir.'/b/b/src/C'); + file_put_contents($this->vendorDir.'/b/b/src/C/C.php', 'generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_5'); + + $this->assertEquals( + array( + 'B\\C\\C' => $this->vendorDir.'/b/b/src/C/C.php', + ), + include $this->vendorDir.'/composer/autoload_classmap.php' + ); + } + public function testPSRToClassMapIgnoresNonExistingDir() { $package = new Package('a', '1.0', '1.0'); From 386382503dfb009f378f8318c2b9b973c3e6aee7 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 20 Aug 2018 22:06:46 +0200 Subject: [PATCH 10/17] Add a test for autoloading if a package is only required via replacing name --- .../Test/Autoload/AutoloadGeneratorTest.php | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 615a613f9..c1605bf97 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -453,6 +453,39 @@ class AutoloadGeneratorTest extends TestCase ); } + public function testNonDevAutoloadExclusionWithRecursionReplace() + { + $package = new Package('a', '1.0', '1.0'); + $package->setRequires(array( + new Link('a', 'a/a'), + )); + + $packages = array(); + $packages[] = $a = new Package('a/a', '1.0', '1.0'); + $packages[] = $b = new Package('b/b', '1.0', '1.0'); + $a->setAutoload(array('psr-0' => array('A' => 'src/', 'A\\B' => 'lib/'))); + $a->setRequires(array( + new Link('a/a', 'c/c'), + )); + $b->setAutoload(array('psr-0' => array('B\\Sub\\Name' => 'src/'))); + $b->setReplaces(array( + new Link('b/b', 'c/c'), + )); + + $this->repository->expects($this->once()) + ->method('getCanonicalPackages') + ->will($this->returnValue($packages)); + + $this->fs->ensureDirectoryExists($this->vendorDir.'/composer'); + $this->fs->ensureDirectoryExists($this->vendorDir.'/a/a/src'); + $this->fs->ensureDirectoryExists($this->vendorDir.'/a/a/lib'); + $this->fs->ensureDirectoryExists($this->vendorDir.'/b/b/src'); + + $this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', false, '_5'); + $this->assertAutoloadFiles('vendors', $this->vendorDir.'/composer'); + $this->assertFileExists($this->vendorDir.'/composer/autoload_classmap.php', "ClassMap file needs to be generated, even if empty."); + } + public function testPSRToClassMapIgnoresNonExistingDir() { $package = new Package('a', '1.0', '1.0'); From 50cb5fe3daa8e29b1de19cc24dddd278422b8ddd Mon Sep 17 00:00:00 2001 From: Sascha Egerer Date: Wed, 12 Dec 2018 16:21:15 +0100 Subject: [PATCH 11/17] Update all whitelist matching root dependencies The update command can receive a pattern like `vendor/prefix-*` to update all matching packages. This has not worked if multiple packages, depending on each other, where matched to the given pattern. No package has been updated in this case as only the first package matching the pattern was added to the whitelist. --- src/Composer/Installer.php | 2 +- ...elist-patterns-with-root-dependencies.test | 45 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-root-dependencies.test diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index bd0d22e3c..12f071c51 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -1332,8 +1332,8 @@ class Installer $whitelistPatternRegexp = BasePackage::packageNameToRegexp($packageName); foreach ($rootRequiredPackageNames as $rootRequiredPackageName) { if (preg_match($whitelistPatternRegexp, $rootRequiredPackageName)) { + $depPackages = array_merge($pool->whatProvides($rootRequiredPackageName)); $nameMatchesRequiredPackage = true; - break; } } } diff --git a/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-root-dependencies.test b/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-root-dependencies.test new file mode 100644 index 000000000..360ef49f5 --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-root-dependencies.test @@ -0,0 +1,45 @@ +--TEST-- +Update with a package whitelist only updates those packages and their dependencies matching the pattern +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "fixed", "version": "1.1.0" }, + { "name": "fixed", "version": "1.0.0" }, + { "name": "whitelisted-component1", "version": "1.1.0", "require": { "whitelisted-component2": "1.1.0" } }, + { "name": "whitelisted-component1", "version": "1.0.0", "require": { "whitelisted-component2": "1.0.0" } }, + { "name": "whitelisted-component2", "version": "1.1.0", "require": { "dependency": "1.1.0" } }, + { "name": "whitelisted-component2", "version": "1.0.0", "require": { "dependency": "1.0.0" } }, + { "name": "dependency", "version": "1.1.0" }, + { "name": "dependency", "version": "1.0.0" }, + { "name": "unrelated", "version": "1.1.0", "require": { "unrelated-dependency": "1.*" } }, + { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } }, + { "name": "unrelated-dependency", "version": "1.1.0" }, + { "name": "unrelated-dependency", "version": "1.0.0" } + ] + } + ], + "require": { + "fixed": "1.*", + "whitelisted-component1": "1.*", + "whitelisted-component2": "1.*", + "unrelated": "1.*" + } +} +--INSTALLED-- +[ + { "name": "fixed", "version": "1.0.0" }, + { "name": "whitelisted-component1", "version": "1.0.0", "require": { "whitelisted-component2": "1.0.0" } }, + { "name": "whitelisted-component2", "version": "1.0.0", "require": { "dependency": "1.0.0" } }, + { "name": "dependency", "version": "1.0.0" }, + { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } }, + { "name": "unrelated-dependency", "version": "1.0.0" } +] +--RUN-- +update whitelisted-* --with-dependencies +--EXPECT-- +Updating dependency (1.0.0) to dependency (1.1.0) +Updating whitelisted-component2 (1.0.0) to whitelisted-component2 (1.1.0) +Updating whitelisted-component1 (1.0.0) to whitelisted-component1 (1.1.0) From dc59af555aadcf52ff74735de571f5f5e3318560 Mon Sep 17 00:00:00 2001 From: Sascha Egerer Date: Wed, 12 Dec 2018 17:27:26 +0100 Subject: [PATCH 12/17] Fix invalid call to array_merge --- src/Composer/Installer.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 12f071c51..2046dd1da 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -1323,7 +1323,7 @@ class Installer foreach ($this->updateWhitelist as $packageName => $void) { $packageQueue = new \SplQueue; - $depPackages = $pool->whatProvides($packageName); + $depPackages = [$pool->whatProvides($packageName)]; $nameMatchesRequiredPackage = in_array($packageName, $requiredPackageNames, true); @@ -1332,12 +1332,14 @@ class Installer $whitelistPatternRegexp = BasePackage::packageNameToRegexp($packageName); foreach ($rootRequiredPackageNames as $rootRequiredPackageName) { if (preg_match($whitelistPatternRegexp, $rootRequiredPackageName)) { - $depPackages = array_merge($pool->whatProvides($rootRequiredPackageName)); + $depPackages[] = $pool->whatProvides($rootRequiredPackageName); $nameMatchesRequiredPackage = true; } } } + $depPackages = array_merge(...$depPackages); + if (count($depPackages) == 0 && !$nameMatchesRequiredPackage && !in_array($packageName, array('nothing', 'lock', 'mirrors'))) { $this->io->writeError('Package "' . $packageName . '" listed for update is not installed. Ignoring.'); } From 1845adcfbdeeff9884ff4561287a32ffaab25749 Mon Sep 17 00:00:00 2001 From: Sascha Egerer Date: Thu, 13 Dec 2018 13:54:22 +0100 Subject: [PATCH 13/17] Fix update whitelist pattern resolving and add more tests --- src/Composer/Installer.php | 25 +++++----- src/Composer/Package/BasePackage.php | 5 +- ...telist-patterns-with-all-dependencies.test | 46 +++++++++++++++++ ...-whitelist-patterns-with-dependencies.test | 49 +++++++++++++++++++ ...elist-patterns-with-root-dependencies.test | 12 ++++- ...itelist-patterns-without-dependencies.test | 44 +++++++++++++++++ 6 files changed, 165 insertions(+), 16 deletions(-) create mode 100644 tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-all-dependencies.test create mode 100644 tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-dependencies.test create mode 100644 tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-without-dependencies.test diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 2046dd1da..88b4ea1d5 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -1301,11 +1301,6 @@ class Installer $rootRequires = array_merge($rootRequires, $rootDevRequires); - $requiredPackageNames = array(); - foreach ($rootRequires as $require) { - $requiredPackageNames[] = $require->getTarget(); - } - $skipPackages = array(); if (!$this->whitelistAllDependencies) { foreach ($rootRequires as $require) { @@ -1323,22 +1318,26 @@ class Installer foreach ($this->updateWhitelist as $packageName => $void) { $packageQueue = new \SplQueue; - $depPackages = [$pool->whatProvides($packageName)]; - - $nameMatchesRequiredPackage = in_array($packageName, $requiredPackageNames, true); - + $depPackages = $pool->whatProvides($packageName); + $matchesByPattern = []; // check if the name is a glob pattern that did not match directly - if (!$nameMatchesRequiredPackage) { + if (empty($depPackages)) { + $whitelistPatternSearchRegexp = BasePackage::packageNameToRegexp($packageName, '^%s$'); + foreach ($localOrLockRepo->search($whitelistPatternSearchRegexp) as $installedPackage) { + $matchesByPattern[] = $pool->whatProvides($installedPackage['name']); + } $whitelistPatternRegexp = BasePackage::packageNameToRegexp($packageName); foreach ($rootRequiredPackageNames as $rootRequiredPackageName) { if (preg_match($whitelistPatternRegexp, $rootRequiredPackageName)) { - $depPackages[] = $pool->whatProvides($rootRequiredPackageName); $nameMatchesRequiredPackage = true; + break; } } } - $depPackages = array_merge(...$depPackages); + if (!empty($matchesByPattern)) { + $depPackages = array_merge($depPackages, array_merge(...$matchesByPattern)); + } if (count($depPackages) == 0 && !$nameMatchesRequiredPackage && !in_array($packageName, array('nothing', 'lock', 'mirrors'))) { $this->io->writeError('Package "' . $packageName . '" listed for update is not installed. Ignoring.'); @@ -1371,7 +1370,7 @@ class Installer continue; } - if (isset($skipPackages[$requirePackage->getName()])) { + if (isset($skipPackages[$requirePackage->getName()]) && !preg_match(BasePackage::packageNameToRegexp($packageName), $requirePackage->getName())) { $this->io->writeError('Dependency "' . $requirePackage->getName() . '" is also a root requirement, but is not explicitly whitelisted. Ignoring.'); continue; } diff --git a/src/Composer/Package/BasePackage.php b/src/Composer/Package/BasePackage.php index 65ea6860f..f2f5be707 100644 --- a/src/Composer/Package/BasePackage.php +++ b/src/Composer/Package/BasePackage.php @@ -239,12 +239,13 @@ abstract class BasePackage implements PackageInterface * Build a regexp from a package name, expanding * globs as required * * @param string $whiteListedPattern + * @param bool $wrap Wrap the cleaned string by the given string * @return string */ - public static function packageNameToRegexp($whiteListedPattern) + public static function packageNameToRegexp($whiteListedPattern, $wrap = '{^%s$}i') { $cleanedWhiteListedPattern = str_replace('\\*', '.*', preg_quote($whiteListedPattern)); - return "{^" . $cleanedWhiteListedPattern . "$}i"; + return sprintf($wrap, $cleanedWhiteListedPattern); } } diff --git a/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-all-dependencies.test b/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-all-dependencies.test new file mode 100644 index 000000000..8ea177cad --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-all-dependencies.test @@ -0,0 +1,46 @@ +--TEST-- +Update with a package whitelist pattern and all-dependencies flag updates packages and their dependencies, even if defined as root dependency, matching the pattern +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "fixed", "version": "1.1.0" }, + { "name": "fixed", "version": "1.0.0" }, + { "name": "whitelisted-component1", "version": "1.1.0" }, + { "name": "whitelisted-component1", "version": "1.0.0" }, + { "name": "whitelisted-component2", "version": "1.1.0", "require": { "dependency": "1.*" } }, + { "name": "whitelisted-component2", "version": "1.0.0", "require": { "dependency": "1.*" } }, + { "name": "dependency", "version": "1.1.0" }, + { "name": "dependency", "version": "1.0.0" }, + { "name": "unrelated", "version": "1.1.0", "require": { "unrelated-dependency": "1.*" } }, + { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } }, + { "name": "unrelated-dependency", "version": "1.1.0" }, + { "name": "unrelated-dependency", "version": "1.0.0" } + ] + } + ], + "require": { + "fixed": "1.*", + "whitelisted-component1": "1.*", + "whitelisted-component2": "1.*", + "dependency": "1.*", + "unrelated": "1.*" + } +} +--INSTALLED-- +[ + { "name": "fixed", "version": "1.0.0" }, + { "name": "whitelisted-component1", "version": "1.0.0" }, + { "name": "whitelisted-component2", "version": "1.0.0", "require": { "dependency": "1.0.0" } }, + { "name": "dependency", "version": "1.0.0" }, + { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } }, + { "name": "unrelated-dependency", "version": "1.0.0" } +] +--RUN-- +update whitelisted-* --with-all-dependencies +--EXPECT-- +Updating whitelisted-component1 (1.0.0) to whitelisted-component1 (1.1.0) +Updating dependency (1.0.0) to dependency (1.1.0) +Updating whitelisted-component2 (1.0.0) to whitelisted-component2 (1.1.0) diff --git a/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-dependencies.test b/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-dependencies.test new file mode 100644 index 000000000..c685f14ce --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-dependencies.test @@ -0,0 +1,49 @@ +--TEST-- +Update with a package whitelist only updates those packages and their dependencies matching the pattern but no dependencies defined as roo package +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "fixed", "version": "1.1.0" }, + { "name": "fixed", "version": "1.0.0" }, + { "name": "whitelisted-component1", "version": "1.1.0" }, + { "name": "whitelisted-component1", "version": "1.0.0" }, + { "name": "whitelisted-component2", "version": "1.1.0", "require": { "dependency": "1.*", "root-dependency": "1.*" } }, + { "name": "whitelisted-component2", "version": "1.0.0", "require": { "dependency": "1.*", "root-dependency": "1.*" } }, + { "name": "dependency", "version": "1.1.0" }, + { "name": "dependency", "version": "1.0.0" }, + { "name": "root-dependency", "version": "1.1.0" }, + { "name": "root-dependency", "version": "1.0.0" }, + { "name": "unrelated", "version": "1.1.0", "require": { "unrelated-dependency": "1.*" } }, + { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } }, + { "name": "unrelated-dependency", "version": "1.1.0" }, + { "name": "unrelated-dependency", "version": "1.0.0" } + ] + } + ], + "require": { + "fixed": "1.*", + "whitelisted-component1": "1.*", + "whitelisted-component2": "1.*", + "root-dependency": "1.*", + "unrelated": "1.*" + } +} +--INSTALLED-- +[ + { "name": "fixed", "version": "1.0.0" }, + { "name": "whitelisted-component1", "version": "1.0.0" }, + { "name": "whitelisted-component2", "version": "1.0.0", "require": { "dependency": "1.0.0" } }, + { "name": "root-dependency", "version": "1.0.0" }, + { "name": "dependency", "version": "1.0.0" }, + { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } }, + { "name": "unrelated-dependency", "version": "1.0.0" } +] +--RUN-- +update whitelisted-* --with-dependencies +--EXPECT-- +Updating whitelisted-component1 (1.0.0) to whitelisted-component1 (1.1.0) +Updating dependency (1.0.0) to dependency (1.1.0) +Updating whitelisted-component2 (1.0.0) to whitelisted-component2 (1.1.0) diff --git a/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-root-dependencies.test b/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-root-dependencies.test index 360ef49f5..a24bafb91 100644 --- a/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-root-dependencies.test +++ b/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-root-dependencies.test @@ -10,8 +10,14 @@ Update with a package whitelist only updates those packages and their dependenci { "name": "fixed", "version": "1.0.0" }, { "name": "whitelisted-component1", "version": "1.1.0", "require": { "whitelisted-component2": "1.1.0" } }, { "name": "whitelisted-component1", "version": "1.0.0", "require": { "whitelisted-component2": "1.0.0" } }, - { "name": "whitelisted-component2", "version": "1.1.0", "require": { "dependency": "1.1.0" } }, + { "name": "whitelisted-component2", "version": "1.1.0", "require": { "dependency": "1.1.0", "whitelisted-component5": "1.0.0" } }, { "name": "whitelisted-component2", "version": "1.0.0", "require": { "dependency": "1.0.0" } }, + { "name": "whitelisted-component3", "version": "1.1.0", "require": { "whitelisted-component4": "1.1.0" } }, + { "name": "whitelisted-component3", "version": "1.0.0", "require": { "whitelisted-component4": "1.0.0" } }, + { "name": "whitelisted-component4", "version": "1.1.0" }, + { "name": "whitelisted-component4", "version": "1.0.0" }, + { "name": "whitelisted-component5", "version": "1.1.0" }, + { "name": "whitelisted-component5", "version": "1.0.0" }, { "name": "dependency", "version": "1.1.0" }, { "name": "dependency", "version": "1.0.0" }, { "name": "unrelated", "version": "1.1.0", "require": { "unrelated-dependency": "1.*" } }, @@ -25,6 +31,7 @@ Update with a package whitelist only updates those packages and their dependenci "fixed": "1.*", "whitelisted-component1": "1.*", "whitelisted-component2": "1.*", + "whitelisted-component3": "1.0.0", "unrelated": "1.*" } } @@ -33,6 +40,9 @@ Update with a package whitelist only updates those packages and their dependenci { "name": "fixed", "version": "1.0.0" }, { "name": "whitelisted-component1", "version": "1.0.0", "require": { "whitelisted-component2": "1.0.0" } }, { "name": "whitelisted-component2", "version": "1.0.0", "require": { "dependency": "1.0.0" } }, + { "name": "whitelisted-component3", "version": "1.0.0", "require": { "whitelisted-component4": "1.0.0" } }, + { "name": "whitelisted-component4", "version": "1.0.0" }, + { "name": "whitelisted-component5", "version": "1.0.0" }, { "name": "dependency", "version": "1.0.0" }, { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } }, { "name": "unrelated-dependency", "version": "1.0.0" } diff --git a/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-without-dependencies.test b/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-without-dependencies.test new file mode 100644 index 000000000..e5551b43f --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-without-dependencies.test @@ -0,0 +1,44 @@ +--TEST-- +Update with a package whitelist only updates those packages matching the pattern +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "fixed", "version": "1.1.0" }, + { "name": "fixed", "version": "1.0.0" }, + { "name": "whitelisted-component1", "version": "1.1.0" }, + { "name": "whitelisted-component1", "version": "1.0.0" }, + { "name": "whitelisted-component2", "version": "1.1.0", "require": { "dependency": "1.*" } }, + { "name": "whitelisted-component2", "version": "1.0.0", "require": { "dependency": "1.*" } }, + { "name": "dependency", "version": "1.1.0" }, + { "name": "dependency", "version": "1.0.0" }, + { "name": "unrelated", "version": "1.1.0", "require": { "unrelated-dependency": "1.*" } }, + { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } }, + { "name": "unrelated-dependency", "version": "1.1.0" }, + { "name": "unrelated-dependency", "version": "1.0.0" } + ] + } + ], + "require": { + "fixed": "1.*", + "whitelisted-component1": "1.*", + "whitelisted-component2": "1.*", + "unrelated": "1.*" + } +} +--INSTALLED-- +[ + { "name": "fixed", "version": "1.0.0" }, + { "name": "whitelisted-component1", "version": "1.0.0" }, + { "name": "whitelisted-component2", "version": "1.0.0", "require": { "dependency": "1.0.0" } }, + { "name": "dependency", "version": "1.0.0" }, + { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } }, + { "name": "unrelated-dependency", "version": "1.0.0" } +] +--RUN-- +update whitelisted-* +--EXPECT-- +Updating whitelisted-component1 (1.0.0) to whitelisted-component1 (1.1.0) +Updating whitelisted-component2 (1.0.0) to whitelisted-component2 (1.1.0) From 82ecf95a3cf6e020a97cbbf1206771925c2cde35 Mon Sep 17 00:00:00 2001 From: Sascha Egerer Date: Mon, 17 Dec 2018 15:21:03 +0100 Subject: [PATCH 14/17] Add PHP 5.3 compatibility --- src/Composer/Installer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 88b4ea1d5..b4885e76c 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -1319,7 +1319,7 @@ class Installer $packageQueue = new \SplQueue; $depPackages = $pool->whatProvides($packageName); - $matchesByPattern = []; + $matchesByPattern = array(); // check if the name is a glob pattern that did not match directly if (empty($depPackages)) { $whitelistPatternSearchRegexp = BasePackage::packageNameToRegexp($packageName, '^%s$'); @@ -1336,7 +1336,7 @@ class Installer } if (!empty($matchesByPattern)) { - $depPackages = array_merge($depPackages, array_merge(...$matchesByPattern)); + $depPackages = array_merge($depPackages, call_user_func_array('array_merge', $matchesByPattern)); } if (count($depPackages) == 0 && !$nameMatchesRequiredPackage && !in_array($packageName, array('nothing', 'lock', 'mirrors'))) { From 1f97ffdcd700df530376f7a5e013c9a78ee0d93b Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 28 Jan 2019 17:54:32 +0100 Subject: [PATCH 15/17] Add some docs --- src/Composer/Installer.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index b4885e76c..f38086611 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -1322,10 +1322,13 @@ class Installer $matchesByPattern = array(); // check if the name is a glob pattern that did not match directly if (empty($depPackages)) { + // add any installed package matching the whitelisted name/pattern $whitelistPatternSearchRegexp = BasePackage::packageNameToRegexp($packageName, '^%s$'); foreach ($localOrLockRepo->search($whitelistPatternSearchRegexp) as $installedPackage) { $matchesByPattern[] = $pool->whatProvides($installedPackage['name']); } + + // add root requirements which match the whitelisted name/pattern $whitelistPatternRegexp = BasePackage::packageNameToRegexp($packageName); foreach ($rootRequiredPackageNames as $rootRequiredPackageName) { if (preg_match($whitelistPatternRegexp, $rootRequiredPackageName)) { From 4765a8f21b38b7ee65968569a00fa96d00f9d2f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20K=C3=A4fer?= Date: Fri, 21 Dec 2018 21:57:19 +0100 Subject: [PATCH 16/17] MB to MiB I did not study computer science, so correct me if I'm wrong. But I think you are calculating mebibyte (MiB) not megabyte (MB). Megabyte would be: ... round($valueInByte / 1000 / 1000, 2).'MB ... Or is there some specific standard you follow? According to https://en.wikipedia.org/wiki/Binary_prefix both calculations (yours and mine) are correct in a way but I find yours to be not completely clear. --- src/Composer/Console/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index e6ff7da9d..ccf83c943 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -262,7 +262,7 @@ class Application extends BaseApplication } if (isset($startTime)) { - $io->writeError('Memory usage: '.round(memory_get_usage() / 1024 / 1024, 2).'MB (peak: '.round(memory_get_peak_usage() / 1024 / 1024, 2).'MB), time: '.round(microtime(true) - $startTime, 2).'s'); + $io->writeError('Memory usage: '.round(memory_get_usage() / 1024 / 1024, 2).'MiB (peak: '.round(memory_get_peak_usage() / 1024 / 1024, 2).'MiB), time: '.round(microtime(true) - $startTime, 2).'s'); } restore_error_handler(); From e0c44f2a25dfca01394dd497ee302ce7fbe1d595 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20K=C3=A4fer?= Date: Fri, 28 Dec 2018 15:07:21 +0100 Subject: [PATCH 17/17] Another MB to MiB --- src/Composer/IO/ConsoleIO.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/IO/ConsoleIO.php b/src/Composer/IO/ConsoleIO.php index c0f235659..8b29177d5 100644 --- a/src/Composer/IO/ConsoleIO.php +++ b/src/Composer/IO/ConsoleIO.php @@ -153,7 +153,7 @@ class ConsoleIO extends BaseIO $memoryUsage = memory_get_usage() / 1024 / 1024; $timeSpent = microtime(true) - $this->startTime; $messages = array_map(function ($message) use ($memoryUsage, $timeSpent) { - return sprintf('[%.1fMB/%.2fs] %s', $memoryUsage, $timeSpent, $message); + return sprintf('[%.1fMiB/%.2fs] %s', $memoryUsage, $timeSpent, $message); }, (array) $messages); }