1
0
Fork 0

Merge branch '1.7'

pull/7600/head
Jordi Boggiano 2018-08-27 12:34:42 +02:00
commit df2f2dc113
6 changed files with 98 additions and 100 deletions

View File

@ -317,12 +317,12 @@ class Pool implements \Countable
* Checks if the package matches the given constraint directly or through * Checks if the package matches the given constraint directly or through
* provided or replaced packages * provided or replaced packages
* *
* @param array|PackageInterface $candidate * @param PackageInterface $candidate
* @param string $name Name of the package to be matched * @param string $name Name of the package to be matched
* @param ConstraintInterface $constraint The constraint to verify * @param ConstraintInterface $constraint The constraint to verify
* @return int One of the MATCH* constants of this class or 0 if there is no match * @return int One of the MATCH* constants of this class or 0 if there is no match
*/ */
private function match($candidate, $name, ConstraintInterface $constraint = null, $bypassFilters) public function match($candidate, $name, ConstraintInterface $constraint = null, $bypassFilters)
{ {
$candidateName = $candidate->getName(); $candidateName = $candidate->getName();
$candidateVersion = $candidate->getVersion(); $candidateVersion = $candidate->getVersion();

View File

@ -28,6 +28,9 @@ class RuleSetGenerator
protected $installedMap; protected $installedMap;
protected $whitelistedMap; protected $whitelistedMap;
protected $addedMap; protected $addedMap;
protected $conflictAddedMap;
protected $addedPackages;
protected $addedPackagesByNames;
public function __construct(PolicyInterface $policy, Pool $pool) public function __construct(PolicyInterface $policy, Pool $pool)
{ {
@ -185,6 +188,7 @@ class RuleSetGenerator
$workQueue->enqueue($package); $workQueue->enqueue($package);
while (!$workQueue->isEmpty()) { while (!$workQueue->isEmpty()) {
/** @var PackageInterface $package */
$package = $workQueue->dequeue(); $package = $workQueue->dequeue();
if (isset($this->addedMap[$package->id])) { if (isset($this->addedMap[$package->id])) {
continue; continue;
@ -192,6 +196,11 @@ class RuleSetGenerator
$this->addedMap[$package->id] = true; $this->addedMap[$package->id] = true;
$this->addedPackages[] = $package;
foreach ($package->getNames() as $name) {
$this->addedPackagesByNames[$name][] = $package;
}
foreach ($package->getRequires() as $link) { foreach ($package->getRequires() as $link) {
if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) { if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) {
continue; continue;
@ -206,32 +215,6 @@ class RuleSetGenerator
} }
} }
foreach ($package->getConflicts() as $link) {
$possibleConflicts = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
foreach ($possibleConflicts as $conflict) {
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createRule2Literals($package, $conflict, Rule::RULE_PACKAGE_CONFLICT, $link));
}
}
// check obsoletes and implicit obsoletes of a package
$isInstalled = isset($this->installedMap[$package->id]);
foreach ($package->getReplaces() as $link) {
$obsoleteProviders = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
foreach ($obsoleteProviders as $provider) {
if ($provider === $package) {
continue;
}
if (!$this->obsoleteImpossibleForAlias($package, $provider)) {
$reason = $isInstalled ? Rule::RULE_INSTALLED_PACKAGE_OBSOLETES : Rule::RULE_PACKAGE_OBSOLETES;
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createRule2Literals($package, $provider, $reason, $link));
}
}
}
$packageName = $package->getName(); $packageName = $package->getName();
$obsoleteProviders = $this->pool->whatProvides($packageName, null); $obsoleteProviders = $this->pool->whatProvides($packageName, null);
@ -250,6 +233,49 @@ class RuleSetGenerator
} }
} }
protected function addConflictRules()
{
/** @var PackageInterface $package */
foreach ($this->addedPackages as $package) {
foreach ($package->getConflicts() as $link) {
if (!isset($this->addedPackagesByNames[$link->getTarget()])) {
continue;
}
/** @var PackageInterface $possibleConflict */
foreach ($this->addedPackagesByNames[$link->getTarget()] as $possibleConflict) {
$conflictMatch = $this->pool->match($possibleConflict, $link->getTarget(), $link->getConstraint(), true);
if ($conflictMatch === Pool::MATCH || $conflictMatch === Pool::MATCH_REPLACE) {
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createRule2Literals($package, $possibleConflict, Rule::RULE_PACKAGE_CONFLICT, $link));
}
}
}
// check obsoletes and implicit obsoletes of a package
$isInstalled = isset($this->installedMap[$package->id]);
foreach ($package->getReplaces() as $link) {
if (!isset($this->addedPackagesByNames[$link->getTarget()])) {
continue;
}
/** @var PackageInterface $possibleConflict */
foreach ($this->addedPackagesByNames[$link->getTarget()] as $provider) {
if ($provider === $package) {
continue;
}
if (!$this->obsoleteImpossibleForAlias($package, $provider)) {
$reason = $isInstalled ? Rule::RULE_INSTALLED_PACKAGE_OBSOLETES : Rule::RULE_PACKAGE_OBSOLETES;
$this->addRule(RuleSet::TYPE_PACKAGE, $this->createRule2Literals($package, $provider, $reason, $link));
}
}
}
}
}
protected function obsoleteImpossibleForAlias($package, $provider) protected function obsoleteImpossibleForAlias($package, $provider)
{ {
$packageIsAlias = $package instanceof AliasPackage; $packageIsAlias = $package instanceof AliasPackage;
@ -327,12 +353,20 @@ class RuleSetGenerator
$this->pool->setWhitelist($this->whitelistedMap); $this->pool->setWhitelist($this->whitelistedMap);
$this->addedMap = array(); $this->addedMap = array();
$this->conflictAddedMap = array();
$this->addedPackages = array();
$this->addedPackagesByNames = array();
foreach ($this->installedMap as $package) { foreach ($this->installedMap as $package) {
$this->addRulesForPackage($package, $ignorePlatformReqs); $this->addRulesForPackage($package, $ignorePlatformReqs);
} }
$this->addRulesForJobs($ignorePlatformReqs); $this->addRulesForJobs($ignorePlatformReqs);
$this->addConflictRules();
// Remove references to packages
$this->addedPackages = $this->addedPackagesByNames = null;
return $this->rules; return $this->rules;
} }
} }

