From caa5545478603e18d639143abf9eddf62cd9b929 Mon Sep 17 00:00:00 2001 From: Thomas Lamy Date: Fri, 5 Jun 2020 16:48:10 +0200 Subject: [PATCH 01/59] #8945 Add "--no-dev" to outdated/show commands --- src/Composer/Command/OutdatedCommand.php | 4 ++ src/Composer/Command/ShowCommand.php | 62 +++++++++++++++++++++++- 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/Composer/Command/OutdatedCommand.php b/src/Composer/Command/OutdatedCommand.php index 599087246..b08c3cb46 100644 --- a/src/Composer/Command/OutdatedCommand.php +++ b/src/Composer/Command/OutdatedCommand.php @@ -37,6 +37,7 @@ class OutdatedCommand extends ShowCommand new InputOption('minor-only', 'm', InputOption::VALUE_NONE, 'Show only packages that have minor SemVer-compatible updates. Use with the --outdated option.'), new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the output: text or json', 'text'), new InputOption('ignore', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Ignore specified package(s). Use it with the --outdated option if you don\'t want to be informed about new versions of some packages.'), + new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables search in require-dev packages.'), )) ->setHelp( <<getOption('minor-only')) { $args['--minor-only'] = true; } + if ($input->getOption('no-dev')) { + $args['--no-dev'] = true; + } $args['--format'] = $input->getOption('format'); $args['--ignore'] = $input->getOption('ignore'); diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index b928765f9..02b709c2d 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -81,6 +81,7 @@ class ShowCommand extends BaseCommand new InputOption('direct', 'D', InputOption::VALUE_NONE, 'Shows only packages that are directly required by the root package'), new InputOption('strict', null, InputOption::VALUE_NONE, 'Return a non-zero exit code when there are outdated packages'), new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the output: text or json', 'text'), + new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables search in require-dev packages.'), )) ->setHelp( <<getRepositoryManager()->getRepositories())); } else { $repos = $installedRepo = new InstalledRepository(array($this->getComposer()->getRepositoryManager()->getLocalRepository())); + $root = $composer->getPackage(); + $repo = $composer->getRepositoryManager()->getLocalRepository(); + + if ($input->getOption('no-dev')) { + $packages = $this->filterRequiredPackages($repo, $root); + } else { + $packages = $this->appendPackages($repo->getPackages(), array()); + } + $packageNames = array_keys($packages); $rootPkg = $this->getComposer()->getPackage(); if (!$installedRepo->getPackages() && ($rootPkg->getRequires() || $rootPkg->getDevRequires())) { $io->writeError('No dependencies installed. Try running composer install or update.'); @@ -348,7 +358,13 @@ EOT } } } else { - foreach ($repo->getPackages() as $package) { + $root = $composer->getPackage(); + if ($input->getOption('no-dev')) { + $packageList = $this->filterRequiredPackages($repo, $root); + } else { + $packageList = $this->appendPackages($repo->getPackages(), array()); + } + foreach ($packageList as $package) { if (!isset($packages[$type][$package->getName()]) || !is_object($packages[$type][$package->getName()]) || version_compare($packages[$type][$package->getName()]->getVersion(), $package->getVersion(), '<') @@ -1223,4 +1239,48 @@ EOT return $this->repositorySet; } + /** + * Find package requires and child requires + * + * @param RepositoryInterface $repo + * @param PackageInterface $package + * @param array $bucket + * @return array + */ + private function filterRequiredPackages(RepositoryInterface $repo, PackageInterface $package, $bucket = array()) + { + $requires = array_keys($package->getRequires()); + + $packageListNames = array_keys($bucket); + $packages = array_filter( + $repo->getPackages(), + function ($package) use ($requires, $packageListNames) { + return in_array($package->getName(), $requires, true) && ! in_array($package->getName(), $packageListNames, true); + } + ); + + $bucket = $this->appendPackages($packages, $bucket); + + foreach ($packages as $requiredPackage) { + $bucket = $this->filterRequiredPackages($repo, $requiredPackage, $bucket); + } + + return $bucket; + } + + /** + * Adds packages to the package list + * + * @param array $packages the list of packages to add + * @param array $bucket the list to add packages to + * @return array + */ + public function appendPackages(array $packages, array $bucket) + { + foreach ($packages as $package) { + $bucket[$package->getName()] = $package; + } + + return $bucket; + } } From 23cae5939c635729fbc4b19ef208e2c2b37485fd Mon Sep 17 00:00:00 2001 From: Thomas Lamy Date: Wed, 17 Jun 2020 13:15:11 +0200 Subject: [PATCH 02/59] #8945 Add "--no-dev" to outdated/show commands docs --- doc/03-cli.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/03-cli.md b/doc/03-cli.md index edfaaa9ed..6737ab605 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -408,6 +408,7 @@ php composer.phar show monolog/monolog 1.0.2 * **--tree (-t):** List your dependencies as a tree. If you pass a package name it will show the dependency tree for that package. * **--latest (-l):** List all installed packages including their latest version. * **--outdated (-o):** Implies --latest, but this lists *only* packages that have a newer version available. +* **--no-dev:** Filters dev dependencies from the package list. * **--minor-only (-m):** Use with --latest. Only shows packages that have minor SemVer-compatible updates. * **--direct (-D):** Restricts the list of packages to your direct dependencies. * **--strict:** Return a non-zero exit code when there are outdated packages. @@ -433,6 +434,7 @@ The color coding is as such: * **--strict:** Returns non-zero exit code if any package is outdated. * **--minor-only (-m):** Only shows packages that have minor SemVer-compatible updates. * **--format (-f):** Lets you pick between text (default) or json output format. +* **--no-dev:** Do not show outdated dev dependencies. ## browse / home From 5ff142329831c95c968731944edd4389795c829f Mon Sep 17 00:00:00 2001 From: Stephan Vock Date: Fri, 19 Jun 2020 10:24:40 +0100 Subject: [PATCH 03/59] GitDriver: use authentication for supports check --- src/Composer/Repository/Vcs/GitDriver.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index cc6e3edb4..1900c6321 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -219,8 +219,17 @@ class GitDriver extends VcsDriver return false; } - $process = new ProcessExecutor($io); + $gitUtil = new GitUtil($io, $config, new ProcessExecutor($io), new Filesystem()); + GitUtil::cleanEnv(); - return $process->execute('git ls-remote --heads ' . ProcessExecutor::escape($url), $output) === 0; + try { + $gitUtil->runCommand(function ($url) { + return 'git ls-remote --heads ' . ProcessExecutor::escape($url); + }, $url, null); + } catch (\RuntimeException $e) { + return false; + } + + return true; } } From e3c75b3e872cbcc69480d0c88387d63135f57985 Mon Sep 17 00:00:00 2001 From: Ayesh Karunaratne Date: Tue, 23 Jun 2020 04:10:26 +0700 Subject: [PATCH 04/59] Replace cascading str_replace() calls with strtr() Replaces cascading str_replace() calls with a single strtr() call that makes it mode readable and provides a micro performance improvement. Some instances of cascading str_replace() calls were not refactored due to the potential recurring replacement patterns. --- src/Composer/Autoload/AutoloadGenerator.php | 3 +-- src/Composer/Plugin/PluginManager.php | 8 +++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 07cb9aaa8..7c1729eab 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -1064,8 +1064,7 @@ INITIALIZER; $path = preg_replace('{/+}', '/', preg_quote(trim(strtr($path, '\\', '/'), '/'))); // add support for wildcards * and ** - $path = str_replace('\\*\\*', '.+?', $path); - $path = str_replace('\\*', '[^/]+?', $path); + $path = strtr($path, array('\\*\\*' => '.+?', '\\*' => '[^/]+?')); // add support for up-level relative paths $updir = null; diff --git a/src/Composer/Plugin/PluginManager.php b/src/Composer/Plugin/PluginManager.php index c910098fe..a1b2f1409 100644 --- a/src/Composer/Plugin/PluginManager.php +++ b/src/Composer/Plugin/PluginManager.php @@ -196,9 +196,11 @@ class PluginManager $className = substr($class, $separatorPos + 1); } $code = preg_replace('{^((?:final\s+)?(?:\s*))class\s+('.preg_quote($className).')}mi', '$1class $2_composer_tmp'.self::$classCounter, $code, 1); - $code = str_replace('__FILE__', var_export($path, true), $code); - $code = str_replace('__DIR__', var_export(dirname($path), true), $code); - $code = str_replace('__CLASS__', var_export($class, true), $code); + $code = strtr($code, array( + '__FILE__' => var_export($path, true), + '__DIR__' => var_export(dirname($path), true), + '__CLASS__' => var_export($class, true), + )); $code = preg_replace('/^\s*<\?(php)?/i', '', $code, 1); eval($code); $class .= '_composer_tmp'.self::$classCounter; From 52332d994e2944c72f9d49296932d98b5cbfe68c Mon Sep 17 00:00:00 2001 From: Stephan Vock Date: Fri, 19 Jun 2020 10:24:40 +0100 Subject: [PATCH 05/59] GitDriver: use authentication for supports check --- src/Composer/Repository/Vcs/GitDriver.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index cc6e3edb4..8c6902298 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -219,8 +219,17 @@ class GitDriver extends VcsDriver return false; } - $process = new ProcessExecutor($io); + $gitUtil = new GitUtil($io, $config, new ProcessExecutor($io), new Filesystem()); + GitUtil::cleanEnv(); - return $process->execute('git ls-remote --heads ' . ProcessExecutor::escape($url), $output) === 0; + try { + $gitUtil->runCommand(function ($url) { + return 'git ls-remote --heads ' . ProcessExecutor::escape($url); + }, $url, sys_get_temp_dir()); + } catch (\RuntimeException $e) { + return false; + } + + return true; } } From 668655c21a99a5016d6a959d0df4bb5e86fe4e9f Mon Sep 17 00:00:00 2001 From: Jonas Drieghe Date: Tue, 23 Jun 2020 19:43:27 +0200 Subject: [PATCH 06/59] Rename variable for clarity --- src/Composer/Command/LicensesCommand.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Composer/Command/LicensesCommand.php b/src/Composer/Command/LicensesCommand.php index c9a099f26..9140aa48d 100644 --- a/src/Composer/Command/LicensesCommand.php +++ b/src/Composer/Command/LicensesCommand.php @@ -96,9 +96,9 @@ EOT break; case 'json': - $dependencies = array(); + $usedLicenses = array(); foreach ($packages as $package) { - $dependencies[$package->getPrettyName()] = array( + $usedLicenses[$package->getPrettyName()] = array( 'version' => $package->getFullPrettyVersion(), 'license' => $package->getLicense(), ); @@ -108,23 +108,23 @@ EOT 'name' => $root->getPrettyName(), 'version' => $root->getFullPrettyVersion(), 'license' => $root->getLicense(), - 'dependencies' => $dependencies, + 'dependencies' => $usedLicenses, ))); break; case 'summary': - $dependencies = array(); + $usedLicenses = array(); foreach ($packages as $package) { $license = $package->getLicense(); $licenseName = $license[0]; - if (!isset($dependencies[$licenseName])) { - $dependencies[$licenseName] = 0; + if (!isset($usedLicenses[$licenseName])) { + $usedLicenses[$licenseName] = 0; } - $dependencies[$licenseName]++; + $usedLicenses[$licenseName]++; } $rows = array(); - foreach ($dependencies as $usedLicense => $numberOfDependencies) { + foreach ($usedLicenses as $usedLicense => $numberOfDependencies) { $rows[] = array($usedLicense, $numberOfDependencies); } From ec6e05d55fd783757baa3f33cb904e10d861632b Mon Sep 17 00:00:00 2001 From: Jonas Drieghe Date: Tue, 23 Jun 2020 19:44:12 +0200 Subject: [PATCH 07/59] Sort licenses so that the most used license will appear first --- src/Composer/Command/LicensesCommand.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Composer/Command/LicensesCommand.php b/src/Composer/Command/LicensesCommand.php index 9140aa48d..ce1e3a390 100644 --- a/src/Composer/Command/LicensesCommand.php +++ b/src/Composer/Command/LicensesCommand.php @@ -123,6 +123,9 @@ EOT $usedLicenses[$licenseName]++; } + // Sort licenses so that the most used license will appear first + arsort($dependencies, SORT_NUMERIC); + $rows = array(); foreach ($usedLicenses as $usedLicense => $numberOfDependencies) { $rows[] = array($usedLicense, $numberOfDependencies); From 73a721c7e5fb959b507790e8a08611e067f49e3d Mon Sep 17 00:00:00 2001 From: Jonas Drieghe Date: Tue, 23 Jun 2020 19:45:26 +0200 Subject: [PATCH 08/59] Add the missing summary format to command-line help text --- src/Composer/Command/LicensesCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/LicensesCommand.php b/src/Composer/Command/LicensesCommand.php index ce1e3a390..2147e3aef 100644 --- a/src/Composer/Command/LicensesCommand.php +++ b/src/Composer/Command/LicensesCommand.php @@ -34,7 +34,7 @@ class LicensesCommand extends BaseCommand ->setName('licenses') ->setDescription('Shows information about licenses of dependencies.') ->setDefinition(array( - new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the output: text or json', 'text'), + new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the output: text, json or summary', 'text'), new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables search in require-dev packages.'), )) ->setHelp( From d5d956df4d9d8356d8192972f3a33399e704a5a3 Mon Sep 17 00:00:00 2001 From: Jonas Drieghe Date: Tue, 23 Jun 2020 19:50:27 +0200 Subject: [PATCH 09/59] Use correct variable name --- src/Composer/Command/LicensesCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/LicensesCommand.php b/src/Composer/Command/LicensesCommand.php index 2147e3aef..dd0de2d8a 100644 --- a/src/Composer/Command/LicensesCommand.php +++ b/src/Composer/Command/LicensesCommand.php @@ -124,7 +124,7 @@ EOT } // Sort licenses so that the most used license will appear first - arsort($dependencies, SORT_NUMERIC); + arsort($usedLicenses, SORT_NUMERIC); $rows = array(); foreach ($usedLicenses as $usedLicense => $numberOfDependencies) { From eea7564c9ec428cd82bd4068ab8fcddc5d938779 Mon Sep 17 00:00:00 2001 From: Jonas Drieghe Date: Wed, 24 Jun 2020 07:28:22 +0200 Subject: [PATCH 10/59] Revert accidental rename of $dependencies variable in unrelated code branch --- src/Composer/Command/LicensesCommand.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Composer/Command/LicensesCommand.php b/src/Composer/Command/LicensesCommand.php index dd0de2d8a..ec0c38acc 100644 --- a/src/Composer/Command/LicensesCommand.php +++ b/src/Composer/Command/LicensesCommand.php @@ -96,9 +96,9 @@ EOT break; case 'json': - $usedLicenses = array(); + $dependencies = array(); foreach ($packages as $package) { - $usedLicenses[$package->getPrettyName()] = array( + $dependencies[$package->getPrettyName()] = array( 'version' => $package->getFullPrettyVersion(), 'license' => $package->getLicense(), ); @@ -108,7 +108,7 @@ EOT 'name' => $root->getPrettyName(), 'version' => $root->getFullPrettyVersion(), 'license' => $root->getLicense(), - 'dependencies' => $usedLicenses, + 'dependencies' => $dependencies, ))); break; From 05eec0b02fe9257d72e785015a3c502b714b6534 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 24 Jun 2020 09:02:20 +0200 Subject: [PATCH 11/59] Prepare alpha2 changelog --- CHANGELOG.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e2f52236..399b2daff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +### [2.0.0-alpha2] 2020-06-24 + + * Added parallel installation of packages (requires OSX/Linux/WSL, and that `unzip` is present in PATH) + * Added optimization of constraints by compiling them to PHP code, which should reduce CPU time of updates + * Added handling of Ctrl-C on Windows for PHP 7.4+ + * Added better support for default branch names other than `master` + * Added --format=summary flag to `license` command + * Fixed issue in platform check when requiring ext-zend-opcache + * Fixed inline aliases issues + * Fixed git integration issue when signatures are set to be shown by default + ### [2.0.0-alpha1] 2020-06-03 * Breaking: This is a major release and while we tried to keep things compatible for most users, you might want to have a look at the [UPGRADE](UPGRADE-2.0.md) guides @@ -27,7 +38,7 @@ * Added --json and --merge flags to `config` command to allow editing complex `extra.*` values by using json as input * Added confirmation prompt when running Composer as superuser in interactive mode * Added --no-check-version to `validate` command to remove the warning in case the version is defined - * Added --ignore-platform-req (without s) to all commands supporting --ignore-platform-reqs, which accepts a package name so you can ignore only specific platform requirements + * Added --ignore-platform-req (without s) to all commands supporting --ignore-platform-reqs, which accepts a package name so you can ignore only specific platform requirements * Added support for wildcards (`*`) in classmap autoloader paths * Added support for configuring GitLab deploy tokens in addition to private tokens, see [gitlab-token](doc/06-config.md#gitlab-token) * Added support for package version guessing for require and init command to take all platform packages into account, not just php version @@ -897,6 +908,7 @@ * Initial release +[2.0.0-alpha2]: https://github.com/composer/composer/compare/2.0.0-alpha1...2.0.0-alpha2 [2.0.0-alpha1]: https://github.com/composer/composer/compare/1.10.7...2.0.0-alpha1 [1.10.7]: https://github.com/composer/composer/compare/1.10.6...1.10.7 [1.10.6]: https://github.com/composer/composer/compare/1.10.5...1.10.6 From 74d89d7110754aaea9b61c5a17d24b0ab2d113a8 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 24 Jun 2020 21:16:00 +0200 Subject: [PATCH 12/59] Get rid of default branch handling in version guesser as it is way too time intensive at every initialization --- .../Package/Loader/RootPackageLoader.php | 5 -- .../Package/Version/VersionGuesser.php | 41 +---------- .../Composer/Test/Mock/VersionGuesserMock.php | 6 -- .../Package/Loader/RootPackageLoaderTest.php | 37 ---------- .../Package/Version/VersionGuesserTest.php | 70 +------------------ 5 files changed, 4 insertions(+), 155 deletions(-) diff --git a/src/Composer/Package/Loader/RootPackageLoader.php b/src/Composer/Package/Loader/RootPackageLoader.php index 536d461ad..ac3d8f433 100644 --- a/src/Composer/Package/Loader/RootPackageLoader.php +++ b/src/Composer/Package/Loader/RootPackageLoader.php @@ -110,11 +110,6 @@ class RootPackageLoader extends ArrayLoader } } - $defaultBranch = $this->versionGuesser->getDefaultBranchName($cwd ?: getcwd()); - if ($defaultBranch && $config['version'] === 'dev-'.$defaultBranch) { - $config['default-branch'] = true; - } - $realPackage = $package = parent::load($config, $class); if ($realPackage instanceof AliasPackage) { $realPackage = $package->getAliasOf(); diff --git a/src/Composer/Package/Version/VersionGuesser.php b/src/Composer/Package/Version/VersionGuesser.php index 24bd33b75..6cf185685 100644 --- a/src/Composer/Package/Version/VersionGuesser.php +++ b/src/Composer/Package/Version/VersionGuesser.php @@ -111,43 +111,6 @@ class VersionGuesser return $versionData; } - /** - * Tries to find name of default branch from VCS info - * - * @param string $path Path to guess into - */ - public function getDefaultBranchName($path) - { - if (version_compare(GitUtil::getVersion($this->process), '2.3.0-rc0', '>=')) { - GitUtil::cleanEnv(); - $oldVal = getenv('GIT_SSH_COMMAND'); - putenv("GIT_SSH_COMMAND=ssh".(Platform::isWindows() ? '.exe' : '')." -o StrictHostKeyChecking=yes"); - $hasGitRemote = 0 === $this->process->execute('git remote show origin', $output, $path); - if ($oldVal) { - putenv("GIT_SSH_COMMAND=$oldVal"); - } else { - putenv("GIT_SSH_COMMAND"); - } - if ($hasGitRemote && preg_match('{^ HEAD branch: (.+)$}m', $output, $match)) { - return trim($match[1]); - } - } - - if (is_dir($path.'/.git')) { - return 'master'; - } - - if (is_dir($path.'/.hg')) { - return 'default'; - } - - if (is_dir($path.'/.svn')) { - return 'trunk'; - } - - return null; - } - private function guessGitVersion(array $packageConfig, $path) { GitUtil::cleanEnv(); @@ -292,11 +255,9 @@ class VersionGuesser return array('version' => $version, 'pretty_version' => $prettyVersion); } - $defaultBranch = $this->getDefaultBranchName($path); - foreach ($branches as $candidate) { // do not compare against itself or other feature branches - if ($candidate === $branch || !preg_match('{^(' . $nonFeatureBranches . ($defaultBranch ? '|'.preg_quote($defaultBranch) : '').'|master|main|latest|next|current|support|tip|trunk|default|develop|\d+\..+)$}', $candidate, $match)) { + if ($candidate === $branch || !preg_match('{^(' . $nonFeatureBranches . '|master|main|latest|next|current|support|tip|trunk|default|develop|\d+\..+)$}', $candidate, $match)) { continue; } diff --git a/tests/Composer/Test/Mock/VersionGuesserMock.php b/tests/Composer/Test/Mock/VersionGuesserMock.php index bcfa1d19d..d2fe1236c 100644 --- a/tests/Composer/Test/Mock/VersionGuesserMock.php +++ b/tests/Composer/Test/Mock/VersionGuesserMock.php @@ -24,10 +24,4 @@ class VersionGuesserMock extends VersionGuesser { return null; } - - public function getDefaultBranchName($path) - { - return null; - } - } diff --git a/tests/Composer/Test/Package/Loader/RootPackageLoaderTest.php b/tests/Composer/Test/Package/Loader/RootPackageLoaderTest.php index e5aebd1d0..25d39270c 100644 --- a/tests/Composer/Test/Package/Loader/RootPackageLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/RootPackageLoaderTest.php @@ -105,8 +105,6 @@ class RootPackageLoaderTest extends TestCase 'pretty_version' => '3.0-dev', 'commit' => 'aabbccddee', )); - $versionGuesser->getDefaultBranchName(Argument::cetera()) - ->willReturn('main'); $config = new Config; $config->merge(array('repositories' => array('packagist' => false))); $loader = new RootPackageLoader($manager->reveal(), $config, null, $versionGuesser->reveal()); @@ -115,28 +113,6 @@ class RootPackageLoaderTest extends TestCase $this->assertEquals('3.0-dev', $package->getPrettyVersion()); } - public function testDefaultBranchIsSetForRootPackageInDefaultBranch() - { - // see #6845 - $manager = $this->prophesize('\\Composer\\Repository\\RepositoryManager'); - $versionGuesser = $this->prophesize('\\Composer\\Package\\Version\\VersionGuesser'); - $versionGuesser->guessVersion(Argument::cetera()) - ->willReturn(array( - 'name' => 'A', - 'version' => 'dev-main', - 'pretty_version' => 'dev-main', - 'commit' => 'aabbccddee', - )); - $versionGuesser->getDefaultBranchName(Argument::cetera()) - ->willReturn('main'); - $config = new Config; - $config->merge(array('repositories' => array('packagist' => false))); - $loader = new RootPackageLoader($manager->reveal(), $config, null, $versionGuesser->reveal()); - $package = $loader->load(array()); - - $this->assertTrue($package->isDefaultBranch()); - } - public function testFeatureBranchPrettyVersion() { if (!function_exists('proc_open')) { @@ -171,17 +147,6 @@ class RootPackageLoaderTest extends TestCase $executor ->expects($this->at(1)) ->method('execute') - ->willReturnCallback(function ($command, &$output) use ($self) { - $self->assertEquals('git remote show origin', $command); - $output = " HEAD branch: master"; - - return 0; - }) - ; - - $executor - ->expects($this->at(2)) - ->method('execute') ->willReturnCallback(function ($command, &$output) use ($self) { $self->assertEquals('git rev-list master..latest-production', $command); $output = ""; @@ -195,8 +160,6 @@ class RootPackageLoaderTest extends TestCase $loader = new RootPackageLoader($manager, $config, null, new VersionGuesser($config, $executor, new VersionParser())); $package = $loader->load(array('require' => array('foo/bar' => 'self.version'))); - $this->assertEquals("9999999-dev", $package->getPrettyVersion()); - $package = $package->getAliasOf(); $this->assertEquals("dev-master", $package->getPrettyVersion()); } diff --git a/tests/Composer/Test/Package/Version/VersionGuesserTest.php b/tests/Composer/Test/Package/Version/VersionGuesserTest.php index 0b7486afd..cedd697ce 100644 --- a/tests/Composer/Test/Package/Version/VersionGuesserTest.php +++ b/tests/Composer/Test/Package/Version/VersionGuesserTest.php @@ -135,7 +135,7 @@ class VersionGuesserTest extends TestCase $this->assertEquals($commitHash, $versionArray['commit']); } - public function testGuessVersionReadsAndRespectsDefaultBranchAsNonFeatureBranch() + public function testGuessVersionDoesNotSeeCustomDefaultBranchAsNonFeatureBranch() { $commitHash = '03a15d220da53c52eddd5f32ffca64a7b3801bea'; $anotherCommitHash = '13a15d220da53c52eddd5f32ffca64a7b3801bea'; @@ -149,6 +149,7 @@ class VersionGuesserTest extends TestCase $self = $this; + // Assumption here is that arbitrary would be the default branch $executor ->expects($this->at(0)) ->method('execute') @@ -160,37 +161,13 @@ class VersionGuesserTest extends TestCase }) ; - $executor - ->expects($this->at(1)) - ->method('execute') - ->willReturnCallback(function ($command, &$output) use ($self) { - $self->assertEquals('git remote show origin', $command); - $output = " HEAD branch: arbitrary\r\n"; - - return 0; - }) - ; - - $executor - ->expects($this->at(2)) - ->method('execute') - ->willReturnCallback(function ($command, &$output, $path) use ($self, $anotherCommitHash) { - $self->assertEquals('git rev-list arbitrary..current', $command); - $output = "$anotherCommitHash\n"; - - return 0; - }) - ; - $config = new Config; $config->merge(array('repositories' => array('packagist' => false))); $guesser = new VersionGuesser($config, $executor, new VersionParser()); $versionArray = $guesser->guessVersion(array('version' => 'self.version'), 'dummy/path'); - $this->assertEquals("dev-arbitrary", $versionArray['version']); + $this->assertEquals("dev-current", $versionArray['version']); $this->assertEquals($anotherCommitHash, $versionArray['commit']); - $this->assertEquals("dev-current", $versionArray['feature_version']); - $this->assertEquals("dev-current", $versionArray['feature_pretty_version']); } public function testGuessVersionReadsAndRespectsNonFeatureBranchesConfigurationForArbitraryNaming() @@ -221,17 +198,6 @@ class VersionGuesserTest extends TestCase $executor ->expects($this->at(1)) ->method('execute') - ->willReturnCallback(function ($command, &$output) use ($self) { - $self->assertEquals('git remote show origin', $command); - $output = " HEAD branch: foo\r\n"; - - return 0; - }) - ; - - $executor - ->expects($this->at(2)) - ->method('execute') ->willReturnCallback(function ($command, &$output, $path) use ($self, $anotherCommitHash) { $self->assertEquals('git rev-list arbitrary..current', $command); $output = "$anotherCommitHash\n"; @@ -278,16 +244,6 @@ class VersionGuesserTest extends TestCase $executor ->expects($this->at(1)) ->method('execute') - ->willReturnCallback(function ($command, &$output) use ($self) { - $self->assertEquals('git remote show origin', $command); - $output = " HEAD branch: foo\r\n"; - - return 0; - }) - ; - $executor - ->expects($this->at(2)) - ->method('execute') ->willReturnCallback(function ($command, &$output, $path) use ($self, $anotherCommitHash) { $self->assertEquals('git rev-list latest-testing..current', $command); $output = "$anotherCommitHash\n"; @@ -459,16 +415,6 @@ class VersionGuesserTest extends TestCase $executor ->expects($this->at(1)) ->method('execute') - ->willReturnCallback(function ($command, &$output) use ($self) { - $self->assertEquals('git remote show origin', $command); - $output = " HEAD branch: foo\r\n"; - - return 0; - }) - ; - $executor - ->expects($this->at(2)) - ->method('execute') ->willReturnCallback(function ($command, &$output) use ($self) { $self->assertEquals('git describe --exact-match --tags', $command); $output = "v2.0.5-alpha2"; @@ -509,16 +455,6 @@ class VersionGuesserTest extends TestCase $executor ->expects($this->at(1)) ->method('execute') - ->willReturnCallback(function ($command, &$output) use ($self) { - $self->assertEquals('git remote show origin', $command); - $output = " HEAD branch: foo\r\n"; - - return 0; - }) - ; - $executor - ->expects($this->at(2)) - ->method('execute') ->willReturnCallback(function ($command, &$output) use ($self) { $self->assertEquals('git describe --exact-match --tags', $command); $output = '1.0.0'; From 95a176c87c29b0e44c05b0a685af9d217cabb053 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 24 Jun 2020 21:23:12 +0200 Subject: [PATCH 13/59] Prepare 1.10.8 release --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 962a7759b..f80d676df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ +### [1.10.8] 2020-06-24 + + * Fixed compatibility issue with git being configured to show signatures by default + * Fixed discarding of local changes when updating packages to include untracked files + * Several minor fixes + ### [1.10.7] 2020-06-03 - * Fix PHP 8 deprecations + * Fixed PHP 8 deprecations * Fixed detection of pcntl_signal being in disabled_functions when pcntl_async_signal is allowed ### [1.10.6] 2020-05-06 @@ -860,6 +866,7 @@ * Initial release +[1.10.8]: https://github.com/composer/composer/compare/1.10.7...1.10.8 [1.10.7]: https://github.com/composer/composer/compare/1.10.6...1.10.7 [1.10.6]: https://github.com/composer/composer/compare/1.10.5...1.10.6 [1.10.5]: https://github.com/composer/composer/compare/1.10.4...1.10.5 From 05dacbdabb64083fea7ba4880bf0ea23b0a0c837 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 25 Jun 2020 08:56:14 +0200 Subject: [PATCH 14/59] Rename DEV_MASTER_ALIAS to DEFAULT_BRANCH_ALIAS --- src/Composer/Command/CreateProjectCommand.php | 4 ++-- src/Composer/DependencyResolver/Problem.php | 10 +++++++--- src/Composer/DependencyResolver/Rule.php | 18 +++++++++--------- src/Composer/Package/AliasPackage.php | 2 +- src/Composer/Package/Loader/ArrayLoader.php | 6 +++--- .../Package/Version/VersionGuesser.php | 2 +- src/Composer/Package/Version/VersionParser.php | 12 ++++++++---- src/Composer/Repository/ComposerRepository.php | 8 ++++---- src/Composer/Repository/VcsRepository.php | 2 +- .../installer/update-mirrors-changes-url.test | 4 ++-- .../Test/Package/Version/VersionParserTest.php | 6 +++--- 11 files changed, 41 insertions(+), 33 deletions(-) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 27d9919d7..c0470e096 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -388,8 +388,8 @@ EOT } } - // avoid displaying 9999999-dev as version if dev-master was selected - if ($package instanceof AliasPackage && $package->getPrettyVersion() === VersionParser::DEV_MASTER_ALIAS) { + // avoid displaying 9999999-dev as version if default-branch was selected + if ($package instanceof AliasPackage && $package->getPrettyVersion() === VersionParser::DEFAULT_BRANCH_ALIAS) { $package = $package->getAliasOf(); } diff --git a/src/Composer/DependencyResolver/Problem.php b/src/Composer/DependencyResolver/Problem.php index eed4c5043..6d94b068b 100644 --- a/src/Composer/DependencyResolver/Problem.php +++ b/src/Composer/DependencyResolver/Problem.php @@ -329,14 +329,18 @@ class Problem public static function getPackageList(array $packages, $isVerbose) { $prepared = array(); + $hasDefaultBranch = array(); foreach ($packages as $package) { $prepared[$package->getName()]['name'] = $package->getPrettyName(); $prepared[$package->getName()]['versions'][$package->getVersion()] = $package->getPrettyVersion().($package instanceof AliasPackage ? ' (alias of '.$package->getAliasOf()->getPrettyVersion().')' : ''); + if ($package->isDefaultBranch()) { + $hasDefaultBranch[$package->getName()] = true; + } } foreach ($prepared as $name => $package) { - // remove the implicit dev-master alias to avoid cruft in the display - if (isset($package['versions'][VersionParser::DEV_MASTER_ALIAS]) && isset($package['versions']['dev-master'])) { - unset($package['versions'][VersionParser::DEV_MASTER_ALIAS]); + // remove the implicit default branch alias to avoid cruft in the display + if (isset($package['versions'][VersionParser::DEFAULT_BRANCH_ALIAS]) && isset($hasDefaultBranch[$name])) { + unset($package['versions'][VersionParser::DEFAULT_BRANCH_ALIAS]); } uksort($package['versions'], 'version_compare'); diff --git a/src/Composer/DependencyResolver/Rule.php b/src/Composer/DependencyResolver/Rule.php index 4cb36258c..ee71929e5 100644 --- a/src/Composer/DependencyResolver/Rule.php +++ b/src/Composer/DependencyResolver/Rule.php @@ -183,7 +183,7 @@ abstract class Rule return 'Root composer.json requires '.$packageName.($constraint ? ' '.$constraint->getPrettyString() : '').' -> satisfiable by '.$this->formatPackagesUnique($pool, $packages, $isVerbose).'.'; case self::RULE_FIXED: - $package = $this->deduplicateMasterAlias($this->reasonData['package']); + $package = $this->deduplicateDefaultBranchAlias($this->reasonData['package']); if ($this->reasonData['lockable']) { return $package->getPrettyName().' is locked to version '.$package->getPrettyVersion().' and an update of this package was not requested.'; } @@ -191,14 +191,14 @@ abstract class Rule return $package->getPrettyName().' is present at version '.$package->getPrettyVersion() . ' and cannot be modified by Composer'; case self::RULE_PACKAGE_CONFLICT: - $package1 = $this->deduplicateMasterAlias($pool->literalToPackage($literals[0])); - $package2 = $this->deduplicateMasterAlias($pool->literalToPackage($literals[1])); + $package1 = $this->deduplicateDefaultBranchAlias($pool->literalToPackage($literals[0])); + $package2 = $this->deduplicateDefaultBranchAlias($pool->literalToPackage($literals[1])); return $package2->getPrettyString().' conflicts with '.$package1->getPrettyString().'.'; case self::RULE_PACKAGE_REQUIRES: $sourceLiteral = array_shift($literals); - $sourcePackage = $this->deduplicateMasterAlias($pool->literalToPackage($sourceLiteral)); + $sourcePackage = $this->deduplicateDefaultBranchAlias($pool->literalToPackage($sourceLiteral)); $requires = array(); foreach ($literals as $literal) { @@ -281,7 +281,7 @@ abstract class Rule $group = $literal > 0 ? 'install' : 'don\'t install'; } - $groups[$group][] = $this->deduplicateMasterAlias($package); + $groups[$group][] = $this->deduplicateDefaultBranchAlias($package); } $ruleTexts = array(); foreach ($groups as $group => $packages) { @@ -295,10 +295,10 @@ abstract class Rule case self::RULE_PACKAGE_ALIAS: $aliasPackage = $pool->literalToPackage($literals[0]); // avoid returning content like "9999999-dev is an alias of dev-master" as it is useless - if ($aliasPackage->getVersion() === VersionParser::DEV_MASTER_ALIAS) { + if ($aliasPackage->getVersion() === VersionParser::DEFAULT_BRANCH_ALIAS) { return ''; } - $package = $this->deduplicateMasterAlias($pool->literalToPackage($literals[1])); + $package = $this->deduplicateDefaultBranchAlias($pool->literalToPackage($literals[1])); return $aliasPackage->getPrettyString() .' is an alias of '.$package->getPrettyString().' and thus requires it to be installed too.'; default: @@ -342,9 +342,9 @@ abstract class Rule return $names; } - private function deduplicateMasterAlias(PackageInterface $package) + private function deduplicateDefaultBranchAlias(PackageInterface $package) { - if ($package instanceof AliasPackage && $package->getPrettyVersion() === VersionParser::DEV_MASTER_ALIAS) { + if ($package instanceof AliasPackage && $package->getPrettyVersion() === VersionParser::DEFAULT_BRANCH_ALIAS) { $package = $package->getAliasOf(); } diff --git a/src/Composer/Package/AliasPackage.php b/src/Composer/Package/AliasPackage.php index 7d843c6ba..a3bc117f0 100644 --- a/src/Composer/Package/AliasPackage.php +++ b/src/Composer/Package/AliasPackage.php @@ -176,7 +176,7 @@ class AliasPackage extends BasePackage implements CompletePackageInterface { // for self.version requirements, we use the original package's branch name instead, to avoid leaking the magic dev-master-alias to users $prettyVersion = $this->prettyVersion; - if ($prettyVersion === VersionParser::DEV_MASTER_ALIAS) { + if ($prettyVersion === VersionParser::DEFAULT_BRANCH_ALIAS) { $prettyVersion = $this->aliasOf->getPrettyVersion(); } diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index b2330e6e2..3d7b3b614 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -90,8 +90,8 @@ class ArrayLoader implements LoaderInterface if (isset($config['version_normalized'])) { $version = $config['version_normalized']; - // handling of existing repos which need to remain composer v1 compatible, in case the version_normalized contained VersionParser::DEV_MASTER_ALIAS, we renormalize it - if ($version === VersionParser::DEV_MASTER_ALIAS) { + // handling of existing repos which need to remain composer v1 compatible, in case the version_normalized contained VersionParser::DEFAULT_BRANCH_ALIAS, we renormalize it + if ($version === VersionParser::DEFAULT_BRANCH_ALIAS) { $version = $this->versionParser->normalize($config['version']); } } else { @@ -369,7 +369,7 @@ class ArrayLoader implements LoaderInterface } if (isset($config['default-branch']) && $config['default-branch'] === true) { - return VersionParser::DEV_MASTER_ALIAS; + return VersionParser::DEFAULT_BRANCH_ALIAS; } } } diff --git a/src/Composer/Package/Version/VersionGuesser.php b/src/Composer/Package/Version/VersionGuesser.php index 6cf185685..1c2f99247 100644 --- a/src/Composer/Package/Version/VersionGuesser.php +++ b/src/Composer/Package/Version/VersionGuesser.php @@ -210,7 +210,7 @@ class VersionGuesser $version = $this->versionParser->normalizeBranch($branch); $isFeatureBranch = 0 === strpos($version, 'dev-'); - if (VersionParser::DEV_MASTER_ALIAS === $version) { + if (VersionParser::DEFAULT_BRANCH_ALIAS === $version) { return array('version' => $version, 'commit' => null, 'pretty_version' => 'dev-'.$branch); } diff --git a/src/Composer/Package/Version/VersionParser.php b/src/Composer/Package/Version/VersionParser.php index 29bdab37b..ab75126c3 100644 --- a/src/Composer/Package/Version/VersionParser.php +++ b/src/Composer/Package/Version/VersionParser.php @@ -18,8 +18,8 @@ use Composer\Semver\Semver; class VersionParser extends SemverVersionParser { - const DEV_MASTER_ALIAS = '9999999-dev'; - + const DEFAULT_BRANCH_ALIAS = '9999999-dev'; + private static $constraints = array(); /** @@ -72,8 +72,12 @@ class VersionParser extends SemverVersionParser */ public static function isUpgrade($normalizedFrom, $normalizedTo) { - $normalizedFrom = str_replace(array('dev-master', 'dev-trunk', 'dev-default'), VersionParser::DEV_MASTER_ALIAS, $normalizedFrom); - $normalizedTo = str_replace(array('dev-master', 'dev-trunk', 'dev-default'), VersionParser::DEV_MASTER_ALIAS, $normalizedTo); + if ($normalizedFrom === $normalizedTo) { + return true; + } + + $normalizedFrom = str_replace(array('dev-master', 'dev-trunk', 'dev-default'), VersionParser::DEFAULT_BRANCH_ALIAS, $normalizedFrom); + $normalizedTo = str_replace(array('dev-master', 'dev-trunk', 'dev-default'), VersionParser::DEFAULT_BRANCH_ALIAS, $normalizedTo); if (substr($normalizedFrom, 0, 4) === 'dev-' || substr($normalizedTo, 0, 4) === 'dev-') { return true; diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index f7462c89f..74ce15e8a 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -596,8 +596,8 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito if (!isset($versionsToLoad[$version['uid']])) { if (!isset($version['version_normalized'])) { $version['version_normalized'] = $this->versionParser->normalize($version['version']); - } elseif ($version['version_normalized'] === VersionParser::DEV_MASTER_ALIAS) { - // handling of existing repos which need to remain composer v1 compatible, in case the version_normalized contained VersionParser::DEV_MASTER_ALIAS, we renormalize it + } elseif ($version['version_normalized'] === VersionParser::DEFAULT_BRANCH_ALIAS) { + // handling of existing repos which need to remain composer v1 compatible, in case the version_normalized contained VersionParser::DEFAULT_BRANCH_ALIAS, we renormalize it $version['version_normalized'] = $this->versionParser->normalize($version['version']); } @@ -717,8 +717,8 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito foreach ($versions as $version) { if (!isset($version['version_normalized'])) { $version['version_normalized'] = $repo->versionParser->normalize($version['version']); - } elseif ($version['version_normalized'] === VersionParser::DEV_MASTER_ALIAS) { - // handling of existing repos which need to remain composer v1 compatible, in case the version_normalized contained VersionParser::DEV_MASTER_ALIAS, we renormalize it + } elseif ($version['version_normalized'] === VersionParser::DEFAULT_BRANCH_ALIAS) { + // handling of existing repos which need to remain composer v1 compatible, in case the version_normalized contained VersionParser::DEFAULT_BRANCH_ALIAS, we renormalize it $version['version_normalized'] = $repo->versionParser->normalize($version['version']); } diff --git a/src/Composer/Repository/VcsRepository.php b/src/Composer/Repository/VcsRepository.php index f3d7fe56b..5c6c09557 100644 --- a/src/Composer/Repository/VcsRepository.php +++ b/src/Composer/Repository/VcsRepository.php @@ -306,7 +306,7 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt } // make sure branch packages have a dev flag - if ('dev-' === substr($parsedBranch, 0, 4) || VersionParser::DEV_MASTER_ALIAS === $parsedBranch) { + if ('dev-' === substr($parsedBranch, 0, 4) || VersionParser::DEFAULT_BRANCH_ALIAS === $parsedBranch) { $version = 'dev-' . $branch; } else { $prefix = substr($branch, 0, 1) === 'v' ? 'v' : ''; diff --git a/tests/Composer/Test/Fixtures/installer/update-mirrors-changes-url.test b/tests/Composer/Test/Fixtures/installer/update-mirrors-changes-url.test index 9bfca4c85..554783fc0 100644 --- a/tests/Composer/Test/Fixtures/installer/update-mirrors-changes-url.test +++ b/tests/Composer/Test/Fixtures/installer/update-mirrors-changes-url.test @@ -3,10 +3,10 @@ Update mirrors updates URLs for all packages if they have changed without updati a/a is dev and gets everything updated as it updates to a new ref b/b is a tag and gets everything updated by updating the package URL directly -c/c is a tag and not whitelisted and gets the new URL but keeps its old ref +c/c is a tag and not allowlisted for update and gets the new URL but keeps its old ref d/d is dev but with a #ref so it should get URL updated but not the reference e/e is dev and newly installed with a #ref so it should get the correct URL but with the #111 ref -e/e is dev but not whitelisted and gets the new URL but keeps its old ref +e/e is dev but not allowlisted for update and gets the new URL but keeps its old ref g/g is dev and installed in a different ref than the #ref, so it gets updated and gets the new URL but not the new ref --COMPOSER-- { diff --git a/tests/Composer/Test/Package/Version/VersionParserTest.php b/tests/Composer/Test/Package/Version/VersionParserTest.php index 6e4ab7cc3..e4c15245e 100644 --- a/tests/Composer/Test/Package/Version/VersionParserTest.php +++ b/tests/Composer/Test/Package/Version/VersionParserTest.php @@ -49,9 +49,9 @@ class VersionParserTest extends TestCase return array( array('0.9.0.0', '1.0.0.0', true), array('1.0.0.0', '0.9.0.0', false), - array('1.0.0.0', VersionParser::DEV_MASTER_ALIAS, true), - array(VersionParser::DEV_MASTER_ALIAS, VersionParser::DEV_MASTER_ALIAS, true), - array(VersionParser::DEV_MASTER_ALIAS, '1.0.0.0', false), + array('1.0.0.0', VersionParser::DEFAULT_BRANCH_ALIAS, true), + array(VersionParser::DEFAULT_BRANCH_ALIAS, VersionParser::DEFAULT_BRANCH_ALIAS, true), + array(VersionParser::DEFAULT_BRANCH_ALIAS, '1.0.0.0', false), array('1.0.0.0', 'dev-foo', true), array('dev-foo', 'dev-foo', true), array('dev-foo', '1.0.0.0', true), From 6f9b39180cd79623621b2d49ad9e683f6e15ff22 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 25 Jun 2020 08:56:46 +0200 Subject: [PATCH 15/59] Add phpdocs --- src/Composer/DependencyResolver/PoolBuilder.php | 4 ++-- src/Composer/Package/Version/StabilityFilter.php | 6 ++++++ src/Composer/Repository/RepositoryInterface.php | 7 +++++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Composer/DependencyResolver/PoolBuilder.php b/src/Composer/DependencyResolver/PoolBuilder.php index 38a5fac3c..1ccd3a80c 100644 --- a/src/Composer/DependencyResolver/PoolBuilder.php +++ b/src/Composer/DependencyResolver/PoolBuilder.php @@ -84,9 +84,9 @@ class PoolBuilder /** * @param int[] $acceptableStabilities array of stability => BasePackage::STABILITY_* value - * @psalm-param array $acceptableStabilities + * @psalm-param array $acceptableStabilities * @param int[] $stabilityFlags an array of package name => BasePackage::STABILITY_* value - * @psalm-param array $stabilityFlags + * @psalm-param array $stabilityFlags * @param array[] $rootAliases * @psalm-param array> $rootAliases * @param string[] $rootReferences an array of package name => source reference diff --git a/src/Composer/Package/Version/StabilityFilter.php b/src/Composer/Package/Version/StabilityFilter.php index ed27af080..df8c4bc16 100644 --- a/src/Composer/Package/Version/StabilityFilter.php +++ b/src/Composer/Package/Version/StabilityFilter.php @@ -22,6 +22,12 @@ class StabilityFilter /** * Checks if any of the provided package names in the given stability match the configured acceptable stability and flags * + * @param int[] $acceptableStabilities array of stability => BasePackage::STABILITY_* value + * @psalm-param array $acceptableStabilities + * @param int[] $stabilityFlags an array of package name => BasePackage::STABILITY_* value + * @psalm-param array $stabilityFlags + * @param string[] $names The package name(s) to check for stability flags + * @param string $stability one of 'stable', 'RC', 'beta', 'alpha' or 'dev' * @return bool true if any package name is acceptable */ public static function isPackageAcceptable(array $acceptableStabilities, array $stabilityFlags, $names, $stability) diff --git a/src/Composer/Repository/RepositoryInterface.php b/src/Composer/Repository/RepositoryInterface.php index 317368d10..afb99ca62 100644 --- a/src/Composer/Repository/RepositoryInterface.php +++ b/src/Composer/Repository/RepositoryInterface.php @@ -13,6 +13,7 @@ namespace Composer\Repository; use Composer\Package\PackageInterface; +use Composer\Package\BasePackage; use Composer\Semver\Constraint\ConstraintInterface; /** @@ -70,8 +71,10 @@ interface RepositoryInterface extends \Countable * - The namesFound returned are names which should be considered as canonically found in this repository, that should not be looked up in any further lower priority repositories * * @param ConstraintInterface[] $packageNameMap package names pointing to constraints - * @param array $acceptableStabilities - * @param array $stabilityFlags + * @param int[] $acceptableStabilities array of stability => BasePackage::STABILITY_* value + * @psalm-param array $acceptableStabilities + * @param int[] $stabilityFlags an array of package name => BasePackage::STABILITY_* value + * @psalm-param array $stabilityFlags * * @return array [namesFound => string[], packages => PackageInterface[]] * @psalm-return array{namesFound: string[], packages: PackageInterface[]} From a133e694d06916672d4620b6741eb4d7348598cd Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 25 Jun 2020 08:57:18 +0200 Subject: [PATCH 16/59] Speed up fund command fetching of latest data and make sure it uses default branch info --- src/Composer/Command/FundCommand.php | 31 +++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/Composer/Command/FundCommand.php b/src/Composer/Command/FundCommand.php index eb60ec577..a5c4dfc02 100644 --- a/src/Composer/Command/FundCommand.php +++ b/src/Composer/Command/FundCommand.php @@ -14,6 +14,8 @@ namespace Composer\Command; use Composer\Package\CompletePackageInterface; use Composer\Package\AliasPackage; +use Composer\Package\BasePackage; +use Composer\Semver\Constraint\MatchAllConstraint; use Composer\Repository\CompositeRepository; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -39,15 +41,38 @@ class FundCommand extends BaseCommand $repo = $composer->getRepositoryManager()->getLocalRepository(); $remoteRepos = new CompositeRepository($composer->getRepositoryManager()->getRepositories()); $fundings = array(); + + $packagesToLoad = array(); foreach ($repo->getPackages() as $package) { if ($package instanceof AliasPackage) { continue; } - $latest = $remoteRepos->findPackage($package->getName(), 'dev-master'); - if ($latest instanceof CompletePackageInterface && $latest->getFunding()) { - $fundings = $this->insertFundingData($fundings, $latest); + $packagesToLoad[$package->getName()] = new MatchAllConstraint(); + } + + // load all packages dev versions in parallel + $result = $remoteRepos->loadPackages($packagesToLoad, array('dev' => BasePackage::STABILITY_DEV), array()); + + // collect funding data from default branches + foreach ($result['packages'] as $pkg) { + if ( + !$pkg instanceof AliasPackage + && $pkg instanceof CompletePackageInterface + && $pkg->isDefaultBranch() + && $pkg->getFunding() + && isset($packagesToLoad[$pkg->getName()]) + ) { + $fundings = $this->insertFundingData($fundings, $pkg); + unset($packagesToLoad[$package->getName()]); + } + } + + // collect funding from installed packages if none was found in the default branch above + foreach ($repo->getPackages() as $package) { + if ($package instanceof AliasPackage || !isset($packagesToLoad[$package->getName()])) { continue; } + if ($package instanceof CompletePackageInterface && $package->getFunding()) { $fundings = $this->insertFundingData($fundings, $package); } From e2f1e8aed6006dcdaa1fa7862a644a62ef87510c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 25 Jun 2020 08:57:42 +0200 Subject: [PATCH 17/59] Allow optimizing fetching of dev versions only if no other stability is needed --- src/Composer/Repository/ComposerRepository.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 74ce15e8a..f7711390f 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -676,6 +676,10 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito if ($acceptableStabilities === null || $stabilityFlags === null || StabilityFilter::isPackageAcceptable($acceptableStabilities, $stabilityFlags, array($name), 'dev')) { $packageNames[$name.'~dev'] = $constraint; } + // if only dev stability is requested, we skip loading the non dev file + if (isset($acceptableStabilities['dev']) && count($acceptableStabilities) === 1 && count($stabilityFlags) === 0) { + unset($packageNames[$name]); + } } foreach ($packageNames as $name => $constraint) { From 86af5e5c6e514dd3865e3bbf501dcc21472dceb3 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 25 Jun 2020 08:58:08 +0200 Subject: [PATCH 18/59] Remove conflict between trunk and master, as they do not normalize anymore --- src/Composer/Repository/VcsRepository.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/Composer/Repository/VcsRepository.php b/src/Composer/Repository/VcsRepository.php index 5c6c09557..24f85053c 100644 --- a/src/Composer/Repository/VcsRepository.php +++ b/src/Composer/Repository/VcsRepository.php @@ -291,13 +291,6 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt $this->io->overwriteError($msg, false); } - if ($branch === 'trunk' && isset($branches['master'])) { - if ($isVeryVerbose) { - $this->io->writeError('Skipped branch '.$branch.', can not parse both master and trunk branches as they both resolve to '.VersionParser::DEV_MASTER_ALIAS.' internally'); - } - continue; - } - if (!$parsedBranch = $this->validateBranch($branch)) { if ($isVeryVerbose) { $this->io->writeError('Skipped branch '.$branch.', invalid name'); From 4a2ea3c67aa49a3dce68ef5a56614e1941d68965 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 25 Jun 2020 09:16:23 +0200 Subject: [PATCH 19/59] Fix var name --- src/Composer/Command/FundCommand.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Composer/Command/FundCommand.php b/src/Composer/Command/FundCommand.php index a5c4dfc02..045eb5051 100644 --- a/src/Composer/Command/FundCommand.php +++ b/src/Composer/Command/FundCommand.php @@ -54,15 +54,15 @@ class FundCommand extends BaseCommand $result = $remoteRepos->loadPackages($packagesToLoad, array('dev' => BasePackage::STABILITY_DEV), array()); // collect funding data from default branches - foreach ($result['packages'] as $pkg) { + foreach ($result['packages'] as $package) { if ( - !$pkg instanceof AliasPackage - && $pkg instanceof CompletePackageInterface - && $pkg->isDefaultBranch() - && $pkg->getFunding() - && isset($packagesToLoad[$pkg->getName()]) + !$package instanceof AliasPackage + && $package instanceof CompletePackageInterface + && $package->isDefaultBranch() + && $package->getFunding() + && isset($packagesToLoad[$package->getName()]) ) { - $fundings = $this->insertFundingData($fundings, $pkg); + $fundings = $this->insertFundingData($fundings, $package); unset($packagesToLoad[$package->getName()]); } } From bee91155a136659429b4b46f4290ea6aa3c09943 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 26 Jun 2020 16:16:16 +0200 Subject: [PATCH 20/59] Fix guessing of default branch when HEAD is a non-feature-branch --- .../Package/Version/VersionGuesser.php | 27 ++++++++++++------- .../Package/Version/VersionGuesserTest.php | 16 +++++------ 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/Composer/Package/Version/VersionGuesser.php b/src/Composer/Package/Version/VersionGuesser.php index 1c2f99247..eaeca6dc5 100644 --- a/src/Composer/Package/Version/VersionGuesser.php +++ b/src/Composer/Package/Version/VersionGuesser.php @@ -137,7 +137,7 @@ class VersionGuesser } else { $version = $this->versionParser->normalizeBranch($match[1]); $prettyVersion = 'dev-' . $match[1]; - $isFeatureBranch = 0 === strpos($version, 'dev-'); + $isFeatureBranch = $this->isFeatureBranch($packageConfig, $match[1]); } if ($match[2]) { @@ -245,19 +245,18 @@ class VersionGuesser $branch = preg_replace('{^dev-}', '', $version); $length = PHP_INT_MAX; - $nonFeatureBranches = ''; - if (!empty($packageConfig['non-feature-branches'])) { - $nonFeatureBranches = implode('|', $packageConfig['non-feature-branches']); - } - // return directly, if branch is configured to be non-feature branch - if (preg_match('{^(' . $nonFeatureBranches . ')$}', $branch)) { + if (!$this->isFeatureBranch($packageConfig, $branch)) { return array('version' => $version, 'pretty_version' => $prettyVersion); } + // sort numeric branches below named ones, to make sure if the branch has the same distance from main and 1.10 and 1.9 for example, main is picked + // and sort using natural sort so that 1.10 will appear before 1.9 + rsort($branches, SORT_NATURAL); + foreach ($branches as $candidate) { // do not compare against itself or other feature branches - if ($candidate === $branch || !preg_match('{^(' . $nonFeatureBranches . '|master|main|latest|next|current|support|tip|trunk|default|develop|\d+\..+)$}', $candidate, $match)) { + if ($candidate === $branch || $this->isFeatureBranch($packageConfig, $candidate)) { continue; } @@ -269,7 +268,7 @@ class VersionGuesser if (strlen($output) < $length) { $length = strlen($output); $version = $this->versionParser->normalizeBranch($candidate); - $prettyVersion = 'dev-' . $match[1]; + $prettyVersion = 'dev-' . $candidate; } } } @@ -277,6 +276,16 @@ class VersionGuesser return array('version' => $version, 'pretty_version' => $prettyVersion); } + private function isFeatureBranch(array $packageConfig, $branchName) + { + $nonFeatureBranches = ''; + if (!empty($packageConfig['non-feature-branches'])) { + $nonFeatureBranches = implode('|', $packageConfig['non-feature-branches']); + } + + return !preg_match('{^(' . $nonFeatureBranches . '|master|main|latest|next|current|support|tip|trunk|default|develop|\d+\..+)$}', $branchName, $match); + } + private function guessFossilVersion(array $packageConfig, $path) { $version = null; diff --git a/tests/Composer/Test/Package/Version/VersionGuesserTest.php b/tests/Composer/Test/Package/Version/VersionGuesserTest.php index cedd697ce..2d6209673 100644 --- a/tests/Composer/Test/Package/Version/VersionGuesserTest.php +++ b/tests/Composer/Test/Package/Version/VersionGuesserTest.php @@ -189,7 +189,7 @@ class VersionGuesserTest extends TestCase ->method('execute') ->willReturnCallback(function ($command, &$output) use ($self, $commitHash, $anotherCommitHash) { $self->assertEquals('git branch --no-color --no-abbrev -v', $command); - $output = " arbitrary $commitHash Commit message\n* current $anotherCommitHash Another message\n"; + $output = " arbitrary $commitHash Commit message\n* feature $anotherCommitHash Another message\n"; return 0; }) @@ -199,7 +199,7 @@ class VersionGuesserTest extends TestCase ->expects($this->at(1)) ->method('execute') ->willReturnCallback(function ($command, &$output, $path) use ($self, $anotherCommitHash) { - $self->assertEquals('git rev-list arbitrary..current', $command); + $self->assertEquals('git rev-list arbitrary..feature', $command); $output = "$anotherCommitHash\n"; return 0; @@ -213,8 +213,8 @@ class VersionGuesserTest extends TestCase $this->assertEquals("dev-arbitrary", $versionArray['version']); $this->assertEquals($anotherCommitHash, $versionArray['commit']); - $this->assertEquals("dev-current", $versionArray['feature_version']); - $this->assertEquals("dev-current", $versionArray['feature_pretty_version']); + $this->assertEquals("dev-feature", $versionArray['feature_version']); + $this->assertEquals("dev-feature", $versionArray['feature_pretty_version']); } public function testGuessVersionReadsAndRespectsNonFeatureBranchesConfigurationForArbitraryNamingRegex() @@ -236,7 +236,7 @@ class VersionGuesserTest extends TestCase ->method('execute') ->willReturnCallback(function ($command, &$output) use ($self, $commitHash, $anotherCommitHash) { $self->assertEquals('git branch --no-color --no-abbrev -v', $command); - $output = " latest-testing $commitHash Commit message\n* current $anotherCommitHash Another message\n"; + $output = " latest-testing $commitHash Commit message\n* feature $anotherCommitHash Another message\n"; return 0; }) @@ -245,7 +245,7 @@ class VersionGuesserTest extends TestCase ->expects($this->at(1)) ->method('execute') ->willReturnCallback(function ($command, &$output, $path) use ($self, $anotherCommitHash) { - $self->assertEquals('git rev-list latest-testing..current', $command); + $self->assertEquals('git rev-list latest-testing..feature', $command); $output = "$anotherCommitHash\n"; return 0; @@ -259,8 +259,8 @@ class VersionGuesserTest extends TestCase $this->assertEquals("dev-latest-testing", $versionArray['version']); $this->assertEquals($anotherCommitHash, $versionArray['commit']); - $this->assertEquals("dev-current", $versionArray['feature_version']); - $this->assertEquals("dev-current", $versionArray['feature_pretty_version']); + $this->assertEquals("dev-feature", $versionArray['feature_version']); + $this->assertEquals("dev-feature", $versionArray['feature_pretty_version']); } public function testGuessVersionReadsAndRespectsNonFeatureBranchesConfigurationForArbitraryNamingWhenOnNonFeatureBranch() From 5ef398ebb9bfcfbe686d3e844547835a9111afef Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 26 Jun 2020 16:58:02 +0200 Subject: [PATCH 21/59] Fix 5.3 support --- src/Composer/Package/Version/VersionGuesser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Package/Version/VersionGuesser.php b/src/Composer/Package/Version/VersionGuesser.php index eaeca6dc5..742758425 100644 --- a/src/Composer/Package/Version/VersionGuesser.php +++ b/src/Composer/Package/Version/VersionGuesser.php @@ -252,7 +252,7 @@ class VersionGuesser // sort numeric branches below named ones, to make sure if the branch has the same distance from main and 1.10 and 1.9 for example, main is picked // and sort using natural sort so that 1.10 will appear before 1.9 - rsort($branches, SORT_NATURAL); + rsort($branches, defined('SORT_NATURAL') ? SORT_NATURAL : SORT_REGULAR); foreach ($branches as $candidate) { // do not compare against itself or other feature branches From 263121049334fc5af1f6b3c23292831b02ae2369 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 19 Jun 2020 23:47:54 +0200 Subject: [PATCH 22/59] Solver: analyze multi conflict rules in conflict resolution correctly --- src/Composer/DependencyResolver/Pool.php | 11 +++++++++++ src/Composer/DependencyResolver/Solver.php | 22 ++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index b46447f72..c4173a00a 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -189,4 +189,15 @@ class Pool implements \Countable { return \in_array($package, $this->unacceptableFixedPackages, true); } + + public function __toString() + { + $str = "Pool:\n"; + + foreach ($this->packages as $package) { + $str .= '- '.str_pad($package->id, 6, ' ', STR_PAD_LEFT).': '.$package->getName()."\n"; + } + + return $str; + } } diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 73e79c819..4ded368ba 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -15,6 +15,7 @@ namespace Composer\DependencyResolver; use Composer\IO\IOInterface; use Composer\Package\PackageInterface; use Composer\Repository\PlatformRepository; +use Composer\Semver\Constraint\MultiConstraint; /** * @author Nils Adermann @@ -385,6 +386,10 @@ class Solver $this->learnedPool[\count($this->learnedPool) - 1][] = $rule; foreach ($rule->getLiterals() as $literal) { + if (!$this->decisions->decided($literal)) { + continue; + } + // skip the one true literal if ($this->decisions->satisfy($literal)) { continue; @@ -412,6 +417,16 @@ class Solver } unset($literal); + $decision = $this->decisions->atOffset($decisionId-1); + if ($rule !== $decision[Decisions::DECISION_REASON] && $decision[Decisions::DECISION_REASON] instanceof MultiConflictRule) { + $num++; + foreach ($decision[Decisions::DECISION_REASON]->getLiterals() as $literal) { + if (!$this->decisions->satisfy($literal)) { + $seen[abs($literal)] = true; + } + } + } + $l1retry = true; while ($l1retry) { $l1retry = false; @@ -459,6 +474,13 @@ class Solver $l1num++; $l1retry = true; } + + $decision = $this->decisions->atOffset($decisionId); + $rule = $decision[Decisions::DECISION_REASON]; + + if ($rule instanceof MultiConflictRule) { + $l1retry = true; + } } $decision = $this->decisions->atOffset($decisionId); From 2cfb60e68375d83dc9be0bc93c9c4905612df80b Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 23 Jun 2020 21:52:55 +0200 Subject: [PATCH 23/59] Solver: Add test case for github issue 7051 --- .../installer/github-issues-7051.test | 331 ++++++++++++++++++ 1 file changed, 331 insertions(+) create mode 100644 tests/Composer/Test/Fixtures/installer/github-issues-7051.test diff --git a/tests/Composer/Test/Fixtures/installer/github-issues-7051.test b/tests/Composer/Test/Fixtures/installer/github-issues-7051.test new file mode 100644 index 000000000..ea365689d --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/github-issues-7051.test @@ -0,0 +1,331 @@ +--TEST-- +Solver Bug Exception caused by analyze on mutli conflict rule reported in GitHub issue 7051 https://github.com/composer/composer/issues/7051 +--COMPOSER-- +{ + "require": { + "illuminate/queue": "*", + "friendsofphp/php-cs-fixer": "*" + }, + "repositories": { + "illuminate/queue": { + "type": "package", + "package": [ + { + "name": "illuminate/queue", + "version": "v5.2.0", + "require": { + "illuminate/console": "5.2.*" + } + } + ] + }, + "friendsofphp/php-cs-fixer": { + "type": "package", + "package": [ + { + "name": "friendsofphp/php-cs-fixer", + "version": "v2.10.5", + "type": "application", + "require": { + "symfony/console": "^3.2 || ^4.0" + } + }, + { + "name": "friendsofphp/php-cs-fixer", + "version": "v2.10.4", + "type": "application", + "require": { + "symfony/console": "^3.2 || ^4.0" + } + } + ] + }, + "illuminate/console": { + "type": "package", + "package": [ + { + "name": "illuminate/console", + "version": "v5.2.26", + "require": { + "symfony/console": "2.8.*" + } + }, + { + "name": "illuminate/console", + "version": "v5.2.25", + "require": { + "symfony/console": "3.1.*" + } + } + ] + }, + "symfony/console": { + "type": "package", + "package": [ + { + "name": "symfony/console", + "version": "v3.4.29" + }, + { + "name": "symfony/console", + "version": "v3.4.28" + }, + { + "name": "symfony/console", + "version": "v3.3.18" + }, + { + "name": "symfony/console", + "version": "v3.3.17" + }, + { + "name": "symfony/console", + "version": "v3.2.14" + }, + { + "name": "symfony/console", + "version": "v3.2.13" + }, + { + "name": "symfony/console", + "version": "v3.1.10" + }, + { + "name": "symfony/console", + "version": "v3.1.9" + }, + { + "name": "symfony/console", + "version": "v2.8.8" + }, + { + "name": "symfony/console", + "version": "v2.8.7" + } + ] + }, + "packagist.org": false + } +} +--RUN-- +update +--EXPECT-OUTPUT-- +Loading composer repositories with package information +Updating dependencies +Your requirements could not be resolved to an installable set of packages. + + Problem 1 + - Conclusion: don't install friendsofphp/php-cs-fixer v2.10.5, learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. + - Conclusion: don't install one of symfony/console[v3.1.10], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. + - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - Conclusion: don't install one of symfony/console[v2.8.7], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - Conclusion: don't install symfony/console[v3.4.29] | install symfony/console[v2.8.8], learned rules: + - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. + - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. + - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. + - Conclusion: don't install one of symfony/console[v2.8.8], friendsofphp/php-cs-fixer[v2.10.5], learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. + - Conclusion: don't install symfony/console v3.4.29, learned rules: + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. + - Conclusion: don't install symfony/console[v3.4.29] | install symfony/console[v2.8.8], learned rules: + - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. + - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. + - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. + - Conclusion: don't install symfony/console v2.8.8, learned rules: + - friendsofphp/php-cs-fixer v2.10.4 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - Conclusion: don't install symfony/console v3.4.29, learned rules: + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. + - Conclusion: don't install symfony/console[v3.4.29] | install symfony/console[v2.8.8], learned rules: + - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. + - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. + - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. + - Root composer.json requires friendsofphp/php-cs-fixer * -> satisfiable by friendsofphp/php-cs-fixer[v2.10.4, v2.10.5]. + - Conclusion: don't install friendsofphp/php-cs-fixer v2.10.5, learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. + - Conclusion: don't install one of symfony/console[v3.1.10], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. + - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - Conclusion: don't install one of symfony/console[v2.8.7], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - Conclusion: don't install symfony/console[v3.4.29] | install symfony/console[v2.8.8], learned rules: + - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. + - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. + - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. + - Conclusion: don't install one of symfony/console[v2.8.8], friendsofphp/php-cs-fixer[v2.10.5], learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. + - Conclusion: don't install symfony/console v3.4.28, learned rules: + - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. + - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. + - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - Conclusion: don't install symfony/console v2.8.8, learned rules: + - friendsofphp/php-cs-fixer v2.10.4 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - Conclusion: don't install symfony/console v3.4.29, learned rules: + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. + - Conclusion: don't install symfony/console[v3.4.29] | install symfony/console[v2.8.8], learned rules: + - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. + - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. + - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. + - Root composer.json requires friendsofphp/php-cs-fixer * -> satisfiable by friendsofphp/php-cs-fixer[v2.10.4, v2.10.5]. + - Conclusion: don't install friendsofphp/php-cs-fixer v2.10.5, learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. + - Conclusion: don't install one of symfony/console[v3.1.10], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. + - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - Conclusion: don't install one of symfony/console[v2.8.7], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - Conclusion: don't install symfony/console[v3.4.29] | install symfony/console[v2.8.8], learned rules: + - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. + - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. + - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. + - Conclusion: don't install one of symfony/console[v2.8.8], friendsofphp/php-cs-fixer[v2.10.5], learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. + - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. + - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. + - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - Conclusion: don't install symfony/console v2.8.7, learned rules: + - friendsofphp/php-cs-fixer v2.10.4 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - Conclusion: don't install symfony/console v3.4.28, learned rules: + - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. + - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. + - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - Conclusion: don't install symfony/console v2.8.8, learned rules: + - friendsofphp/php-cs-fixer v2.10.4 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - Conclusion: don't install symfony/console v3.4.29, learned rules: + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. + - Conclusion: don't install symfony/console[v3.4.29] | install symfony/console[v2.8.8], learned rules: + - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. + - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. + - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. + - Root composer.json requires friendsofphp/php-cs-fixer * -> satisfiable by friendsofphp/php-cs-fixer[v2.10.4, v2.10.5]. + - Conclusion: don't install friendsofphp/php-cs-fixer v2.10.5, learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. + - Conclusion: don't install one of symfony/console[v3.1.10], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. + - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - Conclusion: don't install one of symfony/console[v2.8.7], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - Conclusion: don't install symfony/console[v3.4.29] | install symfony/console[v2.8.8], learned rules: + - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. + - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. + - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. + - Conclusion: don't install one of symfony/console[v2.8.8], friendsofphp/php-cs-fixer[v2.10.5], learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. + - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. + - Conclusion: don't install symfony/console v3.4.29, learned rules: + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. + - Conclusion: don't install symfony/console[v3.4.29] | install symfony/console[v2.8.8], learned rules: + - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. + - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. + - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. + - Root composer.json requires friendsofphp/php-cs-fixer * -> satisfiable by friendsofphp/php-cs-fixer[v2.10.4, v2.10.5]. + - Conclusion: don't install friendsofphp/php-cs-fixer v2.10.5, learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. + - Conclusion: don't install one of symfony/console[v3.1.10], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. + - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - Conclusion: don't install one of symfony/console[v2.8.7], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - Conclusion: don't install symfony/console[v3.4.29] | install symfony/console[v2.8.8], learned rules: + - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. + - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. + - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. + - Conclusion: don't install one of symfony/console[v2.8.8], friendsofphp/php-cs-fixer[v2.10.5], learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. + - Root composer.json requires friendsofphp/php-cs-fixer * -> satisfiable by friendsofphp/php-cs-fixer[v2.10.4, v2.10.5]. + - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. + - friendsofphp/php-cs-fixer v2.10.4 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. + - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. + - Conclusion: don't install symfony/console v3.1.10, learned rules: + - friendsofphp/php-cs-fixer v2.10.4 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - Conclusion: don't install symfony/console v3.4.28, learned rules: + - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. + - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. + - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - Conclusion: don't install symfony/console v2.8.8, learned rules: + - friendsofphp/php-cs-fixer v2.10.4 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - Conclusion: don't install symfony/console v3.4.29, learned rules: + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. + - Conclusion: don't install symfony/console[v3.4.29] | install symfony/console[v2.8.8], learned rules: + - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. + - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. + - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. + - Root composer.json requires friendsofphp/php-cs-fixer * -> satisfiable by friendsofphp/php-cs-fixer[v2.10.4, v2.10.5]. + - Conclusion: don't install friendsofphp/php-cs-fixer v2.10.5, learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. + - Conclusion: don't install one of symfony/console[v3.1.10], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. + - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - Conclusion: don't install one of symfony/console[v2.8.7], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - Conclusion: don't install symfony/console[v3.4.29] | install symfony/console[v2.8.8], learned rules: + - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. + - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. + - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. + - Conclusion: don't install one of symfony/console[v2.8.8], friendsofphp/php-cs-fixer[v2.10.5], learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. + - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. + - Conclusion: don't install symfony/console v3.4.29, learned rules: + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. + - Conclusion: don't install symfony/console[v3.4.29] | install symfony/console[v2.8.8], learned rules: + - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. + - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. + - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. + - Root composer.json requires friendsofphp/php-cs-fixer * -> satisfiable by friendsofphp/php-cs-fixer[v2.10.4, v2.10.5]. + - Conclusion: don't install friendsofphp/php-cs-fixer v2.10.5, learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. + - Conclusion: don't install one of symfony/console[v3.1.10], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. + - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - Conclusion: don't install one of symfony/console[v2.8.7], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - Conclusion: don't install symfony/console[v3.4.29] | install symfony/console[v2.8.8], learned rules: + - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. + - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. + - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. + - Conclusion: don't install one of symfony/console[v2.8.8], friendsofphp/php-cs-fixer[v2.10.5], learned rules: + - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. + +--EXPECT-- + +--EXPECT-EXIT-CODE-- +2 + From c78eb49b5eae0dc8caa664b09872c62c923a29b8 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 23 Jun 2020 21:54:38 +0200 Subject: [PATCH 24/59] Remove unnecessary duplicate rule output from expected test result --- .../Composer/Test/Fixtures/installer/provider-conflicts3.test | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/Composer/Test/Fixtures/installer/provider-conflicts3.test b/tests/Composer/Test/Fixtures/installer/provider-conflicts3.test index 53d11ecf0..964ea0bcc 100644 --- a/tests/Composer/Test/Fixtures/installer/provider-conflicts3.test +++ b/tests/Composer/Test/Fixtures/installer/provider-conflicts3.test @@ -39,13 +39,10 @@ Your requirements could not be resolved to an installable set of packages. Problem 1 - Conclusion: don't install regular/pkg 1.0.3, learned rules: - Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. - - Only one of these can be installed: regular/pkg[1.0.0, 1.0.1, 1.0.2, 1.0.3], replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. replacer/pkg replaces regular/pkg and thus cannot coexist with it. - Conclusion: don't install regular/pkg 1.0.2, learned rules: - Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. - - Only one of these can be installed: regular/pkg[1.0.0, 1.0.1, 1.0.2, 1.0.3], replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. replacer/pkg replaces regular/pkg and thus cannot coexist with it. - Conclusion: don't install regular/pkg 1.0.1, learned rules: - Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. - - Only one of these can be installed: regular/pkg[1.0.0, 1.0.1, 1.0.2, 1.0.3], replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. replacer/pkg replaces regular/pkg and thus cannot coexist with it. - Only one of these can be installed: regular/pkg[1.0.0, 1.0.1, 1.0.2, 1.0.3], replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. replacer/pkg replaces regular/pkg and thus cannot coexist with it. - Root composer.json requires regular/pkg 1.* -> satisfiable by regular/pkg[1.0.0, 1.0.1, 1.0.2, 1.0.3]. - Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. From b34f9164705c81c531c6d75be4e59350feb58a0d Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 25 Jun 2020 09:32:51 +0200 Subject: [PATCH 25/59] Solver: No need to check previous decision if we reached the first one --- src/Composer/DependencyResolver/Solver.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 4ded368ba..5f1170ccf 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -417,12 +417,14 @@ class Solver } unset($literal); - $decision = $this->decisions->atOffset($decisionId-1); - if ($rule !== $decision[Decisions::DECISION_REASON] && $decision[Decisions::DECISION_REASON] instanceof MultiConflictRule) { - $num++; - foreach ($decision[Decisions::DECISION_REASON]->getLiterals() as $literal) { - if (!$this->decisions->satisfy($literal)) { - $seen[abs($literal)] = true; + if ($decisionId > 0) { + $decision = $this->decisions->atOffset($decisionId-1); + if ($rule !== $decision[Decisions::DECISION_REASON] && $decision[Decisions::DECISION_REASON] instanceof MultiConflictRule) { + $num++; + foreach ($decision[Decisions::DECISION_REASON]->getLiterals() as $literal) { + if (!$this->decisions->satisfy($literal)) { + $seen[abs($literal)] = true; + } } } } From 6e05345be73e8e99a08e9de5639f37251a51230d Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 26 Jun 2020 21:29:05 +0200 Subject: [PATCH 26/59] Solver: Move analyze handling of multiconflict rule to clearer location This way we're not looking at the previous decision at the top of the loop but working with the current decision at the bottom --- src/Composer/DependencyResolver/Solver.php | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 5f1170ccf..772d022d8 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -417,18 +417,6 @@ class Solver } unset($literal); - if ($decisionId > 0) { - $decision = $this->decisions->atOffset($decisionId-1); - if ($rule !== $decision[Decisions::DECISION_REASON] && $decision[Decisions::DECISION_REASON] instanceof MultiConflictRule) { - $num++; - foreach ($decision[Decisions::DECISION_REASON]->getLiterals() as $literal) { - if (!$this->decisions->satisfy($literal)) { - $seen[abs($literal)] = true; - } - } - } - } - $l1retry = true; while ($l1retry) { $l1retry = false; @@ -481,6 +469,15 @@ class Solver $rule = $decision[Decisions::DECISION_REASON]; if ($rule instanceof MultiConflictRule) { + // there is only ever exactly one positive decision in a multiconflict rule + foreach ($rule->getLiterals() as $literal) { + if (!isset($seen[abs($literal)]) && !$this->decisions->satisfy($literal)) { + $num++; + $seen[abs($literal)] = true; + break; + } + } + $l1retry = true; } } From 851050e85c07f68ce0a0290fa53f6ed884751a6b Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 26 Jun 2020 21:47:50 +0200 Subject: [PATCH 27/59] Solver: multiconflict analyze handles positive decision same as regular literal --- src/Composer/DependencyResolver/Solver.php | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 772d022d8..c00beef65 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -471,8 +471,20 @@ class Solver if ($rule instanceof MultiConflictRule) { // there is only ever exactly one positive decision in a multiconflict rule foreach ($rule->getLiterals() as $literal) { - if (!isset($seen[abs($literal)]) && !$this->decisions->satisfy($literal)) { - $num++; + if (!isset($seen[abs($literal)]) && $this->decisions->satisfy(-$literal)) { + $l = $this->decisions->decisionLevel($literal); + if (1 === $l) { + $l1num++; + } elseif ($level === $l) { + $num++; + } else { + // not level1 or conflict level, add to new rule + $learnedLiterals[] = $literal; + + if ($l > $ruleLevel) { + $ruleLevel = $l; + } + } $seen[abs($literal)] = true; break; } From edef7481699d4a8fe6c6df7750cb32a76397c701 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 26 Jun 2020 21:55:03 +0200 Subject: [PATCH 28/59] Solver: Clarify when/why undecided literals can get skipped in analyze --- src/Composer/DependencyResolver/Solver.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index c00beef65..f9f1be37c 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -386,7 +386,8 @@ class Solver $this->learnedPool[\count($this->learnedPool) - 1][] = $rule; foreach ($rule->getLiterals() as $literal) { - if (!$this->decisions->decided($literal)) { + // multiconflictrule is really a bunch of rules in one, so some may not have finished propagating yet + if ($rule instanceof MultiConflictRule && !$this->decisions->decided($literal)) { continue; } From de3931c742ba336eb32c48b1ac2bb2bc4e3f4792 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 26 Jun 2020 22:03:18 +0200 Subject: [PATCH 29/59] Revert "Remove unnecessary duplicate rule output from expected test result" This reverts commit c78eb49b5eae0dc8caa664b09872c62c923a29b8. --- .../Composer/Test/Fixtures/installer/provider-conflicts3.test | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/Composer/Test/Fixtures/installer/provider-conflicts3.test b/tests/Composer/Test/Fixtures/installer/provider-conflicts3.test index 964ea0bcc..53d11ecf0 100644 --- a/tests/Composer/Test/Fixtures/installer/provider-conflicts3.test +++ b/tests/Composer/Test/Fixtures/installer/provider-conflicts3.test @@ -39,10 +39,13 @@ Your requirements could not be resolved to an installable set of packages. Problem 1 - Conclusion: don't install regular/pkg 1.0.3, learned rules: - Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. + - Only one of these can be installed: regular/pkg[1.0.0, 1.0.1, 1.0.2, 1.0.3], replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. replacer/pkg replaces regular/pkg and thus cannot coexist with it. - Conclusion: don't install regular/pkg 1.0.2, learned rules: - Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. + - Only one of these can be installed: regular/pkg[1.0.0, 1.0.1, 1.0.2, 1.0.3], replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. replacer/pkg replaces regular/pkg and thus cannot coexist with it. - Conclusion: don't install regular/pkg 1.0.1, learned rules: - Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. + - Only one of these can be installed: regular/pkg[1.0.0, 1.0.1, 1.0.2, 1.0.3], replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. replacer/pkg replaces regular/pkg and thus cannot coexist with it. - Only one of these can be installed: regular/pkg[1.0.0, 1.0.1, 1.0.2, 1.0.3], replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. replacer/pkg replaces regular/pkg and thus cannot coexist with it. - Root composer.json requires regular/pkg 1.* -> satisfiable by regular/pkg[1.0.0, 1.0.1, 1.0.2, 1.0.3]. - Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. From 3f5dde98730310fe58ae6c9432ed75967373045a Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 26 Jun 2020 22:04:17 +0200 Subject: [PATCH 30/59] Solver: Ensure multi conflict rules still get added to learned pool --- src/Composer/DependencyResolver/Solver.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index f9f1be37c..fa5e2c586 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -473,6 +473,7 @@ class Solver // there is only ever exactly one positive decision in a multiconflict rule foreach ($rule->getLiterals() as $literal) { if (!isset($seen[abs($literal)]) && $this->decisions->satisfy(-$literal)) { + $this->learnedPool[\count($this->learnedPool) - 1][] = $rule; $l = $this->decisions->decisionLevel($literal); if (1 === $l) { $l1num++; From 3d2d33dd58d213a014a2348802f83496bef400e4 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 26 Jun 2020 22:11:13 +0200 Subject: [PATCH 31/59] Solver: Add missing rules and replace some with %A for pcre length limit --- .../installer/github-issues-7051.test | 74 +++++++++++-------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/tests/Composer/Test/Fixtures/installer/github-issues-7051.test b/tests/Composer/Test/Fixtures/installer/github-issues-7051.test index ea365689d..55e758441 100644 --- a/tests/Composer/Test/Fixtures/installer/github-issues-7051.test +++ b/tests/Composer/Test/Fixtures/installer/github-issues-7051.test @@ -117,20 +117,25 @@ Your requirements could not be resolved to an installable set of packages. Problem 1 - Conclusion: don't install friendsofphp/php-cs-fixer v2.10.5, learned rules: - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. - Conclusion: don't install one of symfony/console[v3.1.10], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. - Conclusion: don't install one of symfony/console[v2.8.7], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Conclusion: don't install symfony/console[v3.4.29] | install symfony/console[v2.8.8], learned rules: - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. - Conclusion: don't install one of symfony/console[v2.8.8], friendsofphp/php-cs-fixer[v2.10.5], learned rules: - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. - Conclusion: don't install symfony/console v3.4.29, learned rules: - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. @@ -138,100 +143,125 @@ Your requirements could not be resolved to an installable set of packages. - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. - Conclusion: don't install symfony/console v2.8.8, learned rules: - friendsofphp/php-cs-fixer v2.10.4 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Conclusion: don't install symfony/console v3.4.29, learned rules: - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Conclusion: don't install symfony/console[v3.4.29] | install symfony/console[v2.8.8], learned rules: - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. - Root composer.json requires friendsofphp/php-cs-fixer * -> satisfiable by friendsofphp/php-cs-fixer[v2.10.4, v2.10.5]. - Conclusion: don't install friendsofphp/php-cs-fixer v2.10.5, learned rules: - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. - Conclusion: don't install one of symfony/console[v3.1.10], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. - Conclusion: don't install one of symfony/console[v2.8.7], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Conclusion: don't install symfony/console[v3.4.29] | install symfony/console[v2.8.8], learned rules: - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. - Conclusion: don't install one of symfony/console[v2.8.8], friendsofphp/php-cs-fixer[v2.10.5], learned rules: - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. - Conclusion: don't install symfony/console v3.4.28, learned rules: - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Conclusion: don't install symfony/console v2.8.8, learned rules: - friendsofphp/php-cs-fixer v2.10.4 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Conclusion: don't install symfony/console v3.4.29, learned rules: - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Conclusion: don't install symfony/console[v3.4.29] | install symfony/console[v2.8.8], learned rules: - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. - Root composer.json requires friendsofphp/php-cs-fixer * -> satisfiable by friendsofphp/php-cs-fixer[v2.10.4, v2.10.5]. - Conclusion: don't install friendsofphp/php-cs-fixer v2.10.5, learned rules: - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. - Conclusion: don't install one of symfony/console[v3.1.10], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. - Conclusion: don't install one of symfony/console[v2.8.7], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Conclusion: don't install symfony/console[v3.4.29] | install symfony/console[v2.8.8], learned rules: - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. - Conclusion: don't install one of symfony/console[v2.8.8], friendsofphp/php-cs-fixer[v2.10.5], learned rules: - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. - Conclusion: don't install symfony/console v2.8.7, learned rules: - friendsofphp/php-cs-fixer v2.10.4 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Conclusion: don't install symfony/console v3.4.28, learned rules: - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Conclusion: don't install symfony/console v2.8.8, learned rules: - friendsofphp/php-cs-fixer v2.10.4 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Conclusion: don't install symfony/console v3.4.29, learned rules: - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Conclusion: don't install symfony/console[v3.4.29] | install symfony/console[v2.8.8], learned rules: - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. - Root composer.json requires friendsofphp/php-cs-fixer * -> satisfiable by friendsofphp/php-cs-fixer[v2.10.4, v2.10.5]. - Conclusion: don't install friendsofphp/php-cs-fixer v2.10.5, learned rules: - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. - Conclusion: don't install one of symfony/console[v3.1.10], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. - Conclusion: don't install one of symfony/console[v2.8.7], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Conclusion: don't install symfony/console[v3.4.29] | install symfony/console[v2.8.8], learned rules: - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. - Conclusion: don't install one of symfony/console[v2.8.8], friendsofphp/php-cs-fixer[v2.10.5], learned rules: - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. - Conclusion: don't install symfony/console v3.4.29, learned rules: @@ -240,24 +270,30 @@ Your requirements could not be resolved to an installable set of packages. - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. - Root composer.json requires friendsofphp/php-cs-fixer * -> satisfiable by friendsofphp/php-cs-fixer[v2.10.4, v2.10.5]. - Conclusion: don't install friendsofphp/php-cs-fixer v2.10.5, learned rules: - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. - Conclusion: don't install one of symfony/console[v3.1.10], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. - Conclusion: don't install one of symfony/console[v2.8.7], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Conclusion: don't install symfony/console[v3.4.29] | install symfony/console[v2.8.8], learned rules: - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. - Conclusion: don't install one of symfony/console[v2.8.8], friendsofphp/php-cs-fixer[v2.10.5], learned rules: - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. - Root composer.json requires friendsofphp/php-cs-fixer * -> satisfiable by friendsofphp/php-cs-fixer[v2.10.4, v2.10.5]. - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. @@ -266,64 +302,40 @@ Your requirements could not be resolved to an installable set of packages. - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. - Conclusion: don't install symfony/console v3.1.10, learned rules: - friendsofphp/php-cs-fixer v2.10.4 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Conclusion: don't install symfony/console v3.4.28, learned rules: - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Conclusion: don't install symfony/console v2.8.8, learned rules: - friendsofphp/php-cs-fixer v2.10.4 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Conclusion: don't install symfony/console v3.4.29, learned rules: - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Conclusion: don't install symfony/console[v3.4.29] | install symfony/console[v2.8.8], learned rules: - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. - - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. - - Root composer.json requires friendsofphp/php-cs-fixer * -> satisfiable by friendsofphp/php-cs-fixer[v2.10.4, v2.10.5]. - - Conclusion: don't install friendsofphp/php-cs-fixer v2.10.5, learned rules: - - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. - - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. - - Conclusion: don't install one of symfony/console[v3.1.10], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: - - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. - - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. - - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. - - Conclusion: don't install one of symfony/console[v2.8.7], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: - - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. - - Conclusion: don't install symfony/console[v3.4.29] | install symfony/console[v2.8.8], learned rules: - - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. - - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. - - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. - - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. - - Conclusion: don't install one of symfony/console[v2.8.8], friendsofphp/php-cs-fixer[v2.10.5], learned rules: - - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. - - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. - - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. - - Conclusion: don't install symfony/console v3.4.29, learned rules: - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - - Conclusion: don't install symfony/console[v3.4.29] | install symfony/console[v2.8.8], learned rules: - - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. - - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. - - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. - Root composer.json requires friendsofphp/php-cs-fixer * -> satisfiable by friendsofphp/php-cs-fixer[v2.10.4, v2.10.5]. - Conclusion: don't install friendsofphp/php-cs-fixer v2.10.5, learned rules: - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. - Conclusion: don't install one of symfony/console[v3.1.10], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: - - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. - - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. - - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. - - Conclusion: don't install one of symfony/console[v2.8.7], friendsofphp/php-cs-fixer[v2.10.5] | install symfony/console[v3.4.29], learned rules: - - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + %A - Conclusion: don't install symfony/console[v3.4.29] | install symfony/console[v2.8.8], learned rules: - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. - illuminate/console v5.2.25 requires symfony/console 3.1.* -> satisfiable by symfony/console[v3.1.9, v3.1.10]. - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. - Conclusion: don't install one of symfony/console[v2.8.8], friendsofphp/php-cs-fixer[v2.10.5], learned rules: - friendsofphp/php-cs-fixer v2.10.5 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. + - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. - --EXPECT-- --EXPECT-EXIT-CODE-- From 351dcd1c6490dcc091b523cfd804391f737d86ba Mon Sep 17 00:00:00 2001 From: Rob Bast Date: Mon, 29 Jun 2020 13:11:53 +0200 Subject: [PATCH 32/59] Create an issue @ Docker repository on tag --- .github/workflows/release.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a67b4e6e5..7255735d8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -56,3 +56,20 @@ jobs: asset_path: ./composer.phar asset_name: composer.phar asset_content_type: application/octet-stream + + # This step requires a secret token with `pull` access to composer/docker. The default + # secrets.GITHUB_TOKEN is scoped to this repository only which is not sufficient. + - name: "Open issue @ Docker repository" + uses: actions/github-script@v2 + with: + github-token: ${{ secrets.PUBLIC_REPO_ACCESS_TOKEN }} + script: | + // github.ref value looks like 'refs/tags/TAG', cleanup + const tag = "${{ github.ref }}".replace(/refs\/tags\//, ''); + // create new issue on Docker repository + github.issues.create({ + owner: "${{ github.repository_owner }}", + repo: "docker", + title: `New Composer tag: ${ tag }`, + body: `https://github.com/${{ github.repository }}/releases/tag/${ tag }`, + }); From 6e3efabbfc3834ae701bc9fb243dbc42ddc0cce9 Mon Sep 17 00:00:00 2001 From: Ayesh Karunaratne Date: Tue, 30 Jun 2020 22:39:56 +0700 Subject: [PATCH 33/59] Multiple grammar fixes in markdown files --- UPGRADE-2.0.md | 4 ++-- doc/00-intro.md | 2 +- doc/01-basic-usage.md | 2 +- doc/02-libraries.md | 6 ++--- doc/03-cli.md | 22 +++++++++---------- doc/04-schema.md | 16 +++++++------- doc/05-repositories.md | 12 +++++----- doc/06-config.md | 4 ++-- doc/articles/aliases.md | 2 +- doc/articles/autoloader-optimization.md | 10 ++++----- doc/articles/custom-installers.md | 2 +- doc/articles/versions.md | 2 +- ...-unbound-version-constraints-a-bad-idea.md | 2 +- ...-composer-load-repositories-recursively.md | 2 +- 14 files changed, 44 insertions(+), 44 deletions(-) diff --git a/UPGRADE-2.0.md b/UPGRADE-2.0.md index 2b9ccf24a..ff6e6c597 100644 --- a/UPGRADE-2.0.md +++ b/UPGRADE-2.0.md @@ -3,7 +3,7 @@ ## For composer CLI users - The new platform-check feature means that Composer checks the runtime PHP version and available extensions to ensure they match the project dependencies. If a mismatch is found, it exits with error details to make sure problems are not overlooked. To avoid issues when deploying to production it is recommended to run `composer check-platform-reqs` with the production PHP process as part of your build or deployment process. -- If a packages exists in a higher priority repository, it will now be entirely ignored in lower priority repositories. See [repository priorities](https://getcomposer.org/repoprio) for details. +- If a package exists in a higher priority repository, it will now be entirely ignored in lower priority repositories. See [repository priorities](https://getcomposer.org/repoprio) for details. - Invalid PSR-0 / PSR-4 class configurations will not autoload anymore in optimized-autoloader mode, as per the warnings introduced in 1.10 - Package names now must comply to our [naming guidelines](doc/04-schema.md#name) or Composer will abort, as per the warnings introduced in 1.8.1 - Deprecated --no-suggest flag as it is not needed anymore @@ -27,7 +27,7 @@ - packages are now wrapped into a `"packages"` top level key instead of the whole file being the package array - packages now contain an `"installed-path"` key which lists where they were installed - there is a top level `"dev"` key which stores whether dev requirements were installed or not -- `PreFileDownloadEvent` now receives an `HttpDownloader` instance instead of `RemoteFilesystem`, and that instance can not be overridden by listeners anymore +- `PreFileDownloadEvent` now receives an `HttpDownloader` instance instead of `RemoteFilesystem`, and that instance cannot be overridden by listeners anymore - `VersionSelector::findBestCandidate`'s third argument (phpVersion) was removed in favor of passing in a complete PlatformRepository instance into the constructor - `InitCommand::determineRequirements`'s fourth argument (phpVersion) should now receive a complete PlatformRepository instance or null if platform requirements are to be ignored - `IOInterface` now extends PSR-3's `LoggerInterface`, and has new `writeRaw` + `writeErrorRaw` methods diff --git a/doc/00-intro.md b/doc/00-intro.md index ea88808a0..c686835b7 100644 --- a/doc/00-intro.md +++ b/doc/00-intro.md @@ -9,7 +9,7 @@ for you. Composer is **not** a package manager in the same sense as Yum or Apt are. Yes, it deals with "packages" or libraries, but it manages them on a per-project basis, installing them in a directory (e.g. `vendor`) inside your project. By -default it does not install anything globally. Thus, it is a dependency +default, it does not install anything globally. Thus, it is a dependency manager. It does however support a "global" project for convenience via the [global](03-cli.md#global) command. diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md index db34783d0..5dd1726bd 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -77,7 +77,7 @@ versions, how versions relate to each other, and on version constraints. > **Note:** If you are trying to require a package but Composer throws an error > regarding package stability, the version you have specified may not meet your -> default minimum stability requirements. By default only stable releases are taken +> default minimum stability requirements. By default, only stable releases are taken > into consideration when searching for valid package versions in your VCS. > > You might run into this if you are trying to require dev, alpha, beta, or RC diff --git a/doc/02-libraries.md b/doc/02-libraries.md index c02756e50..4a6c58878 100644 --- a/doc/02-libraries.md +++ b/doc/02-libraries.md @@ -26,14 +26,14 @@ In this case the project name is `acme/hello-world`, where `acme` is the vendor name. Supplying a vendor name is mandatory. > **Note:** If you don't know what to use as a vendor name, your GitHub -> username is usually a good bet. While package names are case insensitive, the +> username is usually a good bet. While package names are case-insensitive, the > convention is all lowercase and dashes for word separation. ## Library Versioning In the vast majority of cases, you will be maintaining your library using some sort of version control system like git, svn, hg or fossil. In these cases, -Composer infers versions from your VCS and you **should not** specify a version +Composer infers versions from your VCS, and you **should not** specify a version in your `composer.json` file. (See the [Versions article](articles/versions.md) to learn about how Composer uses VCS branches and tags to resolve version constraints.) @@ -69,7 +69,7 @@ can help your team to always test against the same dependency versions. However, this lock file will not have any effect on other projects that depend on it. It only has an effect on the main project. -If you do not want to commit the lock file and you are using git, add it to +If you do not want to commit the lock file, and you are using git, add it to the `.gitignore`. ## Publishing to a VCS diff --git a/doc/03-cli.md b/doc/03-cli.md index edfaaa9ed..a5e0d5de4 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -185,7 +185,7 @@ php composer.phar update vendor/package:2.0.1 vendor/package2:3.0.* * **--with-all-dependencies:** Update also dependencies of packages in the argument list, including those which are root requirements. * **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to get a faster autoloader. This is recommended especially for production, but can take - a bit of time to run so it is currently not done by default. + a bit of time to run, so it is currently not done by default. * **--classmap-authoritative (-a):** Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`. * **--apcu-autoloader:** Use APCu to cache found/not-found classes. @@ -252,7 +252,7 @@ If you do not specify a package, composer will prompt you to search for a packag * **--sort-packages:** Keep packages sorted in `composer.json`. * **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to get a faster autoloader. This is recommended especially for production, but - can take a bit of time to run so it is currently not done by default. + can take a bit of time to run, so it is currently not done by default. * **--classmap-authoritative (-a):** Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`. * **--apcu-autoloader:** Use APCu to cache found/not-found classes. @@ -763,7 +763,7 @@ performance. * **--no-scripts:** Skips the execution of all scripts defined in `composer.json` file. * **--optimize (-o):** Convert PSR-0/4 autoloading to classmap to get a faster autoloader. This is recommended especially for production, but can take - a bit of time to run so it is currently not done by default. + a bit of time to run, so it is currently not done by default. * **--classmap-authoritative (-a):** Autoload classes from the classmap only. Implicitly enables `--optimize`. * **--apcu:** Use APCu to cache found/not-found classes. @@ -781,7 +781,7 @@ Deletes all content from Composer's cache directories. ## licenses Lists the name, version and license of every package installed. Use -`--format=json` to get machine readable output. +`--format=json` to get machine-readable output. ### Options @@ -897,7 +897,7 @@ directory to something other than `vendor/bin`. The `COMPOSER_CACHE_DIR` var allows you to change the Composer cache directory, which is also configurable via the [`cache-dir`](06-config.md#cache-dir) option. -By default it points to `$COMPOSER_HOME/cache` on \*nix and macOS, and +By default, it points to `$COMPOSER_HOME/cache` on \*nix and macOS, and `C:\Users\\AppData\Local\Composer` (or `%LOCALAPPDATA%/Composer`) on Windows. ### COMPOSER_CAFILE @@ -919,7 +919,7 @@ The `COMPOSER_HOME` var allows you to change the Composer home directory. This is a hidden, global (per-user on the machine) directory that is shared between all projects. -By default it points to `C:\Users\\AppData\Roaming\Composer` on Windows +By default, it points to `C:\Users\\AppData\Roaming\Composer` on Windows and `/Users//.composer` on macOS. On \*nix systems that follow the [XDG Base Directory Specifications](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html), it points to `$XDG_CONFIG_HOME/composer`. On other \*nix systems, it points to @@ -964,8 +964,8 @@ commands) to finish executing. The default value is 300 seconds (5 minutes). ### COMPOSER_ROOT_VERSION -By setting this var you can specify the version of the root package, if it can -not be guessed from VCS info and is not present in `composer.json`. +By setting this var you can specify the version of the root package, if it +cannot be guessed from VCS info and is not present in `composer.json`. ### COMPOSER_VENDOR_DIR @@ -990,13 +990,13 @@ details. ### HTTP_PROXY_REQUEST_FULLURI -If you use a proxy but it does not support the request_fulluri flag, then you +If you use a proxy, but it does not support the request_fulluri flag, then you should set this env var to `false` or `0` to prevent Composer from setting the request_fulluri option. ### HTTPS_PROXY_REQUEST_FULLURI -If you use a proxy but it does not support the request_fulluri flag for HTTPS +If you use a proxy, but it does not support the request_fulluri flag for HTTPS requests, then you should set this env var to `false` or `0` to prevent Composer from setting the request_fulluri option. @@ -1019,7 +1019,7 @@ can also set it to `*` to ignore the proxy for all HTTP requests. If set to `1`, disables network access (best effort). This can be used for debugging or to run Composer on a plane or a starship with poor connectivity. -If set to `prime`, GitHub VCS repositories will prime the cache so it can then be used +If set to `prime`, GitHub VCS repositories will prime the cache, so it can then be used fully offline with `1`. ← [Libraries](02-libraries.md) | [Schema](04-schema.md) → diff --git a/doc/04-schema.md b/doc/04-schema.md index 8bdad86ee..28d0b4ecd 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -123,7 +123,7 @@ Optional. ### homepage -An URL to the website of the project. +A URL to the website of the project. Optional. @@ -184,7 +184,7 @@ An Example for disjunctive licenses: } ``` -Alternatively they can be separated with "or" and enclosed in parenthesis; +Alternatively they can be separated with "or" and enclosed in parentheses; ```json { @@ -192,8 +192,8 @@ Alternatively they can be separated with "or" and enclosed in parenthesis; } ``` -Similarly when multiple licenses need to be applied ("conjunctive license"), -they should be separated with "and" and enclosed in parenthesis. +Similarly, when multiple licenses need to be applied ("conjunctive license"), +they should be separated with "and" and enclosed in parentheses. ### authors @@ -265,8 +265,8 @@ development of new functionality. Each entry consists of the following -* **type:** The type of funding or the platform through which funding can be provided, e.g. patreon, opencollective, tidelift or github. -* **url:** URL to a website with details and a way to fund the package. +* **type:** The type of funding, or the platform through which funding can be provided, e.g. patreon, opencollective, tidelift or github. +* **url:** URL to a website with details, and a way to fund the package. An example: @@ -934,7 +934,7 @@ It can be boolean or a package name/URL pointing to a recommended alternative. Examples: Use `"abandoned": true` to indicates this package is abandoned. -Use `"abandoned": "monolog/monolog"` to indicates this package is abandoned and the +Use `"abandoned": "monolog/monolog"` to indicates this package is abandoned, and the recommended alternative is `monolog/monolog`. Defaults to false. @@ -949,7 +949,7 @@ that will NOT be handled as feature branches. This is an array of strings. If you have non-numeric branch names, for example like "latest", "current", "latest-stable" or something, that do not look like a version number, then Composer handles such branches as feature branches. This means it searches for parent branches, that look like a version -or ends at special branches (like master) and the root package version number becomes the +or ends at special branches (like master), and the root package version number becomes the version of the parent branch or at least master or something. To handle non-numeric named branches as versions instead of searching for a parent branch diff --git a/doc/05-repositories.md b/doc/05-repositories.md index 7a9cc8b46..b32d92690 100644 --- a/doc/05-repositories.md +++ b/doc/05-repositories.md @@ -6,7 +6,7 @@ of repositories are available, and how they work. ## Concepts Before we look at the different types of repositories that exist, we need to -understand some of the basic concepts that Composer is built on. +understand some basic concepts that Composer is built on. ### Package @@ -41,7 +41,7 @@ be preferred. A repository is a package source. It's a list of packages/versions. Composer will look in all your repositories to find the packages your project requires. -By default only the Packagist.org repository is registered in Composer. You can +By default, only the Packagist.org repository is registered in Composer. You can add more repositories to your project by declaring them in `composer.json`. Repositories are only available to the root package and the repositories @@ -122,7 +122,7 @@ It may include any of the other fields specified in the [schema](04-schema.md). The `notify-batch` field allows you to specify a URL that will be called every time a user installs a package. The URL can be either an absolute path -(that will use the same domain as the repository) or a fully qualified URL. +(that will use the same domain as the repository), or a fully qualified URL. An example value: @@ -217,7 +217,7 @@ from these systems. There are a few use cases for this. The most common one is maintaining your own fork of a third party library. If you are using a certain library for your -project and you decide to change something in the library, you will want your +project, and you decide to change something in the library, you will want your project to use the patched version. If the library is on GitHub (this is the case most of the time), you can simply fork it there and push your changes to your fork. After that you update the project's `composer.json`. All you have @@ -316,7 +316,7 @@ Please note: #### BitBucket Driver Configuration -The BitBucket driver uses OAuth to access your private repositories via the BitBucket REST APIs and you will need to create an OAuth consumer to use the driver, please refer to [Atlassian's Documentation](https://confluence.atlassian.com/bitbucket/oauth-on-bitbucket-cloud-238027431.html). You will need to fill the callback url with something to satisfy BitBucket, but the address does not need to go anywhere and is not used by Composer. +The BitBucket driver uses OAuth to access your private repositories via the BitBucket REST APIs, and you will need to create an OAuth consumer to use the driver, please refer to [Atlassian's Documentation](https://confluence.atlassian.com/bitbucket/oauth-on-bitbucket-cloud-238027431.html). You will need to fill the callback url with something to satisfy BitBucket, but the address does not need to go anywhere and is not used by Composer. After creating an OAuth consumer in the BitBucket control panel, you need to setup your auth.json file with the credentials like this (more info [here](https://getcomposer.org/doc/06-config.md#bitbucket-oauth)): @@ -437,7 +437,7 @@ Here is an example for the smarty template engine: } ``` -Typically you would leave the source part off, as you don't really need it. +Typically, you would leave the source part off, as you don't really need it. > **Note**: This repository type has a few limitations and should be avoided > whenever possible: diff --git a/doc/06-config.md b/doc/06-config.md index 30f31b98e..b7e5e8e45 100644 --- a/doc/06-config.md +++ b/doc/06-config.md @@ -192,14 +192,14 @@ metadata for the `git`/`hg` types and to speed up installs. ## cache-files-ttl -Defaults to `15552000` (6 months). Composer caches all dist (zip, tar, ..) +Defaults to `15552000` (6 months). Composer caches all dist (zip, tar, ...) packages that it downloads. Those are purged after six months of being unused by default. This option allows you to tweak this duration (in seconds) or disable it completely by setting it to 0. ## cache-files-maxsize -Defaults to `300MiB`. Composer caches all dist (zip, tar, ..) packages that it +Defaults to `300MiB`. Composer caches all dist (zip, tar, ...) packages that it downloads. When the garbage collection is periodically ran, this is the maximum size the cache will be able to use. Older (less used) files will be removed first until the cache fits. diff --git a/doc/articles/aliases.md b/doc/articles/aliases.md index d36eb11ff..c03ec52f2 100644 --- a/doc/articles/aliases.md +++ b/doc/articles/aliases.md @@ -107,5 +107,5 @@ and alias it to `1.0.x-dev`. > inline-aliased again in A's `composer.json`. > **Note:** Inline aliasing should be avoided, especially for published -> packages/libraries. If you found a bug, try and get your fix merged upstream. +> packages/libraries. If you found a bug, try to get your fix merged upstream. > This helps to avoid issues for users of your package. diff --git a/doc/articles/autoloader-optimization.md b/doc/articles/autoloader-optimization.md index 11faac8a7..8c8fc4fae 100644 --- a/doc/articles/autoloader-optimization.md +++ b/doc/articles/autoloader-optimization.md @@ -48,7 +48,7 @@ There are no real trade-offs with this method. It should always be enabled in production. The only issue is it does not keep track of autoload misses (i.e. when -it can not find a given class), so those fallback to PSR-4 rules and can still +it cannot find a given class), so those fallback to PSR-4 rules and can still result in slow filesystem checks. To solve this issue two Level 2 optimization options exist, and you can decide to enable either if you have a lot of class_exists checks that are done for classes that do not exist in your project. @@ -78,7 +78,7 @@ also means that in case a class is generated at runtime for some reason, it will not be allowed to be autoloaded. If your project or any of your dependencies does that then you might experience "class not found" issues in production. Enable this with care. -> Note: This can not be combined with Level 2/B optimizations. You have to choose one as +> Note: This cannot be combined with Level 2/B optimizations. You have to choose one as > they address the same issue in different ways. ## Optimization Level 2/B: APCu cache @@ -97,15 +97,15 @@ This option adds an APCu cache as a fallback for the class map. It will not automatically generate the class map though, so you should still enable Level 1 optimizations manually if you so desire. -Whether a class is found or not, that fact is always cached in APCu so it can be +Whether a class is found or not, that fact is always cached in APCu, so it can be returned quickly on the next request. ### Trade-offs This option requires APCu which may or may not be available to you. It also -uses APCu memory for autoloading purposes, but it is safe to use and can not +uses APCu memory for autoloading purposes, but it is safe to use and cannot result in classes not being found like the authoritative class map optimization above. -> Note: This can not be combined with Level 2/A optimizations. You have to choose one as +> Note: This cannot be combined with Level 2/A optimizations. You have to choose one as > they address the same issue in different ways. diff --git a/doc/articles/custom-installers.md b/doc/articles/custom-installers.md index 791c04956..421a49cb7 100644 --- a/doc/articles/custom-installers.md +++ b/doc/articles/custom-installers.md @@ -6,7 +6,7 @@ ## Synopsis -At times it may be necessary for a package to require additional actions during +At times, it may be necessary for a package to require additional actions during installation, such as installing packages outside of the default `vendor` library. diff --git a/doc/articles/versions.md b/doc/articles/versions.md index 85c9356d2..060352a79 100644 --- a/doc/articles/versions.md +++ b/doc/articles/versions.md @@ -159,7 +159,7 @@ Example: `~1.2` ### Caret Version Range (^) -The `^` operator behaves very similarly but it sticks closer to semantic +The `^` operator behaves very similarly, but it sticks closer to semantic versioning, and will always allow non-breaking updates. For example `^1.2.3` is equivalent to `>=1.2.3 <2.0.0` as none of the releases until 2.0 should break backwards compatibility. For pre-1.0 versions it also acts with safety diff --git a/doc/faqs/why-are-unbound-version-constraints-a-bad-idea.md b/doc/faqs/why-are-unbound-version-constraints-a-bad-idea.md index 9aef970f9..399a31d74 100644 --- a/doc/faqs/why-are-unbound-version-constraints-a-bad-idea.md +++ b/doc/faqs/why-are-unbound-version-constraints-a-bad-idea.md @@ -5,7 +5,7 @@ A version constraint without an upper bound such as `*`, `>=3.4` or This includes major versions breaking backward compatibility. Once a release of your package is tagged, you cannot tweak its dependencies -anymore in case a dependency breaks BC - you have to do a new release but the +anymore in case a dependency breaks BC - you have to do a new release, but the previous one stays broken. The only good alternative is to define an upper bound on your constraints, diff --git a/doc/faqs/why-can't-composer-load-repositories-recursively.md b/doc/faqs/why-can't-composer-load-repositories-recursively.md index a39aff6fb..1dff52c40 100644 --- a/doc/faqs/why-can't-composer-load-repositories-recursively.md +++ b/doc/faqs/why-can't-composer-load-repositories-recursively.md @@ -24,7 +24,7 @@ their package's packages, etc, then resolve requirements. It could work, but it slows down the initialization a lot since VCS repos can each take a few seconds, and it could end up in a completely broken state since many versions of a package could define the same packages inside a package repository, but with different -dist/source. There are many many ways this could go wrong. +dist/source. There are many ways this could go wrong. - Fetch the repositories of root package, then fetch the repositories of the first level dependencies, then fetch the repositories of their dependencies, etc, From 70f211923b9f72c6994d526b1fda94fd98aa9ad6 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 1 Jul 2020 11:16:18 +0200 Subject: [PATCH 34/59] Add support for list URL in composer repos, fixes #9009 --- UPGRADE-2.0.md | 6 +++ src/Composer/Command/ShowCommand.php | 11 +++--- .../Repository/ComposerRepository.php | 39 +++++++++++++++---- 3 files changed, 43 insertions(+), 13 deletions(-) diff --git a/UPGRADE-2.0.md b/UPGRADE-2.0.md index ff6e6c597..fa2b176f4 100644 --- a/UPGRADE-2.0.md +++ b/UPGRADE-2.0.md @@ -74,3 +74,9 @@ If your repository only has a small number of packages, and you want to avoid th `"providers-api": "https://packagist.org/providers/%package%.json",` The providers-api is optional, but if you implement it it should return packages which provide a given package name, but not the package which has that name. For example https://packagist.org/providers/monolog/monolog.json lists some package which have a "provide" rule for monolog/monolog, but it does not list monolog/monolog itself. + +### list + +This is also optional, it should accept an optional `?filter=xx` query param, which can contain `*` as wildcards matching any substring. + +It must return an array of package names as `{"packageNames": ["a/b", "c/d"]}`. See https://packagist.org/packages/list.json?filter=composer/* for example. diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index b928765f9..c39aba760 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -299,8 +299,9 @@ EOT // list packages $packages = array(); + $packageFilterRegex = null; if (null !== $packageFilter) { - $packageFilter = '{^'.str_replace('\\*', '.*?', preg_quote($packageFilter)).'$}i'; + $packageFilterRegex = '{^'.str_replace('\\*', '.*?', preg_quote($packageFilter)).'$}i'; } $packageListFilter = array(); @@ -342,10 +343,8 @@ EOT $type = 'available'; } if ($repo instanceof ComposerRepository) { - foreach ($repo->getPackageNames() as $name) { - if (!$packageFilter || preg_match($packageFilter, $name)) { - $packages[$type][$name] = $name; - } + foreach ($repo->getPackageNames($packageFilter) as $name) { + $packages[$type][$name] = $name; } } else { foreach ($repo->getPackages() as $package) { @@ -353,7 +352,7 @@ EOT || !is_object($packages[$type][$package->getName()]) || version_compare($packages[$type][$package->getName()]->getVersion(), $package->getVersion(), '<') ) { - if (!$packageFilter || preg_match($packageFilter, $package->getName())) { + if (!$packageFilterRegex || preg_match($packageFilterRegex, $package->getName())) { if (!$packageListFilter || in_array($package->getName(), $packageListFilter, true)) { $packages[$type][$package->getName()] = $package; } diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index f7711390f..941cf08dd 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -58,6 +58,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito protected $providersApiUrl; protected $hasProviders = false; protected $providersUrl; + protected $listUrl; protected $availablePackages; protected $lazyProvidersUrl; protected $providerListing; @@ -288,32 +289,52 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito return parent::getPackages(); } - public function getPackageNames() + public function getPackageNames($packageFilter = null) { - // TODO add getPackageNames to the RepositoryInterface perhaps? With filtering capability embedded? $hasProviders = $this->hasProviders(); + $packageFilterCb = function ($name) { + return true; + }; + if (null !== $packageFilter) { + $packageFilterRegex = '{^'.str_replace('\\*', '.*?', preg_quote($packageFilter)).'$}i'; + $packageFilterCb = function ($name) use ($packageFilterRegex) { + return (bool) preg_match($packageFilterRegex, $name); + }; + } + if ($this->lazyProvidersUrl) { if (is_array($this->availablePackages)) { - return array_keys($this->availablePackages); + return array_filter(array_keys($this->availablePackages), $packageFilterCb); } - // TODO implement new list API endpoint for those repos somehow? + if ($this->listUrl) { + $url = $this->listUrl; + if ($packageFilter) { + $url .= '?filter='.urlencode($packageFilter); + } + + $result = $this->httpDownloader->get($url, $this->options)->decodeJson(); + + return $result['packageNames']; + } if ($this->hasPartialPackages()) { - return array_keys($this->partialPackagesByName); + return array_filter(array_keys($this->partialPackagesByName), $packageFilterCb); } return array(); } if ($hasProviders) { - return $this->getProviderNames(); + return array_filter($this->getProviderNames(), $packageFilterCb); } $names = array(); foreach ($this->getPackages() as $package) { - $names[] = $package->getPrettyName(); + if ($packageFilterCb($package->getName())) { + $names[] = $package->getPrettyName(); + } } return $names; @@ -866,6 +887,10 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito $this->hasProviders = true; } + if (!empty($data['list'])) { + $this->listUrl = $this->canonicalizeUrl($data['list']); + } + if (!empty($data['providers']) || !empty($data['providers-includes'])) { $this->hasProviders = true; } From ee4afa29e77b57ae8d03254149788a30c7a40c39 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 1 Jul 2020 11:17:52 +0200 Subject: [PATCH 35/59] Include dev packages in locked package listings for show command --- src/Composer/Command/ShowCommand.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index c39aba760..6a2219df0 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -175,7 +175,7 @@ EOT $localRepo = $composer->getRepositoryManager()->getLocalRepository(); $locker = $composer->getLocker(); if ($locker->isLocked()) { - $lockedRepo = $locker->getLockedRepository(); + $lockedRepo = $locker->getLockedRepository(true); $installedRepo = new InstalledRepository(array($lockedRepo, $localRepo, $platformRepo)); } else { $installedRepo = new InstalledRepository(array($localRepo, $platformRepo)); @@ -191,7 +191,7 @@ EOT throw new \UnexpectedValueException('A valid composer.json and composer.lock files is required to run this command with --locked'); } $locker = $composer->getLocker(); - $lockedRepo = $locker->getLockedRepository(); + $lockedRepo = $locker->getLockedRepository(true); $installedRepo = new InstalledRepository(array($lockedRepo)); $repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories())); } else { From 1fa6f4c83ca381019451488fd2243eaed6dd389b Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 1 Jul 2020 11:37:38 +0200 Subject: [PATCH 36/59] Add a default signal handler to avoid having the SIGINTs ignored, fixes #9027 --- src/Composer/Console/Application.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 4160a69ac..622938476 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -81,6 +81,13 @@ class Application extends BaseApplication } if (!$shutdownRegistered) { + if (function_exists('pcntl_async_signals') && function_exists('pcntl_signal')) { + pcntl_async_signals(true); + pcntl_signal(SIGINT, function ($sig) { + exit(130); + }); + } + $shutdownRegistered = true; register_shutdown_function(function () { From 212fbc516e3d41c5ebafa46b018d7194751222d2 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 2 Jul 2020 08:12:25 +0200 Subject: [PATCH 37/59] Fix test issues --- .../Test/Fixtures/installer/github-issues-7051.test | 3 +-- tests/Composer/Test/Json/ComposerSchemaTest.php | 7 +++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/Composer/Test/Fixtures/installer/github-issues-7051.test b/tests/Composer/Test/Fixtures/installer/github-issues-7051.test index 55e758441..1c17958ba 100644 --- a/tests/Composer/Test/Fixtures/installer/github-issues-7051.test +++ b/tests/Composer/Test/Fixtures/installer/github-issues-7051.test @@ -103,8 +103,7 @@ Solver Bug Exception caused by analyze on mutli conflict rule reported in GitHub "version": "v2.8.7" } ] - }, - "packagist.org": false + } } } --RUN-- diff --git a/tests/Composer/Test/Json/ComposerSchemaTest.php b/tests/Composer/Test/Json/ComposerSchemaTest.php index a6f85d4cf..9a7902dad 100644 --- a/tests/Composer/Test/Json/ComposerSchemaTest.php +++ b/tests/Composer/Test/Json/ComposerSchemaTest.php @@ -40,10 +40,9 @@ class ComposerSchemaTest extends TestCase public function testRequiredProperties() { $json = '{ }'; - $this->assertEquals(array( - array('property' => 'name', 'message' => 'The property name is required', 'constraint' => 'required'), - array('property' => 'description', 'message' => 'The property description is required', 'constraint' => 'required'), - ), $this->check($json)); + $result = $this->check($json); + $this->assertContains(array('property' => 'name', 'message' => 'The property name is required', 'constraint' => 'required'), $result); + $this->assertContains(array('property' => 'description', 'message' => 'The property description is required', 'constraint' => 'required'), $result); $json = '{ "name": "vendor/package" }'; $this->assertEquals(array( From d7b124850850a4fe0e80f64f975b3d14a58209c1 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 4 Jul 2020 12:00:37 +0200 Subject: [PATCH 38/59] Create pool before reading package ids as the pool creation reassigns package ids, fixes #9034 --- src/Composer/Command/ShowCommand.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index 6a2219df0..9cd67c89f 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -567,6 +567,7 @@ EOT $matchedPackage = null; $versions = array(); $matches = $repositorySet->findPackages($name, $constraint); + $pool = $repositorySet->createPoolForPackage($name); foreach ($matches as $index => $package) { // select an exact match if it is in the installed repo and no specific version was required if (null === $version && $installedRepo->hasPackage($package)) { @@ -577,8 +578,6 @@ EOT $matches[$index] = $package->getId(); } - $pool = $repositorySet->createPoolForPackage($name); - // select preferred package according to policy rules if (!$matchedPackage && $matches && $preferred = $policy->selectPreferredPackages($pool, $matches)) { $matchedPackage = $pool->literalToPackage($preferred[0]); From d2d3aa84948e3511f0f730fd9fa0baeeab8b9867 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 7 Jul 2020 21:11:20 +0200 Subject: [PATCH 39/59] Fix parsing of # in funding links, fixes composer/packagist#1097, closes #9035 --- src/Composer/Repository/Vcs/GitHubDriver.php | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php index 549625fa9..7abda8164 100644 --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -214,20 +214,19 @@ class GitHubDriver extends VcsDriver $result = array(); $key = null; foreach (preg_split('{\r?\n}', $funding) as $line) { - $line = preg_replace('{#.*}', '', $line); $line = trim($line); if (preg_match('{^(\w+)\s*:\s*(.+)$}', $line, $match)) { - if (preg_match('{^\[.*\]$}', $match[2])) { - foreach (array_map('trim', preg_split('{[\'"]?\s*,\s*[\'"]?}', substr($match[2], 1, -1))) as $item) { + if (preg_match('{^\[(.*)\](?:\s*#.*)?$}', $match[2], $match2)) { + foreach (array_map('trim', preg_split('{[\'"]?\s*,\s*[\'"]?}', $match2[1])) as $item) { $result[] = array('type' => $match[1], 'url' => trim($item, '"\' ')); } - } else { - $result[] = array('type' => $match[1], 'url' => trim($match[2], '"\' ')); + } elseif (preg_match('{^([^#].*?)(\s+#.*)?$}', $match[2], $match2)) { + $result[] = array('type' => $match[1], 'url' => trim($match2[1], '"\' ')); } $key = null; - } elseif (preg_match('{^(\w+)\s*:$}', $line, $match)) { + } elseif (preg_match('{^(\w+)\s*:\s*#\s*$}', $line, $match)) { $key = $match[1]; - } elseif ($key && preg_match('{^-\s*(.+)$}', $line, $match)) { + } elseif ($key && preg_match('{^-\s*(.+)(\s+#.*)?$}', $line, $match)) { $result[] = array('type' => $key, 'url' => trim($match[1], '"\' ')); } } From 23c32ed6d3e980114eeaf9193fe378620ec39de6 Mon Sep 17 00:00:00 2001 From: Tomek Date: Tue, 14 Jul 2020 23:06:20 +0100 Subject: [PATCH 40/59] Fix #9033 - inconsistent behavior with minor-only flag --- src/Composer/Command/ShowCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index 9cd67c89f..1396a5c7a 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -248,7 +248,7 @@ EOT } else { $latestPackage = null; if ($input->getOption('latest')) { - $latestPackage = $this->findLatestPackage($package, $composer, $platformRepo); + $latestPackage = $this->findLatestPackage($package, $composer, $platformRepo, $input->getOption('minor-only')); } if ($input->getOption('outdated') && $input->getOption('strict') && $latestPackage && $latestPackage->getFullPrettyVersion() !== $package->getFullPrettyVersion() && !$latestPackage->isAbandoned()) { $exitCode = 1; From 10590dc519fe9b12c8333452c75904155214ca3e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 15 Jul 2020 10:53:44 +0200 Subject: [PATCH 41/59] Improve support for XDG and default to XDG config dir if both that and ~/.composer are available, fixes #9045 --- UPGRADE-2.0.md | 1 + src/Composer/Factory.php | 30 ++++++++++++++++++++++++------ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/UPGRADE-2.0.md b/UPGRADE-2.0.md index fa2b176f4..df2b41359 100644 --- a/UPGRADE-2.0.md +++ b/UPGRADE-2.0.md @@ -5,6 +5,7 @@ - The new platform-check feature means that Composer checks the runtime PHP version and available extensions to ensure they match the project dependencies. If a mismatch is found, it exits with error details to make sure problems are not overlooked. To avoid issues when deploying to production it is recommended to run `composer check-platform-reqs` with the production PHP process as part of your build or deployment process. - If a package exists in a higher priority repository, it will now be entirely ignored in lower priority repositories. See [repository priorities](https://getcomposer.org/repoprio) for details. - Invalid PSR-0 / PSR-4 class configurations will not autoload anymore in optimized-autoloader mode, as per the warnings introduced in 1.10 +- On linux systems supporting the XDG Base Directory Specification, Composer will now prefer using XDG_CONFIG_DIR/composer over ~/.composer if both are available (1.x used ~/.composer first) - Package names now must comply to our [naming guidelines](doc/04-schema.md#name) or Composer will abort, as per the warnings introduced in 1.8.1 - Deprecated --no-suggest flag as it is not needed anymore - PEAR support (repository, downloader, etc.) has been removed diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 8eb2d24cf..e0947b46a 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -69,18 +69,32 @@ class Factory } $userDir = self::getUserDir(); - if (is_dir($userDir . '/.composer')) { - return $userDir . '/.composer'; - } + $dirs = array(); if (self::useXdg()) { // XDG Base Directory Specifications - $xdgConfig = getenv('XDG_CONFIG_HOME') ?: $userDir . '/.config'; + $xdgConfig = getenv('XDG_CONFIG_HOME'); + if (!$xdgConfig && is_dir('/etc/xdg')) { + $xdgConfig = '/etc/xdg'; + } + if (!$xdgConfig) { + $xdgConfig = $userDir . '/.config'; + } - return $xdgConfig . '/composer'; + $dirs[] = $xdgConfig . '/composer'; } - return $userDir . '/.composer'; + $dirs[] = $userDir . '/.composer'; + + // select first dir which exists of: $XDG_CONFIG_HOME/composer or ~/.composer + foreach ($dirs as $dir) { + if (is_dir($dir)) { + return $dir; + } + } + + // if none exists, we default to first defined one (XDG one if system uses it, or ~/.composer otherwise) + return $dirs[0]; } /** @@ -644,6 +658,10 @@ class Factory } } + if (is_dir('/etc/xdg')) { + return true; + } + return false; } From fb0ad7c900500f6e3d5451f79baad8c86ee725d4 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 15 Jul 2020 16:18:21 +0100 Subject: [PATCH 42/59] GitLab: clarify interactive auth prompt --- src/Composer/Util/GitLab.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Composer/Util/GitLab.php b/src/Composer/Util/GitLab.php index 7a69ad251..b3cb421ca 100644 --- a/src/Composer/Util/GitLab.php +++ b/src/Composer/Util/GitLab.php @@ -104,7 +104,7 @@ class GitLab } $this->io->writeError(sprintf('A token will be created and stored in "%s", your password will never be stored', $this->config->getAuthConfigSource()->getName())); - $this->io->writeError('To revoke access to this token you can visit '.$originUrl.'/profile/applications'); + $this->io->writeError('To revoke access to this token you can visit '.$scheme.'://'.$originUrl.'/profile/applications'); $attemptCounter = 0; @@ -116,12 +116,17 @@ class GitLab // 403 is max login attempts exceeded if (in_array($e->getCode(), array(403, 401))) { if (401 === $e->getCode()) { - $this->io->writeError('Bad credentials.'); + $response = json_decode($e->getResponse(), true); + if (isset($response['error']) && $response['error'] === 'invalid_grant') { + $this->io->writeError('Bad credentials. If you have two factor authentication enabled you will have to manually create a personal access token'); + } else { + $this->io->writeError('Bad credentials.'); + } } else { $this->io->writeError('Maximum number of login attempts exceeded. Please try again later.'); } - $this->io->writeError('You can also manually create a personal token at '.$scheme.'://'.$originUrl.'/profile/personal_access_tokens'); + $this->io->writeError('You can also manually create a personal access token enabling the "read_api" scope at '.$scheme.'://'.$originUrl.'/profile/personal_access_tokens'); $this->io->writeError('Add it using "composer config --global --auth gitlab-token.'.$originUrl.' "'); continue; From cae913c434818e1318a336e129bfe4e58f5b8e0f Mon Sep 17 00:00:00 2001 From: johnstevenson Date: Thu, 7 May 2020 19:10:36 +0100 Subject: [PATCH 43/59] Add Windows UAC elevation to self-update command If self-update fails on Windows due to file permission issues, a .vbs script is used to elevate a call to the cmd.exe `move` command. Unfortunately it is not possible to know if the user cancelled the UAC prompt using this method - it is possible using a Powershell script, but flashing hidden windows make this a less desirable option. The only downside is that a UAC invoked process is asynchronous, so a 300 millisecond timeout is used to allow cmd.exe to do its stuff. Therefore if the OS is busy the script may finish first and incorrectly report that the file has not been written. --- src/Composer/Command/SelfUpdateCommand.php | 195 ++++++++++++++++++--- 1 file changed, 167 insertions(+), 28 deletions(-) diff --git a/src/Composer/Command/SelfUpdateCommand.php b/src/Composer/Command/SelfUpdateCommand.php index 925270ade..4ac144ea8 100644 --- a/src/Composer/Command/SelfUpdateCommand.php +++ b/src/Composer/Command/SelfUpdateCommand.php @@ -16,6 +16,7 @@ use Composer\Composer; use Composer\Factory; use Composer\Config; use Composer\Util\Filesystem; +use Composer\Util\Platform; use Composer\SelfUpdate\Keys; use Composer\SelfUpdate\Versions; use Composer\IO\IOInterface; @@ -106,6 +107,11 @@ EOT return $this->fetchKeys($io, $config); } + // ensure composer.phar location is accessible + if (!file_exists($localFilename)) { + throw new FilesystemException('Composer update failed: the "'.$localFilename.'" is not accessible'); + } + // check if current dir is writable and if not try the cache dir from settings $tmpDir = is_writable(dirname($localFilename)) ? dirname($localFilename) : $cacheDir; @@ -248,10 +254,8 @@ TAGSPUBKEY $this->cleanBackups($rollbackDir); } - if ($err = $this->setLocalPhar($localFilename, $tempFilename, $backupFile)) { + if (!$this->setLocalPhar($localFilename, $tempFilename, $backupFile)) { @unlink($tempFilename); - $io->writeError('The file is corrupted ('.$err->getMessage().').'); - $io->writeError('Please re-run the self-update command to try again.'); return 1; } @@ -331,9 +335,7 @@ TAGSPUBKEY $io = $this->getIO(); $io->writeError(sprintf("Rolling back to version %s.", $rollbackVersion)); - if ($err = $this->setLocalPhar($localFilename, $oldFile)) { - $io->writeError('The backup file was corrupted ('.$err->getMessage().').'); - + if (!$this->setLocalPhar($localFilename, $oldFile)) { return 1; } @@ -341,37 +343,49 @@ TAGSPUBKEY } /** - * @param string $localFilename - * @param string $newFilename - * @param string $backupTarget - * @throws \Exception - * @return \UnexpectedValueException|\PharException|null + * Checks if the downloaded/rollback phar is valid then moves it + * + * @param string $localFilename The composer.phar location + * @param string $newFilename The downloaded or backup phar + * @param string $backupTarget The filename to use for the backup + * @throws \FilesystemException If the file cannot be moved + * @return bool Whether the phar is valid and has been moved */ protected function setLocalPhar($localFilename, $newFilename, $backupTarget = null) { + $io = $this->getIO(); + @chmod($newFilename, fileperms($localFilename)); + + // check phar validity + if (!$this->validatePhar($newFilename, $error)) { + $io->writeError('The '.($backupTarget ? 'update' : 'backup').' file is corrupted ('.$error.')'); + + if ($backupTarget) { + $io->writeError('Please re-run the self-update command to try again.'); + } + + return false; + } + + // copy current file into backups dir + if ($backupTarget) { + @copy($localFilename, $backupTarget); + } + try { - @chmod($newFilename, fileperms($localFilename)); - if (!ini_get('phar.readonly')) { - // test the phar validity - $phar = new \Phar($newFilename); - // free the variable to unlock the file - unset($phar); - } - - // copy current file into installations dir - if ($backupTarget && file_exists($localFilename)) { - @copy($localFilename, $backupTarget); - } - rename($newFilename, $localFilename); - return null; + return true; } catch (\Exception $e) { - if (!$e instanceof \UnexpectedValueException && !$e instanceof \PharException) { - throw $e; + // see if we can run this operation as an Admin on Windows + if (!is_writable(dirname($localFilename)) + && $io->isInteractive() + && $this->isWindowsNonAdminUser($isCygwin)) { + return $this->tryAsWindowsAdmin($localFilename, $newFilename, $isCygwin); } - return $e; + $action = 'Composer '.($backupTarget ? 'update' : 'rollback'); + throw new FilesystemException($action.' failed: "'.$localFilename.'" could not be written.'.PHP_EOL.$e->getMessage()); } } @@ -414,4 +428,129 @@ TAGSPUBKEY return $finder; } + + /** + * Validates the downloaded/backup phar file + * + * @param string $pharFile The downloaded or backup phar + * @param null|string $error Set by method on failure + * + * Code taken from getcomposer.org/installer. Any changes should be made + * there and replicated here + * + * @return bool If the operation succeeded + * @throws \Exception + */ + protected function validatePhar($pharFile, &$error) + { + if (ini_get('phar.readonly')) { + return true; + } + + try { + // Test the phar validity + $phar = new \Phar($pharFile); + // Free the variable to unlock the file + unset($phar); + $result = true; + } catch (\Exception $e) { + if (!$e instanceof \UnexpectedValueException && !$e instanceof \PharException) { + throw $e; + } + $error = $e->getMessage(); + $result = false; + } + + return $result; + } + + /** + * Returns true if this is a non-admin Windows user account + * + * @param null|string $isCygwin Set by method + * @return bool + */ + protected function isWindowsNonAdminUser(&$isCygwin) + { + $isCygwin = preg_match('/cygwin/i', php_uname()); + + if (!$isCygwin && !Platform::isWindows()) { + return false; + } + + // fltmc.exe manages filter drivers and errors without admin privileges + $command = sprintf('%sfltmc.exe filters', $isCygwin ? 'cmd.exe /c ' : ''); + exec($command, $output, $exitCode); + + return $exitCode !== 0; + } + + /** + * Invokes a UAC prompt to update composer.phar as an admin + * + * Uses a .vbs script to elevate and run the cmd.exe move command. + * + * @param string $localFilename The composer.phar location + * @param string $newFilename The downloaded or backup phar + * @param bool $isCygwin Whether we are running on Cygwin + * @return bool Whether composer.phar has been updated + */ + protected function tryAsWindowsAdmin($localFilename, $newFilename, $isCygwin) + { + $io = $this->getIO(); + + $io->writeError('Unable to write "'.$localFilename.'". Access is denied.'); + $helpMessage = 'Please run the self-update command as an Administrator.'; + $question = 'Complete this operation with Administrator priviledges [Y,n]? '; + + if (!$io->askConfirmation($question, false)) { + $io->writeError('Operation cancelled. '.$helpMessage.''); + + return false; + } + + $tmpFile = tempnam(sys_get_temp_dir(), ''); + $script = $tmpFile.'.vbs'; + rename($tmpFile, $script); + + $checksum = hash_file('sha256', $newFilename); + + // format the file names for cmd.exe + if ($isCygwin) { + $source = exec(sprintf("cygpath -w '%s'", $newFilename)); + $destination = exec(sprintf("cygpath -w '%s'", $localFilename)); + } else { + // cmd's internal move is fussy about backslashes + $source = str_replace('/', '\\', $newFilename); + $destination = str_replace('/', '\\', $localFilename); + } + + $vbs = <<writeError('Operation succeeded.'); + } else { + $io->writeError('Operation failed (file not written). '.$helpMessage.''); + }; + + return $result; + } } From 272654d6e22fe31af4ed035c235f89aa895f7ad8 Mon Sep 17 00:00:00 2001 From: johnstevenson Date: Thu, 9 Jul 2020 22:03:49 +0100 Subject: [PATCH 44/59] Fixed spelling mistake --- src/Composer/Command/SelfUpdateCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/SelfUpdateCommand.php b/src/Composer/Command/SelfUpdateCommand.php index 4ac144ea8..9200734a6 100644 --- a/src/Composer/Command/SelfUpdateCommand.php +++ b/src/Composer/Command/SelfUpdateCommand.php @@ -501,7 +501,7 @@ TAGSPUBKEY $io->writeError('Unable to write "'.$localFilename.'". Access is denied.'); $helpMessage = 'Please run the self-update command as an Administrator.'; - $question = 'Complete this operation with Administrator priviledges [Y,n]? '; + $question = 'Complete this operation with Administrator privileges [Y,n]? '; if (!$io->askConfirmation($question, false)) { $io->writeError('Operation cancelled. '.$helpMessage.''); From 57f91d01c7d6ae9a3ce6ffa577d7fb16e94d4a8f Mon Sep 17 00:00:00 2001 From: johnstevenson Date: Fri, 10 Jul 2020 11:31:18 +0100 Subject: [PATCH 45/59] Fix doc comment --- src/Composer/Command/SelfUpdateCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/SelfUpdateCommand.php b/src/Composer/Command/SelfUpdateCommand.php index 9200734a6..031e4d253 100644 --- a/src/Composer/Command/SelfUpdateCommand.php +++ b/src/Composer/Command/SelfUpdateCommand.php @@ -467,7 +467,7 @@ TAGSPUBKEY /** * Returns true if this is a non-admin Windows user account * - * @param null|string $isCygwin Set by method + * @param null|bool $isCygwin Set by method * @return bool */ protected function isWindowsNonAdminUser(&$isCygwin) From 6cb4dc41b81e62d46bd13476cc05390596f2d122 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 16 Jul 2020 12:26:48 +0200 Subject: [PATCH 46/59] Fix bitbucket detection of redirects to login page, fixes #9041 --- src/Composer/Util/RemoteFilesystem.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index f9113d03f..cc4cb61f4 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -404,7 +404,7 @@ class RemoteFilesystem if ($originUrl === 'bitbucket.org' && !$this->isPublicBitBucketDownload($fileUrl) && substr($fileUrl, -4) === '.zip' - && (!$locationHeader || substr($locationHeader, -4) !== '.zip') + && (!$locationHeader || substr(parse_url($locationHeader, PHP_URL_PATH), -4) !== '.zip') && $contentType && preg_match('{^text/html\b}i', $contentType) ) { $result = false; From eec2f8d4232e4ddadbe284cb498e1c39ce5e191f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 16 Jul 2020 12:37:54 +0200 Subject: [PATCH 47/59] Remove highest/lowest builds for 1.10 maintenance branch --- .github/workflows/continuous-integration.yml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 2b4afbf1b..b9dabd175 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -38,14 +38,6 @@ jobs: dependencies: highest os: ubuntu-latest experimental: false - - php-version: 5.3 - dependencies: lowest - os: ubuntu-latest - experimental: false - - php-version: 7.4 - dependencies: highest - os: ubuntu-latest - experimental: true # TODO fix build errors there if possible - php-version: 7.4 os: windows-latest dependencies: locked @@ -54,10 +46,6 @@ jobs: os: macos-latest dependencies: locked experimental: false - - php-version: 8.0 - dependencies: highest - os: ubuntu-latest - experimental: true steps: - name: "Checkout" From bf59f536d616f5a65e554602bf46217ebd9d27d0 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 16 Jul 2020 12:53:45 +0200 Subject: [PATCH 48/59] Prepare 1.10.9 release --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f80d676df..388832a6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +### [1.10.9] 2020-07-16 + + * Fixed Bitbucket redirect loop when credentials are outdated + * Fixed GitLab auth prompt wording + * Fixed `self-update` handling of files requiring admin permissions to write to on Windows (it now does a UAC prompt) + * Fixed parsing issues in funding.yml files + ### [1.10.8] 2020-06-24 * Fixed compatibility issue with git being configured to show signatures by default @@ -866,6 +873,7 @@ * Initial release +[1.10.9]: https://github.com/composer/composer/compare/1.10.8...1.10.9 [1.10.8]: https://github.com/composer/composer/compare/1.10.7...1.10.8 [1.10.7]: https://github.com/composer/composer/compare/1.10.6...1.10.7 [1.10.6]: https://github.com/composer/composer/compare/1.10.5...1.10.6 From 89d35af83d0daeb0303cf79cf59e06aa37f6b559 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 16 Jul 2020 13:59:49 +0200 Subject: [PATCH 49/59] Mark transaction classes internal, refs #8827 --- src/Composer/DependencyResolver/LocalRepoTransaction.php | 1 + src/Composer/DependencyResolver/LockTransaction.php | 1 + src/Composer/DependencyResolver/Transaction.php | 1 + 3 files changed, 3 insertions(+) diff --git a/src/Composer/DependencyResolver/LocalRepoTransaction.php b/src/Composer/DependencyResolver/LocalRepoTransaction.php index b22f555e5..de30cc977 100644 --- a/src/Composer/DependencyResolver/LocalRepoTransaction.php +++ b/src/Composer/DependencyResolver/LocalRepoTransaction.php @@ -23,6 +23,7 @@ use Composer\Semver\Constraint\Constraint; /** * @author Nils Adermann + * @internal */ class LocalRepoTransaction extends Transaction { diff --git a/src/Composer/DependencyResolver/LockTransaction.php b/src/Composer/DependencyResolver/LockTransaction.php index 0e0eb0c7a..fbe543522 100644 --- a/src/Composer/DependencyResolver/LockTransaction.php +++ b/src/Composer/DependencyResolver/LockTransaction.php @@ -22,6 +22,7 @@ use Composer\Test\Repository\ArrayRepositoryTest; /** * @author Nils Adermann + * @internal */ class LockTransaction extends Transaction { diff --git a/src/Composer/DependencyResolver/Transaction.php b/src/Composer/DependencyResolver/Transaction.php index a919238f6..f03419719 100644 --- a/src/Composer/DependencyResolver/Transaction.php +++ b/src/Composer/DependencyResolver/Transaction.php @@ -19,6 +19,7 @@ use Composer\Repository\PlatformRepository; /** * @author Nils Adermann + * @internal */ class Transaction { From 11892070d19b40053a289bda94bc88248dcd8edf Mon Sep 17 00:00:00 2001 From: Rob Bast Date: Mon, 29 Jun 2020 13:11:53 +0200 Subject: [PATCH 50/59] Create an issue @ Docker repository on tag --- .github/workflows/release.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a67b4e6e5..7255735d8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -56,3 +56,20 @@ jobs: asset_path: ./composer.phar asset_name: composer.phar asset_content_type: application/octet-stream + + # This step requires a secret token with `pull` access to composer/docker. The default + # secrets.GITHUB_TOKEN is scoped to this repository only which is not sufficient. + - name: "Open issue @ Docker repository" + uses: actions/github-script@v2 + with: + github-token: ${{ secrets.PUBLIC_REPO_ACCESS_TOKEN }} + script: | + // github.ref value looks like 'refs/tags/TAG', cleanup + const tag = "${{ github.ref }}".replace(/refs\/tags\//, ''); + // create new issue on Docker repository + github.issues.create({ + owner: "${{ github.repository_owner }}", + repo: "docker", + title: `New Composer tag: ${ tag }`, + body: `https://github.com/${{ github.repository }}/releases/tag/${ tag }`, + }); From 7cecc6ad3f0b48c46bc2b2eb0aa9a89aabf9e8ba Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 16 Jul 2020 14:59:49 +0200 Subject: [PATCH 51/59] Avoid attempting to use /etc/xdg as base config home, fixes #9053, refs #9045 --- src/Composer/Factory.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index e0947b46a..7fd9af344 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -74,9 +74,6 @@ class Factory if (self::useXdg()) { // XDG Base Directory Specifications $xdgConfig = getenv('XDG_CONFIG_HOME'); - if (!$xdgConfig && is_dir('/etc/xdg')) { - $xdgConfig = '/etc/xdg'; - } if (!$xdgConfig) { $xdgConfig = $userDir . '/.config'; } From b4ecca5d0b85999deb227a70072a465b2a3ffddf Mon Sep 17 00:00:00 2001 From: Shaun Johnston Date: Thu, 16 Jul 2020 23:34:30 +1000 Subject: [PATCH 52/59] Supply event dispatcher in Create Project Command This update allows composer plugins to intercept the create-project command to inject additional functionality such as providing additional remote filesystem handling. --- src/Composer/Command/CreateProjectCommand.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 067fb9a40..6e41e64ac 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -39,6 +39,7 @@ use Composer\Json\JsonFile; use Composer\Config\JsonConfigSource; use Composer\Util\Filesystem; use Composer\Package\Version\VersionParser; +use Composer\EventDispatcher\EventDispatcher; /** * Install a package as new project into new directory. @@ -286,8 +287,12 @@ EOT $config->merge(array('config' => array('secure-http' => false))); } + $composer = Factory::create($io, $config->all(), $disablePlugins); + $eventDispatcher = $composer->getEventDispatcher(); + if (null === $repository) { - $sourceRepo = new CompositeRepository(RepositoryFactory::defaultRepos($io, $config)); + $rm = RepositoryFactory::manager($io, $config, $eventDispatcher, Factory::createRemoteFilesystem($io, $config)); + $sourceRepo = new CompositeRepository(RepositoryFactory::defaultRepos($io, $config, $rm)); } else { $sourceRepo = RepositoryFactory::fromString($io, $config, $repository, true); } @@ -384,7 +389,7 @@ EOT $package = $package->getAliasOf(); } - $dm = $this->createDownloadManager($io, $config); + $dm = $this->createDownloadManager($io, $config, $eventDispatcher); $dm->setPreferSource($preferSource) ->setPreferDist($preferDist) ->setOutputProgress(!$noProgress); @@ -409,11 +414,11 @@ EOT return $installedFromVcs; } - protected function createDownloadManager(IOInterface $io, Config $config) + protected function createDownloadManager(IOInterface $io, Config $config, EventDispatcher $eventDispatcher) { $factory = new Factory(); - return $factory->createDownloadManager($io, $config); + return $factory->createDownloadManager($io, $config, $eventDispatcher); } protected function createInstallationManager() From 6869a1a5cb8793303738f874d15549e961de794b Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 16 Jul 2020 15:36:17 +0200 Subject: [PATCH 53/59] Clean up a little to make impl less invasive and to handle replacers/providers --- src/Composer/Command/ShowCommand.php | 65 ++++++++++------------------ 1 file changed, 22 insertions(+), 43 deletions(-) diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index 02b709c2d..08b53cef6 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -22,6 +22,7 @@ use Composer\Package\Version\VersionParser; use Composer\Package\Version\VersionSelector; use Composer\Plugin\CommandEvent; use Composer\Plugin\PluginEvents; +use Composer\Repository\InstalledArrayRepository; use Composer\Repository\ComposerRepository; use Composer\Repository\CompositeRepository; use Composer\Repository\PlatformRepository; @@ -196,17 +197,18 @@ EOT $installedRepo = new InstalledRepository(array($lockedRepo)); $repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories())); } else { - $repos = $installedRepo = new InstalledRepository(array($this->getComposer()->getRepositoryManager()->getLocalRepository())); - $root = $composer->getPackage(); - $repo = $composer->getRepositoryManager()->getLocalRepository(); + // --installed / default case + if (!$composer) { + $composer = $this->getComposer(); + } + $rootPkg = $composer->getPackage(); + $repos = $installedRepo = new InstalledRepository(array($composer->getRepositoryManager()->getLocalRepository())); if ($input->getOption('no-dev')) { - $packages = $this->filterRequiredPackages($repo, $root); - } else { - $packages = $this->appendPackages($repo->getPackages(), array()); + $packages = $this->filterRequiredPackages($installedRepo, $rootPkg); + $repos = $installedRepo = new InstalledRepository(array(new InstalledArrayRepository(array_map(function ($pkg) { return clone $pkg; }, $packages)))); } - $packageNames = array_keys($packages); - $rootPkg = $this->getComposer()->getPackage(); + if (!$installedRepo->getPackages() && ($rootPkg->getRequires() || $rootPkg->getDevRequires())) { $io->writeError('No dependencies installed. Try running composer install or update.'); } @@ -358,13 +360,7 @@ EOT } } } else { - $root = $composer->getPackage(); - if ($input->getOption('no-dev')) { - $packageList = $this->filterRequiredPackages($repo, $root); - } else { - $packageList = $this->appendPackages($repo->getPackages(), array()); - } - foreach ($packageList as $package) { + foreach ($repo->getPackages() as $package) { if (!isset($packages[$type][$package->getName()]) || !is_object($packages[$type][$package->getName()]) || version_compare($packages[$type][$package->getName()]->getVersion(), $package->getVersion(), '<') @@ -1239,6 +1235,7 @@ EOT return $this->repositorySet; } + /** * Find package requires and child requires * @@ -1249,36 +1246,18 @@ EOT */ private function filterRequiredPackages(RepositoryInterface $repo, PackageInterface $package, $bucket = array()) { - $requires = array_keys($package->getRequires()); + $requires = $package->getRequires(); - $packageListNames = array_keys($bucket); - $packages = array_filter( - $repo->getPackages(), - function ($package) use ($requires, $packageListNames) { - return in_array($package->getName(), $requires, true) && ! in_array($package->getName(), $packageListNames, true); + foreach ($repo->getPackages() as $candidate) { + foreach ($candidate->getNames() as $name) { + if (isset($requires[$name])) { + if (!in_array($candidate, $bucket, true)) { + $bucket[] = $candidate; + $bucket = $this->filterRequiredPackages($repo, $candidate, $bucket); + } + break; + } } - ); - - $bucket = $this->appendPackages($packages, $bucket); - - foreach ($packages as $requiredPackage) { - $bucket = $this->filterRequiredPackages($repo, $requiredPackage, $bucket); - } - - return $bucket; - } - - /** - * Adds packages to the package list - * - * @param array $packages the list of packages to add - * @param array $bucket the list to add packages to - * @return array - */ - public function appendPackages(array $packages, array $bucket) - { - foreach ($packages as $package) { - $bucket[$package->getName()] = $package; } return $bucket; From 8ddbae358dcbac26a948046675f4a5216fb22a58 Mon Sep 17 00:00:00 2001 From: johnstevenson Date: Thu, 16 Jul 2020 17:14:10 +0100 Subject: [PATCH 54/59] Remove cygwin php handling in UAC elevation When called from a native shell and using cygwin PHP, cygpath translates `/tmp` to `User\AppData\Local\Temp`, rather than `/cygdrive/.../tmp`. This change does not affect using windows PHP from a Cygwin shell. --- src/Composer/Command/SelfUpdateCommand.php | 40 ++++++---------------- 1 file changed, 10 insertions(+), 30 deletions(-) diff --git a/src/Composer/Command/SelfUpdateCommand.php b/src/Composer/Command/SelfUpdateCommand.php index 031e4d253..5ac7fa1f1 100644 --- a/src/Composer/Command/SelfUpdateCommand.php +++ b/src/Composer/Command/SelfUpdateCommand.php @@ -380,8 +380,8 @@ TAGSPUBKEY // see if we can run this operation as an Admin on Windows if (!is_writable(dirname($localFilename)) && $io->isInteractive() - && $this->isWindowsNonAdminUser($isCygwin)) { - return $this->tryAsWindowsAdmin($localFilename, $newFilename, $isCygwin); + && $this->isWindowsNonAdminUser()) { + return $this->tryAsWindowsAdmin($localFilename, $newFilename); } $action = 'Composer '.($backupTarget ? 'update' : 'rollback'); @@ -467,20 +467,16 @@ TAGSPUBKEY /** * Returns true if this is a non-admin Windows user account * - * @param null|bool $isCygwin Set by method * @return bool */ - protected function isWindowsNonAdminUser(&$isCygwin) + protected function isWindowsNonAdminUser() { - $isCygwin = preg_match('/cygwin/i', php_uname()); - - if (!$isCygwin && !Platform::isWindows()) { + if (!Platform::isWindows()) { return false; } // fltmc.exe manages filter drivers and errors without admin privileges - $command = sprintf('%sfltmc.exe filters', $isCygwin ? 'cmd.exe /c ' : ''); - exec($command, $output, $exitCode); + exec('fltmc.exe filters', $output, $exitCode); return $exitCode !== 0; } @@ -492,10 +488,9 @@ TAGSPUBKEY * * @param string $localFilename The composer.phar location * @param string $newFilename The downloaded or backup phar - * @param bool $isCygwin Whether we are running on Cygwin * @return bool Whether composer.phar has been updated */ - protected function tryAsWindowsAdmin($localFilename, $newFilename, $isCygwin) + protected function tryAsWindowsAdmin($localFilename, $newFilename) { $io = $this->getIO(); @@ -515,15 +510,9 @@ TAGSPUBKEY $checksum = hash_file('sha256', $newFilename); - // format the file names for cmd.exe - if ($isCygwin) { - $source = exec(sprintf("cygpath -w '%s'", $newFilename)); - $destination = exec(sprintf("cygpath -w '%s'", $localFilename)); - } else { - // cmd's internal move is fussy about backslashes - $source = str_replace('/', '\\', $newFilename); - $destination = str_replace('/', '\\', $localFilename); - } + // cmd's internal move is fussy about backslashes + $source = str_replace('/', '\\', $newFilename); + $destination = str_replace('/', '\\', $localFilename); $vbs = << Date: Fri, 17 Jul 2020 10:38:46 +0200 Subject: [PATCH 55/59] Add support for multiple --repository additions in create-project, and make --add-repository delete the lock file, fixes #8853 --- doc/03-cli.md | 7 +- src/Composer/Command/CreateProjectCommand.php | 67 ++++++++++++------- src/Composer/Repository/RepositoryFactory.php | 2 +- 3 files changed, 48 insertions(+), 28 deletions(-) diff --git a/doc/03-cli.md b/doc/03-cli.md index 76f0125f8..ec119d24c 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -724,8 +724,9 @@ By default the command checks for the packages on packagist.org. which will be used instead of packagist. Can be either an HTTP URL pointing to a `composer` repository, a path to a local `packages.json` file, or a JSON string which similar to what the [repositories](04-schema.md#repositories) - key accepts. -* **--add-repository:** Add the repository option to the composer.json. + key accepts. You can use this multiple times to configure multiple repositories. +* **--add-repository:** Add the custom repository in the composer.json. If a lock + file is present it will be deleted and an update will be run instead of install. * **--dev:** Install packages listed in `require-dev`. * **--no-dev:** Disables installation of require-dev packages. * **--no-scripts:** Disables the execution of the scripts defined in the root @@ -966,7 +967,7 @@ commands) to finish executing. The default value is 300 seconds (5 minutes). ### COMPOSER_ROOT_VERSION -By setting this var you can specify the version of the root package, if it +By setting this var you can specify the version of the root package, if it cannot be guessed from VCS info and is not present in `composer.json`. ### COMPOSER_VENDOR_DIR diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index c0470e096..d633e1519 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -69,9 +69,9 @@ class CreateProjectCommand extends BaseCommand new InputOption('stability', 's', InputOption::VALUE_REQUIRED, 'Minimum-stability allowed (unless a version is specified).'), new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'), new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'), - new InputOption('repository', null, InputOption::VALUE_REQUIRED, 'Pick a different repository (as url or json config) to look for the package.'), + new InputOption('repository', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Add custom repositories to look the package up, either by URL or using JSON arrays'), new InputOption('repository-url', null, InputOption::VALUE_REQUIRED, 'DEPRECATED: Use --repository instead.'), - new InputOption('add-repository', null, InputOption::VALUE_NONE, 'Add the repository option to the composer.json.'), + new InputOption('add-repository', null, InputOption::VALUE_NONE, 'Add the custom repository in the composer.json. If a lock file is present it will be deleted and an update will be run instead of install.'), 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-custom-installers', null, InputOption::VALUE_NONE, 'DEPRECATED: Use no-plugins instead.'), @@ -153,38 +153,50 @@ EOT ); } - public function installProject(IOInterface $io, Config $config, InputInterface $input, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repository = null, $disablePlugins = false, $noScripts = false, $noProgress = false, $noInstall = false, $ignorePlatformReqs = false, $secureHttp = true, $addRepository = false) + public function installProject(IOInterface $io, Config $config, InputInterface $input, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repositories = null, $disablePlugins = false, $noScripts = false, $noProgress = false, $noInstall = false, $ignorePlatformReqs = false, $secureHttp = true, $addRepository = false) { $oldCwd = getcwd(); + if ($repositories !== null && !is_array($repositories)) { + $repositories = (array) $repositories; + } + // we need to manually load the configuration to pass the auth credentials to the io interface! $io->loadConfiguration($config); $this->suggestedPackagesReporter = new SuggestedPackagesReporter($io); if ($packageName !== null) { - $installedFromVcs = $this->installRootPackage($io, $config, $packageName, $directory, $packageVersion, $stability, $preferSource, $preferDist, $installDevPackages, $repository, $disablePlugins, $noScripts, $noProgress, $ignorePlatformReqs, $secureHttp); + $installedFromVcs = $this->installRootPackage($io, $config, $packageName, $directory, $packageVersion, $stability, $preferSource, $preferDist, $installDevPackages, $repositories, $disablePlugins, $noScripts, $noProgress, $ignorePlatformReqs, $secureHttp); } else { $installedFromVcs = false; } + if ($repositories !== null && $addRepository && is_file('composer.lock')) { + unlink('composer.lock'); + } + $composer = Factory::create($io, null, $disablePlugins); // add the repository to the composer.json and use it for the install run later - if ($repository !== null && $addRepository) { - if ($composer->getLocker()->isLocked()) { - $io->writeError('Adding a repository when creating a project that provides a composer.lock file is not supported'); + if ($repositories !== null && $addRepository) { + foreach ($repositories as $index => $repo) { + $repoConfig = RepositoryFactory::configFromString($io, $composer->getConfig(), $repo, true); + $composerJsonRepositoriesConfig = $composer->getConfig()->getRepositories(); + $name = RepositoryFactory::generateRepositoryName($index, $repoConfig, $composerJsonRepositoriesConfig); + $configSource = new JsonConfigSource(new JsonFile('composer.json')); - return false; + if ( + (isset($repoConfig['packagist']) && $repoConfig === array('packagist' => false)) + || (isset($repoConfig['packagist.org']) && $repoConfig === array('packagist.org' => false)) + ) { + $configSource->addRepository('packagist.org', false); + } else { + $configSource->addRepository($name, $repoConfig); + } + + $composer = Factory::create($io, null, $disablePlugins); } - - $repoConfig = RepositoryFactory::configFromString($io, $composer->getConfig(), $repository, true); - $composerJsonRepositoriesConfig = $composer->getConfig()->getRepositories(); - $name = RepositoryFactory::generateRepositoryName(0, $repoConfig, $composerJsonRepositoriesConfig); - $configSource = new JsonConfigSource(new JsonFile('composer.json')); - $configSource->addRepository($name, $repoConfig); - - $composer = Factory::create($io, null, $disablePlugins); } $process = new ProcessExecutor($io); @@ -290,18 +302,12 @@ EOT return 0; } - protected function installRootPackage(IOInterface $io, Config $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repository = null, $disablePlugins = false, $noScripts = false, $noProgress = false, $ignorePlatformReqs = false, $secureHttp = true) + protected function installRootPackage(IOInterface $io, Config $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, array $repositories = null, $disablePlugins = false, $noScripts = false, $noProgress = false, $ignorePlatformReqs = false, $secureHttp = true) { if (!$secureHttp) { $config->merge(array('config' => array('secure-http' => false))); } - if (null === $repository) { - $sourceRepo = new CompositeRepository(RepositoryFactory::defaultRepos($io, $config)); - } else { - $sourceRepo = RepositoryFactory::fromString($io, $config, $repository, true); - } - $parser = new VersionParser(); $requirements = $parser->parseNameVersionPairs(array($packageName)); $name = strtolower($requirements[0]['name']); @@ -346,7 +352,20 @@ EOT } $repositorySet = new RepositorySet($stability); - $repositorySet->addRepository($sourceRepo); + if (null === $repositories) { + $repositorySet->addRepository(new CompositeRepository(RepositoryFactory::defaultRepos($io, $config))); + } else { + foreach ($repositories as $repo) { + $repoConfig = RepositoryFactory::configFromString($io, $config, $repo); + if ( + (isset($repoConfig['packagist']) && $repoConfig === array('packagist' => false)) + || (isset($repoConfig['packagist.org']) && $repoConfig === array('packagist.org' => false)) + ) { + continue; + } + $repositorySet->addRepository(RepositoryFactory::createRepo($io, $config, $repoConfig)); + } + } $platformOverrides = $config->get('platform') ?: array(); $platformRepo = new PlatformRepository(array(), $platformOverrides); diff --git a/src/Composer/Repository/RepositoryFactory.php b/src/Composer/Repository/RepositoryFactory.php index b36664187..afe1db636 100644 --- a/src/Composer/Repository/RepositoryFactory.php +++ b/src/Composer/Repository/RepositoryFactory.php @@ -173,6 +173,6 @@ class RepositoryFactory $name .= '2'; } - return $name; + return is_numeric((string) $name) ? 'repo'.$name : $name; } } From 201533e16fedf5004971e4248831e61fce298a3d Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 17 Jul 2020 11:05:57 +0200 Subject: [PATCH 56/59] Fix return value --- src/Composer/Repository/RepositoryFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/RepositoryFactory.php b/src/Composer/Repository/RepositoryFactory.php index afe1db636..b36664187 100644 --- a/src/Composer/Repository/RepositoryFactory.php +++ b/src/Composer/Repository/RepositoryFactory.php @@ -173,6 +173,6 @@ class RepositoryFactory $name .= '2'; } - return is_numeric((string) $name) ? 'repo'.$name : $name; + return $name; } } From 433968de1736958adeb1bf624f538c06d8f35e27 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 17 Jul 2020 11:30:03 +0200 Subject: [PATCH 57/59] Fix usage of create-project with local filesystem repos --- src/Composer/Command/CreateProjectCommand.php | 2 +- src/Composer/Command/InitCommand.php | 4 ++-- tests/Composer/Test/AllFunctionalTest.php | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index d633e1519..ddebbac8c 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -356,7 +356,7 @@ EOT $repositorySet->addRepository(new CompositeRepository(RepositoryFactory::defaultRepos($io, $config))); } else { foreach ($repositories as $repo) { - $repoConfig = RepositoryFactory::configFromString($io, $config, $repo); + $repoConfig = RepositoryFactory::configFromString($io, $config, $repo, true); if ( (isset($repoConfig['packagist']) && $repoConfig === array('packagist' => false)) || (isset($repoConfig['packagist.org']) && $repoConfig === array('packagist.org' => false)) diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 111edbde9..204038951 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -98,7 +98,7 @@ EOT if ($repositories) { $config = Factory::createConfig($io); foreach ($repositories as $repo) { - $options['repositories'][] = RepositoryFactory::configFromString($io, $config, $repo); + $options['repositories'][] = RepositoryFactory::configFromString($io, $config, $repo, true); } } @@ -173,7 +173,7 @@ EOT $repos = array(new PlatformRepository); $createDefaultPackagistRepo = true; foreach ($repositories as $repo) { - $repoConfig = RepositoryFactory::configFromString($io, $config, $repo); + $repoConfig = RepositoryFactory::configFromString($io, $config, $repo, true); if ( (isset($repoConfig['packagist']) && $repoConfig === array('packagist' => false)) || (isset($repoConfig['packagist.org']) && $repoConfig === array('packagist.org' => false)) diff --git a/tests/Composer/Test/AllFunctionalTest.php b/tests/Composer/Test/AllFunctionalTest.php index 5e8ebb5c4..bd8e7b853 100644 --- a/tests/Composer/Test/AllFunctionalTest.php +++ b/tests/Composer/Test/AllFunctionalTest.php @@ -100,7 +100,7 @@ class AllFunctionalTest extends TestCase * @dataProvider getTestFiles * @depends testBuildPhar */ - public function testIntegration(\SplFileInfo $testFile) + public function testIntegration($testFile) { $testData = $this->parseTestFile($testFile); @@ -133,15 +133,15 @@ class AllFunctionalTest extends TestCase { $tests = array(); foreach (Finder::create()->in(__DIR__.'/Fixtures/functional')->name('*.test')->files() as $file) { - $tests[] = array($file); + $tests[] = array($file->getRealPath()); } return $tests; } - private function parseTestFile(\SplFileInfo $file) + private function parseTestFile($file) { - $tokens = preg_split('#(?:^|\n*)--([A-Z-]+)--\n#', file_get_contents($file->getRealPath()), null, PREG_SPLIT_DELIM_CAPTURE); + $tokens = preg_split('#(?:^|\n*)--([A-Z-]+)--\n#', file_get_contents($file), null, PREG_SPLIT_DELIM_CAPTURE); $data = array(); $section = null; From eac03e16e7213e2ddc7e73818e91a0f21e571bda Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 17 Jul 2020 12:05:07 +0200 Subject: [PATCH 58/59] Reuse repository manager and others directly from the Composer instance, refs #9057 --- src/Composer/Command/CreateProjectCommand.php | 22 +++++-------------- src/Composer/Repository/RepositoryFactory.php | 10 +++++---- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 6e41e64ac..1e52674ba 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -288,13 +288,13 @@ EOT } $composer = Factory::create($io, $config->all(), $disablePlugins); - $eventDispatcher = $composer->getEventDispatcher(); + $config = $composer->getConfig(); + $rm = $composer->getRepositoryManager(); if (null === $repository) { - $rm = RepositoryFactory::manager($io, $config, $eventDispatcher, Factory::createRemoteFilesystem($io, $config)); $sourceRepo = new CompositeRepository(RepositoryFactory::defaultRepos($io, $config, $rm)); } else { - $sourceRepo = RepositoryFactory::fromString($io, $config, $repository, true); + $sourceRepo = RepositoryFactory::fromString($io, $config, $repository, true, $rm); } $parser = new VersionParser(); @@ -389,13 +389,13 @@ EOT $package = $package->getAliasOf(); } - $dm = $this->createDownloadManager($io, $config, $eventDispatcher); + $dm = $composer->getDownloadManager(); $dm->setPreferSource($preferSource) ->setPreferDist($preferDist) ->setOutputProgress(!$noProgress); $projectInstaller = new ProjectInstaller($directory, $dm); - $im = $this->createInstallationManager(); + $im = $composer->getInstallationManager(); $im->addInstaller($projectInstaller); $im->install(new InstalledFilesystemRepository(new JsonFile('php://memory')), new InstallOperation($package)); $im->notifyInstalls($io); @@ -413,16 +413,4 @@ EOT return $installedFromVcs; } - - protected function createDownloadManager(IOInterface $io, Config $config, EventDispatcher $eventDispatcher) - { - $factory = new Factory(); - - return $factory->createDownloadManager($io, $config, $eventDispatcher); - } - - protected function createInstallationManager() - { - return new InstallationManager(); - } } diff --git a/src/Composer/Repository/RepositoryFactory.php b/src/Composer/Repository/RepositoryFactory.php index 9bca0bd07..cf2c79dec 100644 --- a/src/Composer/Repository/RepositoryFactory.php +++ b/src/Composer/Repository/RepositoryFactory.php @@ -62,11 +62,11 @@ class RepositoryFactory * @param bool $allowFilesystem * @return RepositoryInterface */ - public static function fromString(IOInterface $io, Config $config, $repository, $allowFilesystem = false) + public static function fromString(IOInterface $io, Config $config, $repository, $allowFilesystem = false, RepositoryManager $rm = null) { $repoConfig = static::configFromString($io, $config, $repository, $allowFilesystem); - return static::createRepo($io, $config, $repoConfig); + return static::createRepo($io, $config, $repoConfig, $rm); } /** @@ -75,9 +75,11 @@ class RepositoryFactory * @param array $repoConfig * @return RepositoryInterface */ - public static function createRepo(IOInterface $io, Config $config, array $repoConfig) + public static function createRepo(IOInterface $io, Config $config, array $repoConfig, RepositoryManager $rm = null) { - $rm = static::manager($io, $config, null, Factory::createRemoteFilesystem($io, $config)); + if (!$rm) { + $rm = static::manager($io, $config, null, Factory::createRemoteFilesystem($io, $config)); + } $repos = static::createRepos($rm, array($repoConfig)); return reset($repos); From ff7f9dcf2d2cb1dcb828b48b0d611c9cfe29092f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 17 Jul 2020 13:00:41 +0200 Subject: [PATCH 59/59] Update deps --- composer.lock | 101 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 41 deletions(-) diff --git a/composer.lock b/composer.lock index 32344435f..bbadaea6b 100644 --- a/composer.lock +++ b/composer.lock @@ -140,16 +140,16 @@ }, { "name": "composer/spdx-licenses", - "version": "1.5.3", + "version": "1.5.4", "source": { "type": "git", "url": "https://github.com/composer/spdx-licenses.git", - "reference": "0c3e51e1880ca149682332770e25977c70cf9dae" + "reference": "6946f785871e2314c60b4524851f3702ea4f2223" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/0c3e51e1880ca149682332770e25977c70cf9dae", - "reference": "0c3e51e1880ca149682332770e25977c70cf9dae", + "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/6946f785871e2314c60b4524851f3702ea4f2223", + "reference": "6946f785871e2314c60b4524851f3702ea4f2223", "shasum": "" }, "require": { @@ -196,25 +196,34 @@ "spdx", "validator" ], - "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/spdx-licenses/issues", - "source": "https://github.com/composer/spdx-licenses/tree/1.5.3" - }, - "time": "2020-02-14T07:44:31+00:00" + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2020-07-15T15:35:07+00:00" }, { "name": "composer/xdebug-handler", - "version": "1.4.1", + "version": "1.4.2", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7" + "reference": "fa2aaf99e2087f013a14f7432c1cd2dd7d8f1f51" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/1ab9842d69e64fb3a01be6b656501032d1b78cb7", - "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/fa2aaf99e2087f013a14f7432c1cd2dd7d8f1f51", + "reference": "fa2aaf99e2087f013a14f7432c1cd2dd7d8f1f51", "shasum": "" }, "require": { @@ -245,18 +254,21 @@ "Xdebug", "performance" ], - "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/xdebug-handler/issues", - "source": "https://github.com/composer/xdebug-handler/tree/master" - }, "funding": [ { "url": "https://packagist.com", "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" } ], - "time": "2020-03-01T12:26:26+00:00" + "time": "2020-06-04T11:16:35+00:00" }, { "name": "justinrainbow/json-schema", @@ -439,16 +451,16 @@ }, { "name": "seld/phar-utils", - "version": "1.1.0", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/Seldaek/phar-utils.git", - "reference": "8800503d56b9867d43d9c303b9cbcc26016e82f0" + "reference": "8674b1d84ffb47cc59a101f5d5a3b61e87d23796" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/8800503d56b9867d43d9c303b9cbcc26016e82f0", - "reference": "8800503d56b9867d43d9c303b9cbcc26016e82f0", + "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/8674b1d84ffb47cc59a101f5d5a3b61e87d23796", + "reference": "8674b1d84ffb47cc59a101f5d5a3b61e87d23796", "shasum": "" }, "require": { @@ -479,11 +491,7 @@ "keywords": [ "phar" ], - "support": { - "issues": "https://github.com/Seldaek/phar-utils/issues", - "source": "https://github.com/Seldaek/phar-utils/tree/1.1.0" - }, - "time": "2020-02-14T15:25:33+00:00" + "time": "2020-07-07T18:42:57+00:00" }, { "name": "symfony/console", @@ -716,16 +724,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.17.0", + "version": "v1.18.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9" + "reference": "1c302646f6efc070cd46856e600e5e0684d6b454" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e94c8b1bbe2bc77507a1056cdb06451c75b427f9", - "reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/1c302646f6efc070cd46856e600e5e0684d6b454", + "reference": "1c302646f6efc070cd46856e600e5e0684d6b454", "shasum": "" }, "require": { @@ -737,7 +745,11 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.17-dev" + "dev-master": "1.18-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { @@ -784,20 +796,20 @@ "type": "tidelift" } ], - "time": "2020-05-12T16:14:59+00:00" + "time": "2020-07-14T12:35:20+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.17.0", + "version": "v1.18.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fa79b11539418b02fc5e1897267673ba2c19419c" + "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fa79b11539418b02fc5e1897267673ba2c19419c", - "reference": "fa79b11539418b02fc5e1897267673ba2c19419c", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/a6977d63bf9a0ad4c65cd352709e230876f9904a", + "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a", "shasum": "" }, "require": { @@ -809,7 +821,11 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.17-dev" + "dev-master": "1.18-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { @@ -857,7 +873,7 @@ "type": "tidelift" } ], - "time": "2020-05-12T16:47:27+00:00" + "time": "2020-07-14T12:35:20+00:00" }, { "name": "symfony/process", @@ -1406,6 +1422,9 @@ ], "description": "Symfony PHPUnit Bridge", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/phpunit-bridge/tree/4.2" + }, "time": "2019-07-05T06:33:37+00:00" } ],