1
0
Fork 0

Give a clearer error message explaining how to update a conflicting locked dependency

pull/8729/head
Nils Adermann 2020-04-02 15:32:02 +02:00
parent 1b55b466fb
commit 9858718ef6
5 changed files with 80 additions and 3 deletions

View File

@ -268,8 +268,8 @@ class PoolBuilder
} }
} }
// if we're doing a partial update with deps and we're not loading an initial fixed package // if we're doing a partial update with deps we also need to unfix packages which are being replaced in case they
// we also need to trigger an update for transitive deps which are being replaced // are currently locked and thus prevent this updateable package from being installable/updateable
if ($propagateUpdate && $request->getUpdateAllowTransitiveDependencies()) { if ($propagateUpdate && $request->getUpdateAllowTransitiveDependencies()) {
foreach ($package->getReplaces() as $link) { foreach ($package->getReplaces() as $link) {
$replace = $link->getTarget(); $replace = $link->getTarget();

View File

@ -92,7 +92,6 @@ class Problem
} }
$messages = array(); $messages = array();
foreach ($reasons as $rule) { foreach ($reasons as $rule) {
$messages[] = $rule->getPrettyString($repositorySet, $request, $pool, $installedMap, $learnedPool); $messages[] = $rule->getPrettyString($repositorySet, $request, $pool, $installedMap, $learnedPool);
} }
@ -100,6 +99,17 @@ class Problem
return "\n - ".implode("\n - ", $messages); return "\n - ".implode("\n - ", $messages);
} }
public function isCausedByLock()
{
foreach ($this->reasons as $sectionRules) {
foreach ($sectionRules as $rule) {
if ($rule->isCausedByLock()) {
return true;
}
}
}
}
/** /**
* Store a reason descriptor but ignore duplicates * Store a reason descriptor but ignore duplicates
* *

View File

@ -123,6 +123,11 @@ abstract class Rule
abstract public function isAssertion(); abstract public function isAssertion();
public function isCausedByLock()
{
return $this->getReason() === self::RULE_FIXED && $this->reasonData['lockable'];
}
public function getPrettyString(RepositorySet $repositorySet, Request $request, Pool $pool, array $installedMap = array(), array $learnedPool = array()) public function getPrettyString(RepositorySet $repositorySet, Request $request, Pool $pool, array $installedMap = array(), array $learnedPool = array())
{ {
$literals = $this->getLiterals(); $literals = $this->getLiterals();

View File

@ -36,12 +36,15 @@ class SolverProblemsException extends \RuntimeException
$installedMap = $request->getPresentMap(true); $installedMap = $request->getPresentMap(true);
$text = "\n"; $text = "\n";
$hasExtensionProblems = false; $hasExtensionProblems = false;
$isCausedByLock = false;
foreach ($this->problems as $i => $problem) { foreach ($this->problems as $i => $problem) {
$text .= " Problem ".($i + 1).$problem->getPrettyString($repositorySet, $request, $pool, $installedMap, $this->learnedPool)."\n"; $text .= " Problem ".($i + 1).$problem->getPrettyString($repositorySet, $request, $pool, $installedMap, $this->learnedPool)."\n";
if (!$hasExtensionProblems && $this->hasExtensionProblems($problem->getReasons())) { if (!$hasExtensionProblems && $this->hasExtensionProblems($problem->getReasons())) {
$hasExtensionProblems = true; $hasExtensionProblems = true;
} }
$isCausedByLock |= $problem->isCausedByLock();
} }
if (!$isDevExtraction && (strpos($text, 'could not be found') || strpos($text, 'no matching package found'))) { if (!$isDevExtraction && (strpos($text, 'could not be found') || strpos($text, 'no matching package found'))) {
@ -52,6 +55,10 @@ class SolverProblemsException extends \RuntimeException
$text .= $this->createExtensionHint(); $text .= $this->createExtensionHint();
} }
if ($isCausedByLock && !$isDevExtraction) {
$text .= "\nUse the option --with-all-dependencies to allow updates and removals for packages currently locked to specific versions.";
}
return $text; return $text;
} }

View File

@ -0,0 +1,55 @@
--TEST--
Require a new package in the composer.json and updating with its name as an argument and with-dependencies should remove packages it replaces which are not root requirements
--COMPOSER--
{
"repositories": [
{
"type": "package",
"package": [
{ "name": "current/pkg", "version": "1.0.0", "require": { "current/dep": "*" } },
{ "name": "current/dep", "version": "1.0.0" },
{ "name": "new/pkg", "version": "1.0.0", "replace": { "current/dep": "1.0.0" } }
]
}
],
"require": {
"current/pkg": "1.*",
"new/pkg": "1.*"
}
}
--INSTALLED--
[
{ "name": "current/pkg", "version": "1.0.0", "require": { "current/dep": "*" } },
{ "name": "current/dep", "version": "1.0.0" }
]
--LOCK--
{
"packages": [
{ "name": "current/pkg", "version": "1.0.0", "require": { "current/dep": "*" } },
{ "name": "current/dep", "version": "1.0.0" }
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "dev",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": []
}
--RUN--
update new/pkg
--EXPECT-EXIT-CODE--
2
--EXPECT-OUTPUT--
Loading composer repositories with package information
Updating dependencies
Your requirements could not be resolved to an installable set of packages.
Problem 1
- current/dep is locked to version 1.0.0 and an update of this package was not requested.
- new/pkg 1.0.0 can not be installed as that would require removing current/dep 1.0.0. new/pkg replaces current/dep and can thus not coexist with it.
- Root composer.json requires new/pkg 1.* -> satisfiable by new/pkg[1.0.0].
Use the option --with-all-dependencies to allow updates and removals for packages currently locked to specific versions.
--EXPECT--