1
0
Fork 0

Merge branch '2.2' into main

pull/11010/head
Jordi Boggiano 2022-03-15 13:10:31 +01:00
commit 5122bd42fb
No known key found for this signature in database
GPG Key ID: 7BBD42C429EC80BC
18 changed files with 219 additions and 48 deletions

View File

@ -13,7 +13,7 @@ jobs:
pull-requests: write pull-requests: write
steps: steps:
- uses: actions/stale@v3 - uses: actions/stale@v5
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 180 days-before-stale: 180

View File

@ -81,14 +81,14 @@ jobs:
# This step requires a secret token with `pull` access to composer/docker. The default # 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. # secrets.GITHUB_TOKEN is scoped to this repository only which is not sufficient.
- name: "Open issue @ Docker repository" - name: "Open issue @ Docker repository"
uses: actions/github-script@v2 uses: actions/github-script@v6
with: with:
github-token: ${{ secrets.PUBLIC_REPO_ACCESS_TOKEN }} github-token: ${{ secrets.PUBLIC_REPO_ACCESS_TOKEN }}
script: | script: |
// github.ref value looks like 'refs/tags/TAG', cleanup // github.ref value looks like 'refs/tags/TAG', cleanup
const tag = "${{ github.ref }}".replace(/refs\/tags\//, ''); const tag = "${{ github.ref }}".replace(/refs\/tags\//, '');
// create new issue on Docker repository // create new issue on Docker repository
github.issues.create({ github.rest.issues.create({
owner: "${{ github.repository_owner }}", owner: "${{ github.repository_owner }}",
repo: "docker", repo: "docker",
title: `New Composer tag: ${ tag }`, title: `New Composer tag: ${ tag }`,

View File

@ -1,3 +1,11 @@
### [2.2.8] 2022-03-15
* Fixed `files` autoloading sort order to be fully deterministic (#10617)
* Fixed pool optimization pass edge cases (#10579)
* Fixed `require` command failing when `self.version` is used as constraint (#10593)
* Fixed --no-ansi / undecorated output still showing color in repo warnings (#10601)
* Performance improvement in pool optimization step (composer/semver#131)
### [2.2.7] 2022-02-25 ### [2.2.7] 2022-02-25
* Allow installation together with composer/xdebug-handler ^3 (#10528) * Allow installation together with composer/xdebug-handler ^3 (#10528)
@ -1394,6 +1402,7 @@
* Initial release * Initial release
[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.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 [2.2.6]: https://github.com/composer/composer/compare/2.2.5...2.2.6
[2.2.5]: https://github.com/composer/composer/compare/2.2.4...2.2.5 [2.2.5]: https://github.com/composer/composer/compare/2.2.4...2.2.5

12
composer.lock generated
View File

@ -224,16 +224,16 @@
}, },
{ {
"name": "composer/semver", "name": "composer/semver",
"version": "3.2.9", "version": "3.3.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/composer/semver.git", "url": "https://github.com/composer/semver.git",
"reference": "a951f614bd64dcd26137bc9b7b2637ddcfc57649" "reference": "f79c90ad4e9b41ac4dfc5d77bf398cf61fbd718b"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/composer/semver/zipball/a951f614bd64dcd26137bc9b7b2637ddcfc57649", "url": "https://api.github.com/repos/composer/semver/zipball/f79c90ad4e9b41ac4dfc5d77bf398cf61fbd718b",
"reference": "a951f614bd64dcd26137bc9b7b2637ddcfc57649", "reference": "f79c90ad4e9b41ac4dfc5d77bf398cf61fbd718b",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -285,7 +285,7 @@
"support": { "support": {
"irc": "irc://irc.freenode.org/composer", "irc": "irc://irc.freenode.org/composer",
"issues": "https://github.com/composer/semver/issues", "issues": "https://github.com/composer/semver/issues",
"source": "https://github.com/composer/semver/tree/3.2.9" "source": "https://github.com/composer/semver/tree/3.3.0"
}, },
"funding": [ "funding": [
{ {
@ -301,7 +301,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-02-04T13:58:43+00:00" "time": "2022-03-15T08:35:57+00:00"
}, },
{ {
"name": "composer/spdx-licenses", "name": "composer/spdx-licenses",

View File

@ -323,6 +323,13 @@ 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 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. process however, so do not use it in plugins which do not absolutely require it.
## Plugin Autoloading
Due to plugins being loaded by Composer at runtime, and to ensure that plugins which
depend on other packages can function correctly, a runtime autoloader is created whenever
a plugin is loaded. That autoloader is only configured to load with the plugin dependencies,
so you may not have access to all the packages which are installed.
[1]: ../04-schema.md#type [1]: ../04-schema.md#type
[2]: ../04-schema.md#extra [2]: ../04-schema.md#extra
[3]: https://github.com/composer/composer/blob/main/src/Composer/Plugin/PluginInterface.php [3]: https://github.com/composer/composer/blob/main/src/Composer/Plugin/PluginInterface.php

View File

@ -1334,7 +1334,7 @@ INITIALIZER;
/** /**
* Sorts packages by dependency weight * Sorts packages by dependency weight
* *
* Packages of equal weight retain the original order * Packages of equal weight are sorted alphabetically
* *
* @param array<int, array{0: PackageInterface, 1: string}> $packageMap * @param array<int, array{0: PackageInterface, 1: string}> $packageMap
* @return array<int, array{0: PackageInterface, 1: string}> * @return array<int, array{0: PackageInterface, 1: string}>

View File

@ -225,6 +225,9 @@ EOT
return 1; return 1;
} }
if ($constraint === 'self.version') {
continue;
}
$versionParser->parseConstraints($constraint); $versionParser->parseConstraints($constraint);
} }

View File

@ -110,21 +110,18 @@ class PoolOptimizer
// Extract requested package requirements // Extract requested package requirements
foreach ($request->getRequires() as $require => $constraint) { foreach ($request->getRequires() as $require => $constraint) {
$constraint = Intervals::compactConstraint($constraint); $this->extractRequireConstraintsPerPackage($require, $constraint);
$this->requireConstraintsPerPackage[$require][(string) $constraint] = $constraint;
} }
// First pass over all packages to extract information and mark package constraints irremovable // First pass over all packages to extract information and mark package constraints irremovable
foreach ($pool->getPackages() as $package) { foreach ($pool->getPackages() as $package) {
// Extract package requirements // Extract package requirements
foreach ($package->getRequires() as $link) { foreach ($package->getRequires() as $link) {
$constraint = Intervals::compactConstraint($link->getConstraint()); $this->extractRequireConstraintsPerPackage($link->getTarget(), $link->getConstraint());
$this->requireConstraintsPerPackage[$link->getTarget()][(string) $constraint] = $constraint;
} }
// Extract package conflicts // Extract package conflicts
foreach ($package->getConflicts() as $link) { foreach ($package->getConflicts() as $link) {
$constraint = Intervals::compactConstraint($link->getConstraint()); $this->extractConflictConstraintsPerPackage($link->getTarget(), $link->getConstraint());
$this->conflictConstraintsPerPackage[$link->getTarget()][(string) $constraint] = $constraint;
} }
// Keep track of alias packages for every package so if either the alias or aliased is kept // Keep track of alias packages for every package so if either the alias or aliased is kept
@ -452,4 +449,55 @@ class PoolOptimizer
} }
} }
} }
/**
* Disjunctive require constraints need to be considered in their own group. E.g. "^2.14 || ^3.3" needs to generate
* two require constraint groups in order for us to keep the best matching package for "^2.14" AND "^3.3" as otherwise, we'd
* only keep either one which can cause trouble (e.g. when using --prefer-lowest).
*
* @param string $package
* @param ConstraintInterface $constraint
* @return void
*/
private function extractRequireConstraintsPerPackage($package, ConstraintInterface $constraint)
{
foreach ($this->expandDisjunctiveMultiConstraints($constraint) as $expanded) {
$this->requireConstraintsPerPackage[$package][(string) $expanded] = $expanded;
}
}
/**
* Disjunctive conflict constraints need to be considered in their own group. E.g. "^2.14 || ^3.3" needs to generate
* two conflict constraint groups in order for us to keep the best matching package for "^2.14" AND "^3.3" as otherwise, we'd
* only keep either one which can cause trouble (e.g. when using --prefer-lowest).
*
* @param string $package
* @param ConstraintInterface $constraint
* @return void
*/
private function extractConflictConstraintsPerPackage($package, ConstraintInterface $constraint)
{
foreach ($this->expandDisjunctiveMultiConstraints($constraint) as $expanded) {
$this->conflictConstraintsPerPackage[$package][(string) $expanded] = $expanded;
}
}
/**
*
* @param ConstraintInterface $constraint
* @return ConstraintInterface[]
*/
private function expandDisjunctiveMultiConstraints(ConstraintInterface $constraint)
{
$constraint = Intervals::compactConstraint($constraint);
if ($constraint instanceof MultiConstraint && $constraint->isDisjunctive()) {
// No need to call ourselves recursively here because Intervals::compactConstraint() ensures that there
// are no nested disjunctive MultiConstraint instances possible
return $constraint->getConstraints();
}
// Regular constraints and conjunctive MultiConstraints
return array($constraint);
}
} }

View File

@ -473,6 +473,11 @@ class Installer
return $exitCode; return $exitCode;
} }
// exists as of composer/semver 3.3.0
if (method_exists('Composer\Semver\CompilingMatcher', 'clear')) { // @phpstan-ignore-line
\Composer\Semver\CompilingMatcher::clear();
}
// write lock // write lock
$platformReqs = $this->extractPlatformRequirements($this->package->getRequires()); $platformReqs = $this->extractPlatformRequirements($this->package->getRequires());
$platformDevReqs = $this->extractPlatformRequirements($this->package->getDevRequires()); $platformDevReqs = $this->extractPlatformRequirements($this->package->getDevRequires());

View File

@ -428,6 +428,14 @@ class HttpDownloader
*/ */
public static function outputWarnings(IOInterface $io, string $url, $data): void public static function outputWarnings(IOInterface $io, string $url, $data): void
{ {
$cleanMessage = function ($msg) use ($io) {
if (!$io->isDecorated()) {
$msg = Preg::replace('{'.chr(27).'\\[[;\d]*m}u', '', $msg);
}
return $msg;
};
// legacy warning/info keys // legacy warning/info keys
foreach (array('warning', 'info') as $type) { foreach (array('warning', 'info') as $type) {
if (empty($data[$type])) { if (empty($data[$type])) {
@ -443,7 +451,7 @@ class HttpDownloader
} }
} }
$io->writeError('<'.$type.'>'.ucfirst($type).' from '.Url::sanitize($url).': '.$data[$type].'</'.$type.'>'); $io->writeError('<'.$type.'>'.ucfirst($type).' from '.Url::sanitize($url).': '.$cleanMessage($data[$type]).'</'.$type.'>');
} }
// modern Composer 2.2+ format with support for multiple warning/info messages // modern Composer 2.2+ format with support for multiple warning/info messages
@ -461,7 +469,7 @@ class HttpDownloader
continue; continue;
} }
$io->writeError('<'.$type.'>'.ucfirst($type).' from '.Url::sanitize($url).': '.$spec['message'].'</'.$type.'>'); $io->writeError('<'.$type.'>'.ucfirst($type).' from '.Url::sanitize($url).': '.$cleanMessage($spec['message']).'</'.$type.'>');
} }
} }
} }

View File

@ -13,13 +13,14 @@
namespace Composer\Util; namespace Composer\Util;
use Composer\Package\PackageInterface; use Composer\Package\PackageInterface;
use Composer\Package\RootPackageInterface;
class PackageSorter class PackageSorter
{ {
/** /**
* Sorts packages by dependency weight * Sorts packages by dependency weight
* *
* Packages of equal weight retain the original order * Packages of equal weight are sorted alphabetically
* *
* @param PackageInterface[] $packages * @param PackageInterface[] $packages
* @return PackageInterface[] sorted array * @return PackageInterface[] sorted array
@ -29,7 +30,11 @@ class PackageSorter
$usageList = array(); $usageList = array();
foreach ($packages as $package) { foreach ($packages as $package) {
foreach (array_merge($package->getRequires(), $package->getDevRequires()) as $link) { $links = $package->getRequires();
if ($package instanceof RootPackageInterface) {
$links = array_merge($links, $package->getDevRequires());
}
foreach ($links as $link) {
$target = $link->getTarget(); $target = $link->getTarget();
$usageList[$target][] = $package->getName(); $usageList[$target][] = $package->getName();
} }
@ -62,39 +67,26 @@ class PackageSorter
return $weight; return $weight;
}; };
$weightList = array(); $weightedPackages = array();
foreach ($packages as $index => $package) { foreach ($packages as $index => $package) {
$weight = $computeImportance($package->getName()); $name = $package->getName();
$weightList[$index] = $weight; $weight = $computeImportance($name);
$weightedPackages[] = array('name' => $name, 'weight' => $weight, 'index' => $index);
} }
$stable_sort = function (&$array): void { usort($weightedPackages, function (array $a, array $b): int {
static $transform, $restore; if ($a['weight'] !== $b['weight']) {
return $a['weight'] - $b['weight'];
$i = 0;
if (!$transform) {
$transform = function (&$v, $k) use (&$i): void {
$v = array($v, ++$i, $k, $v);
};
$restore = function (&$v): void {
$v = $v[3];
};
} }
array_walk($array, $transform); return strnatcasecmp($a['name'], $b['name']);
asort($array); });
array_walk($array, $restore);
};
$stable_sort($weightList);
$sortedPackages = array(); $sortedPackages = array();
foreach (array_keys($weightList) as $index) { foreach ($weightedPackages as $pkg) {
$sortedPackages[] = $packages[$index]; $sortedPackages[] = $packages[$pkg['index']];
} }
return $sortedPackages; return $sortedPackages;

View File

@ -1010,6 +1010,14 @@ EOF;
$packages[] = $c = new Package('c/lorem', '1.0', '1.0'); $packages[] = $c = new Package('c/lorem', '1.0', '1.0');
$packages[] = $e = new Package('e/e', '1.0', '1.0'); $packages[] = $e = new Package('e/e', '1.0', '1.0');
// expected order:
// c requires nothing
// d requires c
// b requires c & d
// e requires c
// z requires c
// (b, e, z ordered alphabetically)
$z->setAutoload(array('files' => array('testA.php'))); $z->setAutoload(array('files' => array('testA.php')));
$z->setRequires(array('c/lorem' => new Link('z/foo', 'c/lorem', new MatchAllConstraint()))); $z->setRequires(array('c/lorem' => new Link('z/foo', 'c/lorem', new MatchAllConstraint())));

View File

@ -9,9 +9,9 @@ class ComposerStaticInitFilesAutoloadOrder
public static $files = array ( public static $files = array (
'bfdd693009729d60c830ff8d79129635' => __DIR__ . '/..' . '/c/lorem/testC.php', 'bfdd693009729d60c830ff8d79129635' => __DIR__ . '/..' . '/c/lorem/testC.php',
'61e6098c8cafe404d6cf19e59fc2b788' => __DIR__ . '/..' . '/d/d/testD.php', '61e6098c8cafe404d6cf19e59fc2b788' => __DIR__ . '/..' . '/d/d/testD.php',
'8bceec6fdc149a1a6acbf7ad3cfed51c' => __DIR__ . '/..' . '/z/foo/testA.php',
'c5466e580c6c2403f225c43b6a21a96f' => __DIR__ . '/..' . '/b/bar/testB.php', 'c5466e580c6c2403f225c43b6a21a96f' => __DIR__ . '/..' . '/b/bar/testB.php',
'69dfc37c40a853a7cbac6c9d2367c5f4' => __DIR__ . '/..' . '/e/e/testE.php', '69dfc37c40a853a7cbac6c9d2367c5f4' => __DIR__ . '/..' . '/e/e/testE.php',
'8bceec6fdc149a1a6acbf7ad3cfed51c' => __DIR__ . '/..' . '/z/foo/testA.php',
'ab280164f4754f5dfdb0721de84d737f' => __DIR__ . '/../..' . '/root2.php', 'ab280164f4754f5dfdb0721de84d737f' => __DIR__ . '/../..' . '/root2.php',
); );

View File

@ -15,7 +15,7 @@ Test filters irrelevant package "package/b" in version 1.0.0
"name": "package/a", "name": "package/a",
"version": "1.0.0", "version": "1.0.0",
"require": { "require": {
"package/b": "^1.0" "package/b": ">=1.0 <1.1 || ^1.2"
} }
}, },
{ {
@ -25,6 +25,10 @@ Test filters irrelevant package "package/b" in version 1.0.0
{ {
"name": "package/b", "name": "package/b",
"version": "1.0.1" "version": "1.0.1"
},
{
"name": "package/b",
"version": "1.2.0"
} }
] ]
@ -41,6 +45,10 @@ Test filters irrelevant package "package/b" in version 1.0.0
{ {
"name": "package/b", "name": "package/b",
"version": "1.0.1" "version": "1.0.1"
},
{
"name": "package/b",
"version": "1.2.0"
} }
] ]

View File

@ -0,0 +1,55 @@
--TEST--
Test keeps package "package/b" in version 2.2.0 because for prefer-lowest either one might be relevant
--REQUEST--
{
"require": {
"package/a": "^1.0"
},
"preferLowest": true
}
--POOL-BEFORE--
[
{
"name": "package/a",
"version": "1.0.0",
"require": {
"package/b": "^1.0 || ^2.2"
}
},
{
"name": "package/b",
"version": "1.0.0"
},
{
"name": "package/b",
"version": "1.0.1"
},
{
"name": "package/b",
"version": "2.2.0"
}
]
--POOL-AFTER--
[
{
"name": "package/a",
"version": "1.0.0",
"require": {
"package/b": "^1.0"
}
},
{
"name": "package/b",
"version": "1.0.0"
},
{
"name": "package/b",
"version": "2.2.0"
}
]

View File

@ -11,7 +11,7 @@ update
!!PreUpdate:["composer/ca-bundle","composer/composer","composer/metadata-minifier","composer/pcre","composer/semver","composer/spdx-licenses","composer/xdebug-handler","justinrainbow/json-schema","psr/container","psr/log","psr/log-implementation","react/promise","seld/jsonlint","seld/phar-utils","symfony/console","symfony/deprecation-contracts","symfony/filesystem","symfony/finder","symfony/polyfill-ctype","symfony/polyfill-intl-grapheme","symfony/polyfill-intl-normalizer","symfony/polyfill-mbstring","symfony/polyfill-php73","symfony/polyfill-php80","symfony/process","symfony/service-contracts","symfony/string"] !!PreUpdate:["composer/ca-bundle","composer/composer","composer/metadata-minifier","composer/pcre","composer/semver","composer/spdx-licenses","composer/xdebug-handler","justinrainbow/json-schema","psr/container","psr/log","psr/log-implementation","react/promise","seld/jsonlint","seld/phar-utils","symfony/console","symfony/deprecation-contracts","symfony/filesystem","symfony/finder","symfony/polyfill-ctype","symfony/polyfill-intl-grapheme","symfony/polyfill-intl-normalizer","symfony/polyfill-mbstring","symfony/polyfill-php73","symfony/polyfill-php80","symfony/process","symfony/service-contracts","symfony/string"]
!!Versions:console:%[2-8]\.\d+\.\d+.0%;process:%[2-8]\.\d+\.\d+.0%;filesystem:%[2-8]\.\d+\.\d+.0% !!Versions:console:%[2-8]\.\d+\.\d+.0%;process:%[2-8]\.\d+\.\d+.0%;filesystem:%[2-8]\.\d+\.\d+.0%
Loading composer repositories with package information Loading composer repositories with package information
Updating dependencies %((Info|Warning) from .*\n)?%Updating dependencies
Lock file operations: 6 installs, 0 updates, 0 removals Lock file operations: 6 installs, 0 updates, 0 removals
- Locking plugin/a (1.1.1) - Locking plugin/a (1.1.1)
- Locking plugin/b (2.2.2) - Locking plugin/b (2.2.2)

View File

@ -17,7 +17,7 @@ update plugin/* symfony/console symfony/filesystem symfony/process
!!PreUpdate:["composer/ca-bundle","composer/composer","composer/metadata-minifier","composer/pcre","composer/semver","composer/spdx-licenses","composer/xdebug-handler","justinrainbow/json-schema","psr/container","psr/log","psr/log-implementation","react/promise","seld/jsonlint","seld/phar-utils","symfony/console","symfony/deprecation-contracts","symfony/filesystem","symfony/finder","symfony/polyfill-ctype","symfony/polyfill-intl-grapheme","symfony/polyfill-intl-normalizer","symfony/polyfill-mbstring","symfony/polyfill-php73","symfony/polyfill-php80","symfony/process","symfony/service-contracts","symfony/string","plugin/a","plugin/b","root/pkg"] !!PreUpdate:["composer/ca-bundle","composer/composer","composer/metadata-minifier","composer/pcre","composer/semver","composer/spdx-licenses","composer/xdebug-handler","justinrainbow/json-schema","psr/container","psr/log","psr/log-implementation","react/promise","seld/jsonlint","seld/phar-utils","symfony/console","symfony/deprecation-contracts","symfony/filesystem","symfony/finder","symfony/polyfill-ctype","symfony/polyfill-intl-grapheme","symfony/polyfill-intl-normalizer","symfony/polyfill-mbstring","symfony/polyfill-php73","symfony/polyfill-php80","symfony/process","symfony/service-contracts","symfony/string","plugin/a","plugin/b","root/pkg"]
!!Versions:console:%[2-8]\.\d+\.\d+.0%;process:%[2-8]\.\d+\.\d+.0%;filesystem:%[2-8]\.\d+\.\d+.0% !!Versions:console:%[2-8]\.\d+\.\d+.0%;process:%[2-8]\.\d+\.\d+.0%;filesystem:%[2-8]\.\d+\.\d+.0%
Loading composer repositories with package information Loading composer repositories with package information
Updating dependencies %((Info|Warning) from .*\n)?%Updating dependencies
Lock file operations: 0 installs, 5 updates, 0 removals Lock file operations: 0 installs, 5 updates, 0 removals
- Upgrading plugin/a (1.1.1 => 1.1.2) - Upgrading plugin/a (1.1.1 => 1.1.2)
- Upgrading plugin/b (2.2.2 => 2.2.3) - Upgrading plugin/b (2.2.2 => 2.2.3)

View File

@ -99,6 +99,34 @@ class PackageSorterTest extends TestCase
'foo/bar6', 'foo/bar6',
), ),
), ),
'circular deps sorted alphabetically if weighted equally' => array(
array(
$this->createPackage('foo/bar1', array('circular/part1')),
$this->createPackage('foo/bar2', array('circular/part2')),
$this->createPackage('circular/part1', array('circular/part2')),
$this->createPackage('circular/part2', array('circular/part1')),
),
array(
'circular/part1',
'circular/part2',
'foo/bar1',
'foo/bar2',
),
),
'equal weight sorted alphabetically' => array(
array(
$this->createPackage('foo/bar10', array('foo/dep')),
$this->createPackage('foo/bar2', array('foo/dep')),
$this->createPackage('foo/baz', array('foo/dep')),
$this->createPackage('foo/dep', array()),
),
array(
'foo/dep',
'foo/bar2',
'foo/bar10',
'foo/baz',
),
),
); );
} }