Move partial update handling to pool builder
parent
6679dde713
commit
da84763f03
|
@ -43,6 +43,8 @@ class PoolBuilder
|
||||||
private $packages = array();
|
private $packages = array();
|
||||||
private $unacceptableFixedPackages = array();
|
private $unacceptableFixedPackages = array();
|
||||||
|
|
||||||
|
private $unfixList = array();
|
||||||
|
|
||||||
public function __construct(array $acceptableStabilities, array $stabilityFlags, array $rootAliases, array $rootReferences, EventDispatcher $eventDispatcher = null)
|
public function __construct(array $acceptableStabilities, array $stabilityFlags, array $rootAliases, array $rootReferences, EventDispatcher $eventDispatcher = null)
|
||||||
{
|
{
|
||||||
$this->acceptableStabilities = $acceptableStabilities;
|
$this->acceptableStabilities = $acceptableStabilities;
|
||||||
|
@ -54,6 +56,16 @@ class PoolBuilder
|
||||||
|
|
||||||
public function buildPool(array $repositories, Request $request)
|
public function buildPool(array $repositories, Request $request)
|
||||||
{
|
{
|
||||||
|
if ($request->getUpdateAllowList()) {
|
||||||
|
$this->unfixList = $request->getUpdateAllowList();
|
||||||
|
|
||||||
|
foreach ($request->getLockedRepository()->getPackages() as $lockedPackage) {
|
||||||
|
if (!$this->isUpdateable($lockedPackage)) {
|
||||||
|
$request->fixPackage($lockedPackage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$loadNames = array();
|
$loadNames = array();
|
||||||
foreach ($request->getFixedPackages() as $package) {
|
foreach ($request->getFixedPackages() as $package) {
|
||||||
$this->nameConstraints[$package->getName()] = null;
|
$this->nameConstraints[$package->getName()] = null;
|
||||||
|
@ -218,6 +230,9 @@ class PoolBuilder
|
||||||
if (!isset($this->loadedNames[$require])) {
|
if (!isset($this->loadedNames[$require])) {
|
||||||
$loadNames[$require] = null;
|
$loadNames[$require] = null;
|
||||||
}
|
}
|
||||||
|
if (isset($request->getUpdateAllowList()[$package->getName()])) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
$linkConstraint = $link->getConstraint();
|
$linkConstraint = $link->getConstraint();
|
||||||
if ($linkConstraint && !($linkConstraint instanceof EmptyConstraint)) {
|
if ($linkConstraint && !($linkConstraint instanceof EmptyConstraint)) {
|
||||||
|
@ -235,5 +250,123 @@ class PoolBuilder
|
||||||
|
|
||||||
return $loadNames;
|
return $loadNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds all dependencies of the update whitelist to the whitelist, too.
|
||||||
|
*
|
||||||
|
* Packages which are listed as requirements in the root package will be
|
||||||
|
* skipped including their dependencies, unless they are listed in the
|
||||||
|
* update whitelist themselves or $whitelistAllDependencies is true.
|
||||||
|
*
|
||||||
|
* @param RepositoryInterface $lockRepo Use the locked repo
|
||||||
|
* As we want the most accurate package list to work with, and installed
|
||||||
|
* repo might be empty but locked repo will always be current.
|
||||||
|
* @param array $rootRequires An array of links to packages in require of the root package
|
||||||
|
* @param array $rootDevRequires An array of links to packages in require-dev of the root package
|
||||||
|
*/
|
||||||
|
private function whitelistUpdateDependencies($lockRepo, array $rootRequires, array $rootDevRequires)
|
||||||
|
{
|
||||||
|
$rootRequires = array_merge($rootRequires, $rootDevRequires);
|
||||||
|
|
||||||
|
$skipPackages = array();
|
||||||
|
if (!$this->whitelistAllDependencies) {
|
||||||
|
foreach ($rootRequires as $require) {
|
||||||
|
$skipPackages[$require->getTarget()] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$installedRepo = new InstalledRepository(array($lockRepo));
|
||||||
|
|
||||||
|
$seen = array();
|
||||||
|
|
||||||
|
$rootRequiredPackageNames = array_keys($rootRequires);
|
||||||
|
|
||||||
|
foreach ($this->updateWhitelist as $packageName => $void) {
|
||||||
|
$packageQueue = new \SplQueue;
|
||||||
|
$nameMatchesRequiredPackage = false;
|
||||||
|
|
||||||
|
$depPackages = $installedRepo->findPackagesWithReplacersAndProviders($packageName);
|
||||||
|
$matchesByPattern = array();
|
||||||
|
|
||||||
|
// check if the name is a glob pattern that did not match directly
|
||||||
|
if (empty($depPackages)) {
|
||||||
|
// add any installed package matching the whitelisted name/pattern
|
||||||
|
$whitelistPatternSearchRegexp = BasePackage::packageNameToRegexp($packageName, '^%s$');
|
||||||
|
foreach ($lockRepo->search($whitelistPatternSearchRegexp) as $installedPackage) {
|
||||||
|
$matchesByPattern[] = $installedRepo->findPackages($installedPackage['name']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add root requirements which match the whitelisted name/pattern
|
||||||
|
$whitelistPatternRegexp = BasePackage::packageNameToRegexp($packageName);
|
||||||
|
foreach ($rootRequiredPackageNames as $rootRequiredPackageName) {
|
||||||
|
if (preg_match($whitelistPatternRegexp, $rootRequiredPackageName)) {
|
||||||
|
$nameMatchesRequiredPackage = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($matchesByPattern)) {
|
||||||
|
$depPackages = array_merge($depPackages, call_user_func_array('array_merge', $matchesByPattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($depPackages) == 0 && !$nameMatchesRequiredPackage) {
|
||||||
|
$this->io->writeError('<warning>Package "' . $packageName . '" listed for update is not installed. Ignoring.</warning>');
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($depPackages as $depPackage) {
|
||||||
|
$packageQueue->enqueue($depPackage);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!$packageQueue->isEmpty()) {
|
||||||
|
$package = $packageQueue->dequeue();
|
||||||
|
if (isset($seen[spl_object_hash($package)])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$seen[spl_object_hash($package)] = true;
|
||||||
|
$this->updateWhitelist[$package->getName()] = true;
|
||||||
|
|
||||||
|
if (!$this->whitelistTransitiveDependencies && !$this->whitelistAllDependencies) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$requires = $package->getRequires();
|
||||||
|
|
||||||
|
foreach ($requires as $require) {
|
||||||
|
$requirePackages = $installedRepo->findPackagesWithReplacersAndProviders($require->getTarget());
|
||||||
|
|
||||||
|
foreach ($requirePackages as $requirePackage) {
|
||||||
|
if (isset($this->updateWhitelist[$requirePackage->getName()])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($skipPackages[$requirePackage->getName()]) && !preg_match(BasePackage::packageNameToRegexp($packageName), $requirePackage->getName())) {
|
||||||
|
$this->io->writeError('<warning>Dependency "' . $requirePackage->getName() . '" is also a root requirement, but is not explicitly whitelisted. Ignoring.</warning>');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$packageQueue->enqueue($requirePackage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param PackageInterface $package
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function isUpdateable(PackageInterface $package)
|
||||||
|
{
|
||||||
|
foreach ($this->unfixList as $pattern => $void) {
|
||||||
|
$patternRegexp = BasePackage::packageNameToRegexp($pattern);
|
||||||
|
if (preg_match($patternRegexp, $package->getName())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,9 @@ class Request
|
||||||
protected $requires = array();
|
protected $requires = array();
|
||||||
protected $fixedPackages = array();
|
protected $fixedPackages = array();
|
||||||
protected $unlockables = array();
|
protected $unlockables = array();
|
||||||
|
protected $updateAllowList = array();
|
||||||
|
protected $updateAllowTransitiveDependencies = false;
|
||||||
|
protected $updateAllowTransitiveRootDependencies = false;
|
||||||
|
|
||||||
public function __construct(LockArrayRepository $lockedRepository = null)
|
public function __construct(LockArrayRepository $lockedRepository = null)
|
||||||
{
|
{
|
||||||
|
@ -53,6 +56,18 @@ class Request
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setUpdateAllowList($updateAllowList, $updateAllowTransitiveDependencies, $updateAllowTransitiveRootDependencies)
|
||||||
|
{
|
||||||
|
$this->updateAllowList = $updateAllowList;
|
||||||
|
$this->updateAllowTransitiveDependencies = $updateAllowTransitiveDependencies;
|
||||||
|
$this->updateAllowTransitiveRootDependencies = $updateAllowTransitiveRootDependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUpdateAllowList()
|
||||||
|
{
|
||||||
|
return $this->updateAllowList;
|
||||||
|
}
|
||||||
|
|
||||||
public function getRequires()
|
public function getRequires()
|
||||||
{
|
{
|
||||||
return $this->requires;
|
return $this->requires;
|
||||||
|
|
|
@ -356,12 +356,12 @@ class Installer
|
||||||
if (!$lockedRepository) {
|
if (!$lockedRepository) {
|
||||||
$this->io->writeError('<error>Cannot update only a partial set of packages without a lock file present.</error>', true, IOInterface::QUIET);
|
$this->io->writeError('<error>Cannot update only a partial set of packages without a lock file present.</error>', true, IOInterface::QUIET);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}/*
|
||||||
$this->whitelistUpdateDependencies(
|
$this->whitelistUpdateDependencies(
|
||||||
$lockedRepository,
|
$lockedRepository,
|
||||||
$this->package->getRequires(),
|
$this->package->getRequires(),
|
||||||
$this->package->getDevRequires()
|
$this->package->getDevRequires()
|
||||||
);
|
);*/
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->io->writeError('<info>Loading composer repositories with package information</info>');
|
$this->io->writeError('<info>Loading composer repositories with package information</info>');
|
||||||
|
@ -394,14 +394,9 @@ class Installer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the updateWhitelist is enabled, packages not in it are also fixed
|
// pass the allow list into the request, so the pool builder can apply it
|
||||||
// to the version specified in the lock
|
if ($this->updateWhitelist) {
|
||||||
if ($this->updateWhitelist && $lockedRepository) {
|
$request->setUpdateAllowList($this->updateWhitelist, $this->whitelistTransitiveDependencies, $this->whitelistAllDependencies);
|
||||||
foreach ($lockedRepository->getPackages() as $lockedPackage) {
|
|
||||||
if (!$this->isUpdateable($lockedPackage)) {
|
|
||||||
$request->fixPackage($lockedPackage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$pool = $repositorySet->createPool($request, $this->eventDispatcher);
|
$pool = $repositorySet->createPool($request, $this->eventDispatcher);
|
||||||
|
@ -847,26 +842,6 @@ class Installer
|
||||||
return $normalizedAliases;
|
return $normalizedAliases;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param PackageInterface $package
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private function isUpdateable(PackageInterface $package)
|
|
||||||
{
|
|
||||||
if (!$this->updateWhitelist) {
|
|
||||||
throw new \LogicException('isUpdateable should only be called when a whitelist is present');
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($this->updateWhitelist as $whiteListedPattern => $void) {
|
|
||||||
$patternRegexp = BasePackage::packageNameToRegexp($whiteListedPattern);
|
|
||||||
if (preg_match($patternRegexp, $package->getName())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $links
|
* @param array $links
|
||||||
* @return array
|
* @return array
|
||||||
|
@ -883,108 +858,6 @@ class Installer
|
||||||
return $platformReqs;
|
return $platformReqs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds all dependencies of the update whitelist to the whitelist, too.
|
|
||||||
*
|
|
||||||
* Packages which are listed as requirements in the root package will be
|
|
||||||
* skipped including their dependencies, unless they are listed in the
|
|
||||||
* update whitelist themselves or $whitelistAllDependencies is true.
|
|
||||||
*
|
|
||||||
* @param RepositoryInterface $lockRepo Use the locked repo
|
|
||||||
* As we want the most accurate package list to work with, and installed
|
|
||||||
* repo might be empty but locked repo will always be current.
|
|
||||||
* @param array $rootRequires An array of links to packages in require of the root package
|
|
||||||
* @param array $rootDevRequires An array of links to packages in require-dev of the root package
|
|
||||||
*/
|
|
||||||
private function whitelistUpdateDependencies($lockRepo, array $rootRequires, array $rootDevRequires)
|
|
||||||
{
|
|
||||||
$rootRequires = array_merge($rootRequires, $rootDevRequires);
|
|
||||||
|
|
||||||
$skipPackages = array();
|
|
||||||
if (!$this->whitelistAllDependencies) {
|
|
||||||
foreach ($rootRequires as $require) {
|
|
||||||
$skipPackages[$require->getTarget()] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$installedRepo = new InstalledRepository(array($lockRepo));
|
|
||||||
|
|
||||||
$seen = array();
|
|
||||||
|
|
||||||
$rootRequiredPackageNames = array_keys($rootRequires);
|
|
||||||
|
|
||||||
foreach ($this->updateWhitelist as $packageName => $void) {
|
|
||||||
$packageQueue = new \SplQueue;
|
|
||||||
$nameMatchesRequiredPackage = false;
|
|
||||||
|
|
||||||
$depPackages = $installedRepo->findPackagesWithReplacersAndProviders($packageName);
|
|
||||||
$matchesByPattern = array();
|
|
||||||
|
|
||||||
// check if the name is a glob pattern that did not match directly
|
|
||||||
if (empty($depPackages)) {
|
|
||||||
// add any installed package matching the whitelisted name/pattern
|
|
||||||
$whitelistPatternSearchRegexp = BasePackage::packageNameToRegexp($packageName, '^%s$');
|
|
||||||
foreach ($lockRepo->search($whitelistPatternSearchRegexp) as $installedPackage) {
|
|
||||||
$matchesByPattern[] = $installedRepo->findPackages($installedPackage['name']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add root requirements which match the whitelisted name/pattern
|
|
||||||
$whitelistPatternRegexp = BasePackage::packageNameToRegexp($packageName);
|
|
||||||
foreach ($rootRequiredPackageNames as $rootRequiredPackageName) {
|
|
||||||
if (preg_match($whitelistPatternRegexp, $rootRequiredPackageName)) {
|
|
||||||
$nameMatchesRequiredPackage = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($matchesByPattern)) {
|
|
||||||
$depPackages = array_merge($depPackages, call_user_func_array('array_merge', $matchesByPattern));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count($depPackages) == 0 && !$nameMatchesRequiredPackage) {
|
|
||||||
$this->io->writeError('<warning>Package "' . $packageName . '" listed for update is not installed. Ignoring.</warning>');
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($depPackages as $depPackage) {
|
|
||||||
$packageQueue->enqueue($depPackage);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!$packageQueue->isEmpty()) {
|
|
||||||
$package = $packageQueue->dequeue();
|
|
||||||
if (isset($seen[spl_object_hash($package)])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$seen[spl_object_hash($package)] = true;
|
|
||||||
$this->updateWhitelist[$package->getName()] = true;
|
|
||||||
|
|
||||||
if (!$this->whitelistTransitiveDependencies && !$this->whitelistAllDependencies) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$requires = $package->getRequires();
|
|
||||||
|
|
||||||
foreach ($requires as $require) {
|
|
||||||
$requirePackages = $installedRepo->findPackagesWithReplacersAndProviders($require->getTarget());
|
|
||||||
|
|
||||||
foreach ($requirePackages as $requirePackage) {
|
|
||||||
if (isset($this->updateWhitelist[$requirePackage->getName()])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($skipPackages[$requirePackage->getName()]) && !preg_match(BasePackage::packageNameToRegexp($packageName), $requirePackage->getName())) {
|
|
||||||
$this->io->writeError('<warning>Dependency "' . $requirePackage->getName() . '" is also a root requirement, but is not explicitly whitelisted. Ignoring.</warning>');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$packageQueue->enqueue($requirePackage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace local repositories with InstalledArrayRepository instances
|
* Replace local repositories with InstalledArrayRepository instances
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue