Add warning when the latest version of a package cannot be auto-selected in require/init/create-project, fixes #10884 (#10896)
parent
92ff8e5bc6
commit
eba49147e7
|
@ -434,7 +434,7 @@ EOT
|
|||
|
||||
// find the latest version if there are multiple
|
||||
$versionSelector = new VersionSelector($repositorySet, $platformRepo);
|
||||
$package = $versionSelector->findBestCandidate($name, $packageVersion, $stability, $platformRequirementFilter);
|
||||
$package = $versionSelector->findBestCandidate($name, $packageVersion, $stability, $platformRequirementFilter, 0, $io);
|
||||
|
||||
if (!$package) {
|
||||
$errorMessage = "Could not find package $name with " . ($packageVersion ? "version $packageVersion" : "stability $stability");
|
||||
|
|
|
@ -282,7 +282,7 @@ trait PackageDiscoveryTrait
|
|||
$versionSelector = new VersionSelector($repoSet, $platformRepo);
|
||||
$effectiveMinimumStability = $this->getMinimumStability($input);
|
||||
|
||||
$package = $versionSelector->findBestCandidate($name, null, $preferredStability, $platformRequirementFilter);
|
||||
$package = $versionSelector->findBestCandidate($name, null, $preferredStability, $platformRequirementFilter, 0, $this->getIO());
|
||||
|
||||
if (false === $package) {
|
||||
// platform packages can not be found in the pool in versions other than the local platform's has
|
||||
|
|
|
@ -15,6 +15,7 @@ namespace Composer\Package\Version;
|
|||
use Composer\Filter\PlatformRequirementFilter\IgnoreAllPlatformRequirementFilter;
|
||||
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory;
|
||||
use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterInterface;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Package\BasePackage;
|
||||
use Composer\Package\AliasPackage;
|
||||
use Composer\Package\PackageInterface;
|
||||
|
@ -65,10 +66,11 @@ class VersionSelector
|
|||
* @param string $targetPackageVersion
|
||||
* @param string $preferredStability
|
||||
* @param PlatformRequirementFilterInterface|bool|string[] $platformRequirementFilter
|
||||
* @param int $repoSetFlags*
|
||||
* @param int $repoSetFlags
|
||||
* @param IOInterface|null $io If passed, warnings will be output there in case versions cannot be selected due to platform requirements
|
||||
* @return PackageInterface|false
|
||||
*/
|
||||
public function findBestCandidate(string $packageName, string $targetPackageVersion = null, string $preferredStability = 'stable', $platformRequirementFilter = null, int $repoSetFlags = 0)
|
||||
public function findBestCandidate(string $packageName, string $targetPackageVersion = null, string $preferredStability = 'stable', $platformRequirementFilter = null, int $repoSetFlags = 0, ?IOInterface $io = null)
|
||||
{
|
||||
if (!isset(BasePackage::$stabilities[$preferredStability])) {
|
||||
// If you get this, maybe you are still relying on the Composer 1.x signature where the 3rd arg was the php version
|
||||
|
@ -84,10 +86,11 @@ class VersionSelector
|
|||
|
||||
$constraint = $targetPackageVersion ? $this->getParser()->parseConstraints($targetPackageVersion) : null;
|
||||
$candidates = $this->repositorySet->findPackages(strtolower($packageName), $constraint, $repoSetFlags);
|
||||
$skippedWarnings = [];
|
||||
|
||||
if ($this->platformConstraints && !($platformRequirementFilter instanceof IgnoreAllPlatformRequirementFilter)) {
|
||||
$platformConstraints = $this->platformConstraints;
|
||||
$candidates = array_filter($candidates, static function ($pkg) use ($platformConstraints, $platformRequirementFilter): bool {
|
||||
$candidates = array_filter($candidates, static function ($pkg) use ($platformConstraints, $platformRequirementFilter, &$skippedWarnings): bool {
|
||||
$reqs = $pkg->getRequires();
|
||||
|
||||
foreach ($reqs as $name => $link) {
|
||||
|
@ -99,10 +102,12 @@ class VersionSelector
|
|||
}
|
||||
}
|
||||
|
||||
$skippedWarnings[$pkg->getName()][] = ['version' => $pkg->getPrettyVersion(), 'link' => $link, 'reason' => 'is not satisfied by your platform'];
|
||||
return false;
|
||||
} elseif (PlatformRepository::isPlatformPackage($name)) {
|
||||
// Package requires a platform package that is unknown on current platform.
|
||||
// It means that current platform cannot validate this constraint and so package is not installable.
|
||||
$skippedWarnings[$pkg->getName()][] = ['version' => $pkg->getPrettyVersion(), 'link' => $link, 'reason' => 'is missing from your platform'];
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -116,6 +121,20 @@ class VersionSelector
|
|||
return false;
|
||||
}
|
||||
|
||||
if (count($skippedWarnings) > 0 && $io !== null) {
|
||||
foreach ($skippedWarnings as $name => $warnings) {
|
||||
foreach ($warnings as $index => $warning) {
|
||||
$link = $warning['link'];
|
||||
$latest = $index === 0 ? "'s latest version" : '';
|
||||
$io->writeError(
|
||||
'<warning>Cannot use '.$name.$latest.' '.$warning['version'].' as it '.$link->getDescription().' '.$link->getTarget().' '.$link->getPrettyConstraint().' which '.$warning['reason'].'.</>',
|
||||
true,
|
||||
$index === 0 ? IOInterface::NORMAL : IOInterface::VERBOSE
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// select highest version if we have many
|
||||
$package = reset($candidates);
|
||||
$minPriority = BasePackage::$stabilities[$preferredStability];
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Test\Command;
|
||||
|
||||
use Composer\Test\TestCase;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class RequireCommandTest extends TestCase
|
||||
{
|
||||
public function testRequireThrowsIfNoneMatches(): void
|
||||
{
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage(
|
||||
'Package required/pkg has requirements incompatible with your PHP version, PHP extensions and Composer version:' . PHP_EOL .
|
||||
' - required/pkg 1.0.0 requires ext-foobar ^1 but it is not present.'
|
||||
);
|
||||
|
||||
$this->initTempComposer([
|
||||
'repositories' => [
|
||||
'packages' => [
|
||||
'type' => 'package',
|
||||
'package' => [
|
||||
['name' => 'required/pkg', 'version' => '1.0.0', 'require' => ['ext-foobar' => '^1']],
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$appTester = $this->getApplicationTester();
|
||||
$appTester->run(['command' => 'require', '--dry-run' => true, '--no-audit' => true, 'packages' => ['required/pkg']]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideRequire
|
||||
* @param array<mixed> $composerJson
|
||||
* @param array<mixed> $command
|
||||
*/
|
||||
public function testRequire(array $composerJson, array $command, string $expected): void
|
||||
{
|
||||
$this->initTempComposer($composerJson);
|
||||
|
||||
$appTester = $this->getApplicationTester();
|
||||
$appTester->run(array_merge(['command' => 'require', '--dry-run' => true, '--no-audit' => true], $command));
|
||||
|
||||
if (str_contains($expected, '%d')) {
|
||||
$pattern = '{^'.str_replace('%d', '[0-9.]+', preg_quote(trim($expected))).'$}';
|
||||
$this->assertMatchesRegularExpression($pattern, trim($appTester->getDisplay(true)));
|
||||
} else {
|
||||
$this->assertSame(trim($expected), trim($appTester->getDisplay(true)));
|
||||
}
|
||||
|
||||
// workaround until https://github.com/symfony/symfony/pull/46747 is merged
|
||||
putenv('SHELL_VERBOSITY');
|
||||
unset($_ENV['SHELL_VERBOSITY']);
|
||||
unset($_SERVER['SHELL_VERBOSITY']);
|
||||
}
|
||||
|
||||
public function provideRequire(): \Generator
|
||||
{
|
||||
yield 'warn once for missing ext but a lower package matches' => [
|
||||
[
|
||||
'repositories' => [
|
||||
'packages' => [
|
||||
'type' => 'package',
|
||||
'package' => [
|
||||
['name' => 'required/pkg', 'version' => '1.2.0', 'require' => ['ext-foobar' => '^1']],
|
||||
['name' => 'required/pkg', 'version' => '1.1.0', 'require' => ['ext-foobar' => '^1']],
|
||||
['name' => 'required/pkg', 'version' => '1.0.0'],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
['packages' => ['required/pkg']],
|
||||
<<<OUTPUT
|
||||
<warning>Cannot use required/pkg's latest version 1.2.0 as it requires ext-foobar ^1 which is missing from your platform.
|
||||
Using version ^1.0 for required/pkg
|
||||
./composer.json has been updated
|
||||
Running composer update required/pkg
|
||||
Loading composer repositories with package information
|
||||
Updating dependencies
|
||||
Lock file operations: 1 install, 0 updates, 0 removals
|
||||
- Locking required/pkg (1.0.0)
|
||||
Installing dependencies from lock file (including require-dev)
|
||||
Package operations: 1 install, 0 updates, 0 removals
|
||||
- Installing required/pkg (1.0.0)
|
||||
OUTPUT
|
||||
];
|
||||
|
||||
yield 'warn multiple times when verbose' => [
|
||||
[
|
||||
'repositories' => [
|
||||
'packages' => [
|
||||
'type' => 'package',
|
||||
'package' => [
|
||||
['name' => 'required/pkg', 'version' => '1.2.0', 'require' => ['ext-foobar' => '^1']],
|
||||
['name' => 'required/pkg', 'version' => '1.1.0', 'require' => ['ext-foobar' => '^1']],
|
||||
['name' => 'required/pkg', 'version' => '1.0.0'],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
['packages' => ['required/pkg'], '--no-install' => true, '-v' => true],
|
||||
<<<OUTPUT
|
||||
<warning>Cannot use required/pkg's latest version 1.2.0 as it requires ext-foobar ^1 which is missing from your platform.
|
||||
<warning>Cannot use required/pkg 1.1.0 as it requires ext-foobar ^1 which is missing from your platform.
|
||||
Using version ^1.0 for required/pkg
|
||||
./composer.json has been updated
|
||||
Running composer update required/pkg
|
||||
Loading composer repositories with package information
|
||||
Updating dependencies
|
||||
Dependency resolution completed in %d seconds
|
||||
Analyzed %d packages to resolve dependencies
|
||||
Analyzed %d rules to resolve dependencies
|
||||
Lock file operations: 1 install, 0 updates, 0 removals
|
||||
Installs: required/pkg:1.0.0
|
||||
- Locking required/pkg (1.0.0)
|
||||
OUTPUT
|
||||
];
|
||||
|
||||
yield 'warn for not satisfied req which is satisfied by lower version' => [
|
||||
[
|
||||
'repositories' => [
|
||||
'packages' => [
|
||||
'type' => 'package',
|
||||
'package' => [
|
||||
['name' => 'required/pkg', 'version' => '1.1.0', 'require' => ['php' => '^20']],
|
||||
['name' => 'required/pkg', 'version' => '1.0.0', 'require' => ['php' => '>=7']],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
['packages' => ['required/pkg'], '--no-install' => true],
|
||||
<<<OUTPUT
|
||||
<warning>Cannot use required/pkg's latest version 1.1.0 as it requires php ^20 which is not satisfied by your platform.
|
||||
Using version ^1.0 for required/pkg
|
||||
./composer.json has been updated
|
||||
Running composer update required/pkg
|
||||
Loading composer repositories with package information
|
||||
Updating dependencies
|
||||
Lock file operations: 1 install, 0 updates, 0 removals
|
||||
- Locking required/pkg (1.0.0)
|
||||
OUTPUT
|
||||
];
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue