diff --git a/CHANGELOG.md b/CHANGELOG.md index 78675fcc6..801ec56eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,10 @@ * Fixed symlink creation in linux VM guest filesystems to be recognized by Windows (#10592) * Performance improvement in pool optimization step (#10585) +### [2.2.9] 2022-03-15 + + * Fixed regression with plugins that modify install path of packages, [see docs](https://getcomposer.org/doc/articles/plugins.md#plugin-modifies-install-path) if you are authoring such a plugin (#10621) + ### [2.2.8] 2022-03-15 * Fixed `files` autoloading sort order to be fully deterministic (#10617) @@ -1424,6 +1428,7 @@ * Initial release [2.3.0-RC1]: https://github.com/composer/composer/compare/2.2.9...2.3.0-RC1 +[2.2.9]: https://github.com/composer/composer/compare/2.2.8...2.2.9 [2.2.8]: https://github.com/composer/composer/compare/2.2.7...2.2.8 [2.2.7]: https://github.com/composer/composer/compare/2.2.6...2.2.7 [2.2.6]: https://github.com/composer/composer/compare/2.2.5...2.2.6 diff --git a/doc/articles/plugins.md b/doc/articles/plugins.md index fff0cdb22..bcbbe2cad 100644 --- a/doc/articles/plugins.md +++ b/doc/articles/plugins.md @@ -323,6 +323,15 @@ hint to Composer that the plugin should be installed on its own before proceedin the rest of the package downloads. This slightly slows down the overall installation process however, so do not use it in plugins which do not absolutely require it. +### plugin-modifies-install-path + +Some special plugins modify the install path of packages. + +As of Composer 2.2.9, you can specify `{"extra": {"plugin-modifies-install-path": true}}` +in your composer.json to hint to Composer that the plugin should be activated as soon +as possible to prevent any bad side-effects from Composer assuming packages are installed +in another location than they actually are. + ## Plugin Autoloading Due to plugins being loaded by Composer at runtime, and to ensure that plugins which diff --git a/src/Composer/Plugin/PluginManager.php b/src/Composer/Plugin/PluginManager.php index 87a2faf23..52c25d945 100644 --- a/src/Composer/Plugin/PluginManager.php +++ b/src/Composer/Plugin/PluginManager.php @@ -469,7 +469,18 @@ class PluginManager private function loadRepository(RepositoryInterface $repo, bool $isGlobalRepo): void { $packages = $repo->getPackages(); - $sortedPackages = PackageSorter::sortPackages($packages); + + $weights = array(); + foreach ($packages as $package) { + if ($package->getType() === 'composer-plugin') { + $extra = $package->getExtra(); + if ($package->getName() === 'composer/installers' || true === ($extra['plugin-modifies-install-path'] ?? false)) { + $weights[$package->getName()] = -10000; + } + } + } + + $sortedPackages = PackageSorter::sortPackages($packages, $weights); foreach ($sortedPackages as $package) { if (!($package instanceof CompletePackage)) { continue; diff --git a/src/Composer/Util/PackageSorter.php b/src/Composer/Util/PackageSorter.php index b5e9780ae..69d2afb63 100644 --- a/src/Composer/Util/PackageSorter.php +++ b/src/Composer/Util/PackageSorter.php @@ -23,9 +23,10 @@ class PackageSorter * Packages of equal weight are sorted alphabetically * * @param PackageInterface[] $packages + * @param array $weights Pre-set weights for some packages to give them more (negative number) or less (positive) weight offsets * @return PackageInterface[] sorted array */ - public static function sortPackages(array $packages): array + public static function sortPackages(array $packages, array $weights = array()): array { $usageList = array(); @@ -41,7 +42,7 @@ class PackageSorter } $computing = array(); $computed = array(); - $computeImportance = function ($name) use (&$computeImportance, &$computing, &$computed, $usageList) { + $computeImportance = function ($name) use (&$computeImportance, &$computing, &$computed, $usageList, $weights) { // reusing computed importance if (isset($computed[$name])) { return $computed[$name]; @@ -53,7 +54,7 @@ class PackageSorter } $computing[$name] = true; - $weight = 0; + $weight = $weights[$name] ?? 0; if (isset($usageList[$name])) { foreach ($usageList[$name] as $user) { diff --git a/tests/Composer/Test/Util/PackageSorterTest.php b/tests/Composer/Test/Util/PackageSorterTest.php index 981274316..0a2563854 100644 --- a/tests/Composer/Test/Util/PackageSorterTest.php +++ b/tests/Composer/Test/Util/PackageSorterTest.php @@ -127,6 +127,23 @@ class PackageSorterTest extends TestCase 'foo/baz', ), ), + 'pre-weighted packages bumped to top incl their deps' => array( + array( + $this->createPackage('foo/bar', array('foo/dep')), + $this->createPackage('foo/bar2', array('foo/dep2')), + $this->createPackage('foo/dep', array()), + $this->createPackage('foo/dep2', array()), + ), + array( + 'foo/dep', + 'foo/bar', + 'foo/dep2', + 'foo/bar2', + ), + array( + 'foo/bar' => -1000 + ) + ), ); } @@ -135,10 +152,11 @@ class PackageSorterTest extends TestCase * * @param Package[] $packages * @param string[] $expectedOrderedList + * @param array $weights */ - public function testSortingOrdersDependenciesHigherThanPackage(array $packages, array $expectedOrderedList): void + public function testSortingOrdersDependenciesHigherThanPackage(array $packages, array $expectedOrderedList, array $weights = []): void { - $sortedPackages = PackageSorter::sortPackages($packages); + $sortedPackages = PackageSorter::sortPackages($packages, $weights); $sortedPackageNames = array_map(function ($package): string { return $package->getName(); }, $sortedPackages);