View File

@ -183,30 +183,13 @@ class GitHubDriver extends VcsDriver
return $this->gitDriver->getFileContent($file, $identifier); return $this->gitDriver->getFileContent($file, $identifier);
} }
$notFoundRetries = 2; $resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/contents/' . $file . '?ref='.urlencode($identifier);
while ($notFoundRetries) { $resource = JsonFile::parseJson($this->getContents($resource));
try { if (empty($resource['content']) || $resource['encoding'] !== 'base64' || !($content = base64_decode($resource['content']))) {
$resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/contents/' . $file . '?ref='.urlencode($identifier); throw new \RuntimeException('Could not retrieve ' . $file . ' for '.$identifier);
$resource = JsonFile::parseJson($this->getContents($resource));
if (empty($resource['content']) || $resource['encoding'] !== 'base64' || !($content = base64_decode($resource['content']))) {
throw new \RuntimeException('Could not retrieve ' . $file . ' for '.$identifier);
}
return $content;
} catch (TransportException $e) {
if (404 !== $e->getCode()) {
throw $e;
}
// TODO should be removed when possible
// retry fetching if github returns a 404 since they happen randomly
$notFoundRetries--;
return null;
}
} }
return null; return $content;
} }
/** /**
@ -378,7 +361,7 @@ class GitHubDriver extends VcsDriver
return $this->attemptCloneFallback(); return $this->attemptCloneFallback();
} }
$rateLimited = $githubUtil->isRateLimited($e->getHeaders()); $rateLimited = $gitHubUtil->isRateLimited($e->getHeaders());
if (!$this->io->hasAuthentication($this->originUrl)) { if (!$this->io->hasAuthentication($this->originUrl)) {
if (!$this->io->isInteractive()) { if (!$this->io->isInteractive()) {
@ -392,7 +375,7 @@ class GitHubDriver extends VcsDriver
} }
if ($rateLimited) { if ($rateLimited) {
$rateLimit = $githubUtil->getRateLimit($e->getHeaders()); $rateLimit = $gitHubUtil->getRateLimit($e->getHeaders());
$this->io->writeError(sprintf( $this->io->writeError(sprintf(
'<error>GitHub API limit (%d calls/hr) is exhausted. You are already authorized so you have to wait until %s before doing more requests</error>', '<error>GitHub API limit (%d calls/hr) is exhausted. You are already authorized so you have to wait until %s before doing more requests</error>',
$rateLimit['limit'], $rateLimit['limit'],

View File

@ -1,5 +1,5 @@
--TEST-- --TEST--
Requiring a replaced package in a version, that is not provided by the replacing package, should install correctly (although that is not a very smart idea) Requiring a replaced package in a version, that is not provided by the replacing package, should result in a conflict, when installing from lock
--COMPOSER-- --COMPOSER--
{ {
"repositories": [ "repositories": [
@ -17,9 +17,7 @@ Requiring a replaced package in a version, that is not provided by the replacing
"foo/replaced": "2.0.0" "foo/replaced": "2.0.0"
} }
} }
--RUN-- --LOCK--
install
--EXPECT-LOCK--
{ {
"packages": [ "packages": [
{ "name": "foo/original", "version": "1.0.0", "replace": {"foo/replaced": "1.0.0"}, "type": "library" }, { "name": "foo/original", "version": "1.0.0", "replace": {"foo/replaced": "1.0.0"}, "type": "library" },
@ -34,8 +32,8 @@ install
"platform": [], "platform": [],
"platform-dev": [] "platform-dev": []
} }
--RUN--
install
--EXPECT-EXIT-CODE-- --EXPECT-EXIT-CODE--
0 2
--EXPECT-- --EXPECT--
Installing foo/original (1.0.0)
Installing foo/replaced (2.0.0)

View File

@ -0,0 +1,24 @@
--TEST--
Requiring a replaced package in a version, that is not provided by the replacing package, should result in a conflict
--COMPOSER--
{
"repositories": [
{
"type": "package",
"package": [
{ "name": "foo/original", "version": "1.0.0", "replace": {"foo/replaced": "1.0.0"} },
{ "name": "foo/replaced", "version": "1.0.0" },
{ "name": "foo/replaced", "version": "2.0.0" }
]
}
],
"require": {
"foo/original": "1.0.0",
"foo/replaced": "2.0.0"
}
}
--RUN--
install
--EXPECT-EXIT-CODE--
2
--EXPECT--

View File

@ -1,41 +0,0 @@
--TEST--
Requiring a replaced package in a version, that is not provided by the replacing package, should install correctly (although that is not a very smart idea) also when installing from lock
--COMPOSER--
{
"repositories": [
{
"type": "package",
"package": [
{ "name": "foo/original", "version": "1.0.0", "replace": {"foo/replaced": "1.0.0"} },
{ "name": "foo/replaced", "version": "1.0.0" },
{ "name": "foo/replaced", "version": "2.0.0" }
]
}
],
"require": {
"foo/original": "1.0.0",
"foo/replaced": "2.0.0"
}
}
--LOCK--
{
"packages": [
{ "name": "foo/original", "version": "1.0.0", "replace": {"foo/replaced": "1.0.0"}, "type": "library" },
{ "name": "foo/replaced", "version": "2.0.0", "type": "library" }
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": []
}
--RUN--
install
--EXPECT-EXIT-CODE--
0
--EXPECT--
Installing foo/original (1.0.0)
Installing foo/replaced (2.0.0)