Merge pull request #8717 from naderman/t/pool-builder-allow-list
Move processing of partial update argument list into the pool builderpull/8729/head
commit
379baa1560
|
@ -159,7 +159,7 @@ php composer.phar update
|
|||
> if the `composer.lock` has not been updated since changes were made to the
|
||||
> `composer.json` that might affect dependency resolution.
|
||||
|
||||
If you only want to install, upgrade or remove one dependency, you can whitelist them:
|
||||
If you only want to install, upgrade or remove one dependency, you can explicitly list it as an argument:
|
||||
|
||||
```sh
|
||||
php composer.phar update monolog/monolog [...]
|
||||
|
|
|
@ -155,8 +155,8 @@ php composer.phar update "vendor/*"
|
|||
* **--no-scripts:** Skips execution of scripts defined in `composer.json`.
|
||||
* **--no-progress:** Removes the progress display that can mess with some
|
||||
terminals or scripts which don't handle backspace characters.
|
||||
* **--with-dependencies:** Add also dependencies of whitelisted packages to the whitelist, except those that are root requirements.
|
||||
* **--with-all-dependencies:** Add also all dependencies of whitelisted packages to the whitelist, including those that are root requirements.
|
||||
* **--with-dependencies:** Update also dependencies of packages in the argument list, except those which are root requirements.
|
||||
* **--with-all-dependencies:** Update also dependencies of packages in the argument list, including those which are root requirements.
|
||||
* **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to get a faster
|
||||
autoloader. This is recommended especially for production, but can take
|
||||
a bit of time to run so it is currently not done by default.
|
||||
|
|
|
@ -28,20 +28,20 @@ class Cache
|
|||
private $io;
|
||||
private $root;
|
||||
private $enabled = true;
|
||||
private $whitelist;
|
||||
private $allowlist;
|
||||
private $filesystem;
|
||||
|
||||
/**
|
||||
* @param IOInterface $io
|
||||
* @param string $cacheDir location of the cache
|
||||
* @param string $whitelist List of characters that are allowed in path names (used in a regex character class)
|
||||
* @param string $allowlist List of characters that are allowed in path names (used in a regex character class)
|
||||
* @param Filesystem $filesystem optional filesystem instance
|
||||
*/
|
||||
public function __construct(IOInterface $io, $cacheDir, $whitelist = 'a-z0-9.', Filesystem $filesystem = null)
|
||||
public function __construct(IOInterface $io, $cacheDir, $allowlist = 'a-z0-9.', Filesystem $filesystem = null)
|
||||
{
|
||||
$this->io = $io;
|
||||
$this->root = rtrim($cacheDir, '/\\') . '/';
|
||||
$this->whitelist = $whitelist;
|
||||
$this->allowlist = $allowlist;
|
||||
$this->filesystem = $filesystem ?: new Filesystem();
|
||||
|
||||
if (!self::isUsable($cacheDir)) {
|
||||
|
@ -77,7 +77,7 @@ class Cache
|
|||
public function read($file)
|
||||
{
|
||||
if ($this->enabled) {
|
||||
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
|
||||
$file = preg_replace('{[^'.$this->allowlist.']}i', '-', $file);
|
||||
if (file_exists($this->root . $file)) {
|
||||
$this->io->writeError('Reading '.$this->root . $file.' from cache', true, IOInterface::DEBUG);
|
||||
|
||||
|
@ -91,7 +91,7 @@ class Cache
|
|||
public function write($file, $contents)
|
||||
{
|
||||
if ($this->enabled) {
|
||||
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
|
||||
$file = preg_replace('{[^'.$this->allowlist.']}i', '-', $file);
|
||||
|
||||
$this->io->writeError('Writing '.$this->root . $file.' into cache', true, IOInterface::DEBUG);
|
||||
|
||||
|
@ -129,7 +129,7 @@ class Cache
|
|||
public function copyFrom($file, $source)
|
||||
{
|
||||
if ($this->enabled) {
|
||||
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
|
||||
$file = preg_replace('{[^'.$this->allowlist.']}i', '-', $file);
|
||||
$this->filesystem->ensureDirectoryExists(dirname($this->root . $file));
|
||||
|
||||
if (!file_exists($source)) {
|
||||
|
@ -150,7 +150,7 @@ class Cache
|
|||
public function copyTo($file, $target)
|
||||
{
|
||||
if ($this->enabled) {
|
||||
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
|
||||
$file = preg_replace('{[^'.$this->allowlist.']}i', '-', $file);
|
||||
if (file_exists($this->root . $file)) {
|
||||
try {
|
||||
touch($this->root . $file, filemtime($this->root . $file), time());
|
||||
|
@ -177,7 +177,7 @@ class Cache
|
|||
public function remove($file)
|
||||
{
|
||||
if ($this->enabled) {
|
||||
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
|
||||
$file = preg_replace('{[^'.$this->allowlist.']}i', '-', $file);
|
||||
if (file_exists($this->root . $file)) {
|
||||
return $this->filesystem->unlink($this->root . $file);
|
||||
}
|
||||
|
@ -229,7 +229,7 @@ class Cache
|
|||
public function sha1($file)
|
||||
{
|
||||
if ($this->enabled) {
|
||||
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
|
||||
$file = preg_replace('{[^'.$this->allowlist.']}i', '-', $file);
|
||||
if (file_exists($this->root . $file)) {
|
||||
return sha1_file($this->root . $file);
|
||||
}
|
||||
|
@ -241,7 +241,7 @@ class Cache
|
|||
public function sha256($file)
|
||||
{
|
||||
if ($this->enabled) {
|
||||
$file = preg_replace('{[^'.$this->whitelist.']}i', '-', $file);
|
||||
$file = preg_replace('{[^'.$this->allowlist.']}i', '-', $file);
|
||||
if (file_exists($this->root . $file)) {
|
||||
return hash_file('sha256', $this->root . $file);
|
||||
}
|
||||
|
|
|
@ -86,8 +86,8 @@ EOT
|
|||
{
|
||||
$io = $this->getIO();
|
||||
|
||||
$whitelist = array('name', 'description', 'author', 'type', 'homepage', 'require', 'require-dev', 'stability', 'license');
|
||||
$options = array_filter(array_intersect_key($input->getOptions(), array_flip($whitelist)));
|
||||
$allowlist = array('name', 'description', 'author', 'type', 'homepage', 'require', 'require-dev', 'stability', 'license');
|
||||
$options = array_filter(array_intersect_key($input->getOptions(), array_flip($allowlist)));
|
||||
|
||||
if (isset($options['author'])) {
|
||||
$options['authors'] = $this->formatAuthors($options['author']);
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
namespace Composer\Command;
|
||||
|
||||
use Composer\Config\JsonConfigSource;
|
||||
use Composer\DependencyResolver\Request;
|
||||
use Composer\Installer;
|
||||
use Composer\Plugin\CommandEvent;
|
||||
use Composer\Plugin\PluginEvents;
|
||||
|
@ -179,8 +180,8 @@ EOT
|
|||
->setClassMapAuthoritative($authoritative)
|
||||
->setApcuAutoloader($apcu)
|
||||
->setUpdate(true)
|
||||
->setUpdateWhitelist($packages)
|
||||
->setWhitelistTransitiveDependencies(!$input->getOption('no-update-with-dependencies'))
|
||||
->setUpdateAllowList($packages)
|
||||
->setUpdateAllowTransitiveDependencies($input->getOption('no-update-with-dependencies') ? Request::UPDATE_ONLY_LISTED : Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE)
|
||||
->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))
|
||||
->setRunScripts(!$input->getOption('no-scripts'))
|
||||
->setDryRun($dryRun)
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
namespace Composer\Command;
|
||||
|
||||
use Composer\DependencyResolver\Request;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
@ -248,6 +249,13 @@ EOT
|
|||
$authoritative = $input->getOption('classmap-authoritative') || $composer->getConfig()->get('classmap-authoritative');
|
||||
$apcu = $input->getOption('apcu-autoloader') || $composer->getConfig()->get('apcu-autoloader');
|
||||
|
||||
$updateAllowTransitiveDependencies = Request::UPDATE_ONLY_LISTED;
|
||||
if ($input->getOption('update-with-all-dependencies')) {
|
||||
$updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS;
|
||||
} elseif ($input->getOption('update-with-dependencies')) {
|
||||
$updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE;
|
||||
}
|
||||
|
||||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'require', $input, $output);
|
||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||
|
||||
|
@ -264,8 +272,7 @@ EOT
|
|||
->setClassMapAuthoritative($authoritative)
|
||||
->setApcuAutoloader($apcu)
|
||||
->setUpdate(true)
|
||||
->setWhitelistTransitiveDependencies($input->getOption('update-with-dependencies'))
|
||||
->setWhitelistAllDependencies($input->getOption('update-with-all-dependencies'))
|
||||
->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies)
|
||||
->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))
|
||||
->setPreferStable($input->getOption('prefer-stable'))
|
||||
->setPreferLowest($input->getOption('prefer-lowest'))
|
||||
|
@ -275,7 +282,7 @@ EOT
|
|||
// if no lock is present, or the file is brand new, we do not do a
|
||||
// partial update as this is not supported by the Installer
|
||||
if (!$this->firstRequire && $composer->getConfig()->get('lock')) {
|
||||
$install->setUpdateWhitelist(array_keys($requirements));
|
||||
$install->setUpdateAllowList(array_keys($requirements));
|
||||
}
|
||||
|
||||
$status = $install->run();
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
namespace Composer\Command;
|
||||
|
||||
use Composer\Composer;
|
||||
use Composer\DependencyResolver\Request;
|
||||
use Composer\Installer;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Plugin\CommandEvent;
|
||||
|
@ -48,8 +49,8 @@ class UpdateCommand extends BaseCommand
|
|||
new InputOption('no-autoloader', null, InputOption::VALUE_NONE, 'Skips autoloader generation'),
|
||||
new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'),
|
||||
new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'),
|
||||
new InputOption('with-dependencies', null, InputOption::VALUE_NONE, 'Add also dependencies of whitelisted packages to the whitelist, except those defined in root package.'),
|
||||
new InputOption('with-all-dependencies', null, InputOption::VALUE_NONE, 'Add also all dependencies of whitelisted packages to the whitelist, including those defined in root package.'),
|
||||
new InputOption('with-dependencies', null, InputOption::VALUE_NONE, 'Update also dependencies of packages in the argument list, except those which are root requirements.'),
|
||||
new InputOption('with-all-dependencies', null, InputOption::VALUE_NONE, 'Update also dependencies of packages in the argument list, including those which are root requirements.'),
|
||||
new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'),
|
||||
new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump.'),
|
||||
new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'),
|
||||
|
@ -145,6 +146,13 @@ EOT
|
|||
$authoritative = $input->getOption('classmap-authoritative') || $config->get('classmap-authoritative');
|
||||
$apcu = $input->getOption('apcu-autoloader') || $config->get('apcu-autoloader');
|
||||
|
||||
$updateAllowTransitiveDependencies = Request::UPDATE_ONLY_LISTED;
|
||||
if ($input->getOption('with-all-dependencies')) {
|
||||
$updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS;
|
||||
} elseif ($input->getOption('with-dependencies')) {
|
||||
$updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE;
|
||||
}
|
||||
|
||||
$install
|
||||
->setDryRun($input->getOption('dry-run'))
|
||||
->setVerbose($input->getOption('verbose'))
|
||||
|
@ -158,9 +166,8 @@ EOT
|
|||
->setApcuAutoloader($apcu)
|
||||
->setUpdate(true)
|
||||
->setUpdateMirrors($updateMirrors)
|
||||
->setUpdateWhitelist($packages)
|
||||
->setWhitelistTransitiveDependencies($input->getOption('with-dependencies'))
|
||||
->setWhitelistAllDependencies($input->getOption('with-all-dependencies'))
|
||||
->setUpdateAllowList($packages)
|
||||
->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies)
|
||||
->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))
|
||||
->setPreferStable($input->getOption('prefer-stable'))
|
||||
->setPreferLowest($input->getOption('prefer-lowest'))
|
||||
|
|
|
@ -112,4 +112,22 @@ class LockTransaction extends Transaction
|
|||
|
||||
return $packages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks which of the given aliases from composer.json are actually in use for the lock file
|
||||
*/
|
||||
public function getAliases($aliases)
|
||||
{
|
||||
$usedAliases = array();
|
||||
|
||||
foreach ($this->resultPackages['all'] as $package) {
|
||||
if ($package instanceof AliasPackage) {
|
||||
if (isset($aliases[$package->getName()])) {
|
||||
$usedAliases[$package->getName()] = $aliases[$package->getName()];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $usedAliases;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
namespace Composer\DependencyResolver;
|
||||
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Package\AliasPackage;
|
||||
use Composer\Package\BasePackage;
|
||||
use Composer\Package\Package;
|
||||
|
@ -36,24 +37,46 @@ class PoolBuilder
|
|||
private $rootAliases;
|
||||
private $rootReferences;
|
||||
private $eventDispatcher;
|
||||
private $io;
|
||||
|
||||
private $aliasMap = array();
|
||||
private $nameConstraints = array();
|
||||
private $loadedNames = array();
|
||||
private $packages = array();
|
||||
private $unacceptableFixedPackages = array();
|
||||
private $updateAllowList = array();
|
||||
private $skippedLoad = array();
|
||||
private $updateAllowWarned = 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, IOInterface $io, EventDispatcher $eventDispatcher = null)
|
||||
{
|
||||
$this->acceptableStabilities = $acceptableStabilities;
|
||||
$this->stabilityFlags = $stabilityFlags;
|
||||
$this->rootAliases = $rootAliases;
|
||||
$this->rootReferences = $rootReferences;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
$this->io = $io;
|
||||
}
|
||||
|
||||
public function buildPool(array $repositories, Request $request)
|
||||
{
|
||||
if ($request->getUpdateAllowList()) {
|
||||
$this->updateAllowList = $request->getUpdateAllowList();
|
||||
$this->warnAboutNonMatchingUpdateAllowList($request);
|
||||
|
||||
foreach ($request->getLockedRepository()->getPackages() as $lockedPackage) {
|
||||
if (!$this->isUpdateAllowed($lockedPackage)) {
|
||||
$request->fixPackage($lockedPackage);
|
||||
$lockedName = $lockedPackage->getName();
|
||||
// remember which packages we skipped loading remote content for in this partial update
|
||||
$this->skippedLoad[$lockedPackage->getName()] = $lockedName;
|
||||
foreach ($lockedPackage->getReplaces() as $link) {
|
||||
$this->skippedLoad[$link->getTarget()] = $lockedName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$loadNames = array();
|
||||
foreach ($request->getFixedPackages() as $package) {
|
||||
$this->nameConstraints[$package->getName()] = null;
|
||||
|
@ -73,7 +96,7 @@ class PoolBuilder
|
|||
|| $package->getRepository() instanceof PlatformRepository
|
||||
|| StabilityFilter::isPackageAcceptable($this->acceptableStabilities, $this->stabilityFlags, $package->getNames(), $package->getStability())
|
||||
) {
|
||||
$loadNames += $this->loadPackage($request, $package);
|
||||
$loadNames += $this->loadPackage($request, $package, false);
|
||||
} else {
|
||||
$this->unacceptableFixedPackages[] = $package;
|
||||
}
|
||||
|
@ -108,7 +131,6 @@ class PoolBuilder
|
|||
if ($repository instanceof PlatformRepository || $repository === $request->getLockedRepository()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result = $repository->loadPackages($loadNames, $this->acceptableStabilities, $this->stabilityFlags);
|
||||
|
||||
foreach ($result['namesFound'] as $name) {
|
||||
|
@ -177,9 +199,10 @@ class PoolBuilder
|
|||
return $pool;
|
||||
}
|
||||
|
||||
private function loadPackage(Request $request, PackageInterface $package)
|
||||
private function loadPackage(Request $request, PackageInterface $package, $propagateUpdate = true)
|
||||
{
|
||||
$index = count($this->packages);
|
||||
end($this->packages);
|
||||
$index = key($this->packages) + 1;
|
||||
$this->packages[] = $package;
|
||||
|
||||
if ($package instanceof AliasPackage) {
|
||||
|
@ -198,7 +221,9 @@ class PoolBuilder
|
|||
}
|
||||
}
|
||||
|
||||
if (isset($this->rootAliases[$name][$package->getVersion()])) {
|
||||
// if propogateUpdate is false we are loading a fixed package, root aliases do not apply as they are manually
|
||||
// loaded as separate packages in this case
|
||||
if ($propagateUpdate && isset($this->rootAliases[$name][$package->getVersion()])) {
|
||||
$alias = $this->rootAliases[$name][$package->getVersion()];
|
||||
if ($package instanceof AliasPackage) {
|
||||
$basePackage = $package->getAliasOf();
|
||||
|
@ -217,6 +242,16 @@ class PoolBuilder
|
|||
$require = $link->getTarget();
|
||||
if (!isset($this->loadedNames[$require])) {
|
||||
$loadNames[$require] = null;
|
||||
// if this is a partial update with transitive dependencies we need to unfix the package we now know is a
|
||||
// dependency of another package which we are trying to update, and then attempt to load it again
|
||||
} elseif ($propagateUpdate && $request->getUpdateAllowTransitiveDependencies() && isset($this->skippedLoad[$require])) {
|
||||
if ($request->getUpdateAllowTransitiveRootDependencies() || !$this->isRootRequire($request, $this->skippedLoad[$require])) {
|
||||
$this->unfixPackage($request, $require);
|
||||
$loadNames[$require] = null;
|
||||
} elseif (!$request->getUpdateAllowTransitiveRootDependencies() && $this->isRootRequire($request, $require) && !isset($this->updateAllowWarned[$require])) {
|
||||
$this->updateAllowWarned[$require] = true;
|
||||
$this->io->writeError('<warning>Dependency "'.$require.'" is also a root requirement. Package has not been listed as an update argument, so keeping locked at old version. Use --with-all-dependencies to include root dependencies.</warning>');
|
||||
}
|
||||
}
|
||||
|
||||
$linkConstraint = $link->getConstraint();
|
||||
|
@ -233,7 +268,109 @@ class PoolBuilder
|
|||
}
|
||||
}
|
||||
|
||||
// if we're doing a partial update with deps and we're not loading an initial fixed package
|
||||
// we also need to trigger an update for transitive deps which are being replaced
|
||||
if ($propagateUpdate && $request->getUpdateAllowTransitiveDependencies()) {
|
||||
foreach ($package->getReplaces() as $link) {
|
||||
$replace = $link->getTarget();
|
||||
if (isset($this->loadedNames[$replace]) && isset($this->skippedLoad[$replace])) {
|
||||
if ($request->getUpdateAllowTransitiveRootDependencies() || !$this->isRootRequire($request, $this->skippedLoad[$replace])) {
|
||||
$this->unfixPackage($request, $replace);
|
||||
$loadNames[$replace] = null;
|
||||
// TODO should we try to merge constraints here?
|
||||
$this->nameConstraints[$replace] = null;
|
||||
} elseif (!$request->getUpdateAllowTransitiveRootDependencies() && $this->isRootRequire($request, $replace) && !isset($this->updateAllowWarned[$require])) {
|
||||
$this->updateAllowWarned[$replace] = true;
|
||||
$this->io->writeError('<warning>Dependency "'.$require.'" is also a root requirement. Package has not been listed as an update argument, so keeping locked at old version. Use --with-all-dependencies to include root dependencies.</warning>');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $loadNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a particular name is required directly in the request
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isRootRequire(Request $request, $name)
|
||||
{
|
||||
$rootRequires = $request->getRequires();
|
||||
return isset($rootRequires[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the update allow list allows this package in the lock file to be updated
|
||||
* @return bool
|
||||
*/
|
||||
private function isUpdateAllowed(PackageInterface $package)
|
||||
{
|
||||
foreach ($this->updateAllowList as $pattern => $void) {
|
||||
$patternRegexp = BasePackage::packageNameToRegexp($pattern);
|
||||
if (preg_match($patternRegexp, $package->getName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function warnAboutNonMatchingUpdateAllowList(Request $request)
|
||||
{
|
||||
foreach ($this->updateAllowList as $pattern => $void) {
|
||||
$patternRegexp = BasePackage::packageNameToRegexp($pattern);
|
||||
// update pattern matches a locked package? => all good
|
||||
foreach ($request->getLockedRepository()->getPackages() as $package) {
|
||||
if (preg_match($patternRegexp, $package->getName())) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
// update pattern matches a root require? => all good, probably a new package
|
||||
foreach ($request->getRequires() as $packageName => $constraint) {
|
||||
if (preg_match($patternRegexp, $packageName)) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
if (strpos($pattern, '*') !== false) {
|
||||
$this->io->writeError('<warning>Pattern "' . $pattern . '" listed for update does not match any locked packages.</warning>');
|
||||
} else {
|
||||
$this->io->writeError('<warning>Package "' . $pattern . '" listed for update is not locked.</warning>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverts the decision to use a fixed package from lock file if a partial update with transitive dependencies
|
||||
* found that this package actually needs to be updated
|
||||
*/
|
||||
private function unfixPackage(Request $request, $name)
|
||||
{
|
||||
// remove locked package by this name which was already initialized
|
||||
foreach ($request->getLockedRepository()->getPackages() as $lockedPackage) {
|
||||
if (!($lockedPackage instanceof AliasPackage) && $lockedPackage->getName() === $name) {
|
||||
if (false !== $index = array_search($lockedPackage, $this->packages, true)) {
|
||||
$request->unfixPackage($lockedPackage);
|
||||
unset($this->packages[$index]);
|
||||
if (isset($this->aliasMap[spl_object_hash($lockedPackage)])) {
|
||||
foreach ($this->aliasMap[spl_object_hash($lockedPackage)] as $aliasIndex => $aliasPackage) {
|
||||
$request->unfixPackage($aliasPackage);
|
||||
unset($this->packages[$aliasIndex]);
|
||||
}
|
||||
unset($this->aliasMap[spl_object_hash($lockedPackage)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we unfixed a replaced package name, we also need to unfix the replacer itself
|
||||
if ($this->skippedLoad[$name] !== $name) {
|
||||
$this->unfixPackage($request, $this->skippedLoad[$name]);
|
||||
}
|
||||
|
||||
unset($this->skippedLoad[$name]);
|
||||
unset($this->loadedNames[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -182,7 +182,7 @@ class Problem
|
|||
if ($package->getName() === $packageName) {
|
||||
$fixedPackage = $package;
|
||||
if ($pool->isUnacceptableFixedPackage($package)) {
|
||||
return array("- ", $package->getPrettyName().' is fixed to '.$package->getPrettyVersion().' (lock file version) by a partial update but that version is rejected by your minimum-stability. Make sure you whitelist it for update.');
|
||||
return array("- ", $package->getPrettyName().' is fixed to '.$package->getPrettyVersion().' (lock file version) by a partial update but that version is rejected by your minimum-stability. Make sure you list it as an argument for the update command.');
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ class Problem
|
|||
return $fixedConstraint->matches(new Constraint('==', $p->getVersion()));
|
||||
});
|
||||
if (0 === count($filtered)) {
|
||||
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages).' but the package is fixed to '.$fixedPackage->getPrettyVersion().' (lock file version) by a partial update and that version does not match. Make sure you whitelist it for update.');
|
||||
return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages).' but the package is fixed to '.$fixedPackage->getPrettyVersion().' (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,10 +23,29 @@ use Composer\Semver\Constraint\ConstraintInterface;
|
|||
*/
|
||||
class Request
|
||||
{
|
||||
/**
|
||||
* Identifies a partial update for listed packages only, all dependencies will remain at locked versions
|
||||
*/
|
||||
const UPDATE_ONLY_LISTED = 0;
|
||||
|
||||
/**
|
||||
* Identifies a partial update for listed packages and recursively all their dependencies, however dependencies
|
||||
* also directly required by the root composer.json and their dependencies will remain at the locked version.
|
||||
*/
|
||||
const UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE = 1;
|
||||
|
||||
/**
|
||||
* Identifies a partial update for listed packages and recursively all their dependencies, even dependencies
|
||||
* also directly required by the root composer.json will be updated.
|
||||
*/
|
||||
const UPDATE_LISTED_WITH_TRANSITIVE_DEPS = 2;
|
||||
|
||||
protected $lockedRepository;
|
||||
protected $requires = array();
|
||||
protected $fixedPackages = array();
|
||||
protected $unlockables = array();
|
||||
protected $updateAllowList = array();
|
||||
protected $updateAllowTransitiveDependencies = false;
|
||||
|
||||
public function __construct(LockArrayRepository $lockedRepository = null)
|
||||
{
|
||||
|
@ -49,10 +68,37 @@ class Request
|
|||
$this->fixedPackages[spl_object_hash($package)] = $package;
|
||||
|
||||
if (!$lockable) {
|
||||
$this->unlockables[] = $package;
|
||||
$this->unlockables[spl_object_hash($package)] = $package;
|
||||
}
|
||||
}
|
||||
|
||||
public function unfixPackage(PackageInterface $package)
|
||||
{
|
||||
unset($this->fixedPackages[spl_object_hash($package)]);
|
||||
unset($this->unlockables[spl_object_hash($package)]);
|
||||
}
|
||||
|
||||
public function setUpdateAllowList($updateAllowList, $updateAllowTransitiveDependencies)
|
||||
{
|
||||
$this->updateAllowList = $updateAllowList;
|
||||
$this->updateAllowTransitiveDependencies = $updateAllowTransitiveDependencies;
|
||||
}
|
||||
|
||||
public function getUpdateAllowList()
|
||||
{
|
||||
return $this->updateAllowList;
|
||||
}
|
||||
|
||||
public function getUpdateAllowTransitiveDependencies()
|
||||
{
|
||||
return $this->updateAllowTransitiveDependencies !== self::UPDATE_ONLY_LISTED;
|
||||
}
|
||||
|
||||
public function getUpdateAllowTransitiveRootDependencies()
|
||||
{
|
||||
return $this->updateAllowTransitiveDependencies === self::UPDATE_LISTED_WITH_TRANSITIVE_DEPS;
|
||||
}
|
||||
|
||||
public function getRequires()
|
||||
{
|
||||
return $this->requires;
|
||||
|
|
|
@ -142,9 +142,8 @@ class Installer
|
|||
* @var array|null
|
||||
*/
|
||||
protected $updateMirrors = false;
|
||||
protected $updateWhitelist = null;
|
||||
protected $whitelistTransitiveDependencies = false;
|
||||
protected $whitelistAllDependencies = false;
|
||||
protected $updateAllowList = null;
|
||||
protected $updateAllowTransitiveDependencies = Request::UPDATE_ONLY_LISTED;
|
||||
|
||||
/**
|
||||
* @var SuggestedPackagesReporter
|
||||
|
@ -199,8 +198,8 @@ class Installer
|
|||
gc_collect_cycles();
|
||||
gc_disable();
|
||||
|
||||
if ($this->updateWhitelist && $this->updateMirrors) {
|
||||
throw new \RuntimeException("The installer options updateMirrors and updateWhitelist are mutually exclusive.");
|
||||
if ($this->updateAllowList && $this->updateMirrors) {
|
||||
throw new \RuntimeException("The installer options updateMirrors and updateAllowList are mutually exclusive.");
|
||||
}
|
||||
|
||||
// Force update if there is no lock file present
|
||||
|
@ -352,16 +351,11 @@ class Installer
|
|||
$lockedRepository = $this->locker->getLockedRepository(true);
|
||||
}
|
||||
|
||||
if ($this->updateWhitelist) {
|
||||
if ($this->updateAllowList) {
|
||||
if (!$lockedRepository) {
|
||||
$this->io->writeError('<error>Cannot update only a partial set of packages without a lock file present.</error>', true, IOInterface::QUIET);
|
||||
return 1;
|
||||
}
|
||||
$this->whitelistUpdateDependencies(
|
||||
$lockedRepository,
|
||||
$this->package->getRequires(),
|
||||
$this->package->getDevRequires()
|
||||
);
|
||||
}
|
||||
|
||||
$this->io->writeError('<info>Loading composer repositories with package information</info>');
|
||||
|
@ -394,17 +388,12 @@ class Installer
|
|||
}
|
||||
}
|
||||
|
||||
// if the updateWhitelist is enabled, packages not in it are also fixed
|
||||
// to the version specified in the lock
|
||||
if ($this->updateWhitelist && $lockedRepository) {
|
||||
foreach ($lockedRepository->getPackages() as $lockedPackage) {
|
||||
if (!$this->isUpdateable($lockedPackage)) {
|
||||
$request->fixPackage($lockedPackage);
|
||||
}
|
||||
}
|
||||
// pass the allow list into the request, so the pool builder can apply it
|
||||
if ($this->updateAllowList) {
|
||||
$request->setUpdateAllowList($this->updateAllowList, $this->updateAllowTransitiveDependencies);
|
||||
}
|
||||
|
||||
$pool = $repositorySet->createPool($request, $this->eventDispatcher);
|
||||
$pool = $repositorySet->createPool($request, $this->io, $this->eventDispatcher);
|
||||
|
||||
// solve dependencies
|
||||
$solver = new Solver($policy, $pool, $this->io, $repositorySet);
|
||||
|
@ -508,7 +497,7 @@ class Installer
|
|||
$lockTransaction->getNewLockPackages(true, $this->updateMirrors),
|
||||
$platformReqs,
|
||||
$platformDevReqs,
|
||||
$aliases,
|
||||
$lockTransaction->getAliases($aliases),
|
||||
$this->package->getMinimumStability(),
|
||||
$this->package->getStabilityFlags(),
|
||||
$this->preferStable || $this->package->getPreferStable(),
|
||||
|
@ -623,7 +612,7 @@ class Installer
|
|||
$request->requireName($link->getTarget(), $link->getConstraint());
|
||||
}
|
||||
|
||||
$pool = $repositorySet->createPool($request, $this->eventDispatcher);
|
||||
$pool = $repositorySet->createPool($request, $this->io, $this->eventDispatcher);
|
||||
|
||||
// solve dependencies
|
||||
$solver = new Solver($policy, $pool, $this->io, $repositorySet);
|
||||
|
@ -847,26 +836,6 @@ class Installer
|
|||
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
|
||||
* @return array
|
||||
|
@ -883,108 +852,6 @@ class Installer
|
|||
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
|
||||
*
|
||||
|
@ -1265,41 +1132,29 @@ class Installer
|
|||
* @param array $packages
|
||||
* @return Installer
|
||||
*/
|
||||
public function setUpdateWhitelist(array $packages)
|
||||
public function setUpdateAllowList(array $packages)
|
||||
{
|
||||
$this->updateWhitelist = array_flip(array_map('strtolower', $packages));
|
||||
$this->updateAllowList = array_flip(array_map('strtolower', $packages));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should dependencies of whitelisted packages (but not direct dependencies) be updated?
|
||||
* Should dependencies of packages marked for update be updated?
|
||||
*
|
||||
* This will NOT whitelist any dependencies that are also directly defined
|
||||
* in the root package.
|
||||
* Depending on the chosen constant this will either only update the directly named packages, all transitive
|
||||
* dependencies which are not root requirement or all transitive dependencies including root requirements
|
||||
*
|
||||
* @param bool $updateTransitiveDependencies
|
||||
* @param int $updateAllowTransitiveDependencies One of the UPDATE_ constants on the Request class
|
||||
* @return Installer
|
||||
*/
|
||||
public function setWhitelistTransitiveDependencies($updateTransitiveDependencies = true)
|
||||
public function setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies)
|
||||
{
|
||||
$this->whitelistTransitiveDependencies = (bool) $updateTransitiveDependencies;
|
||||
if (!in_array($updateAllowTransitiveDependencies, array(Request::UPDATE_ONLY_LISTED, Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE, Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS), true)) {
|
||||
throw new \RuntimeException("Invalid value for updateAllowTransitiveDependencies supplied");
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should all dependencies of whitelisted packages be updated recursively?
|
||||
*
|
||||
* This will whitelist any dependencies of the whitelisted packages, including
|
||||
* those defined in the root package.
|
||||
*
|
||||
* @param bool $updateAllDependencies
|
||||
* @return Installer
|
||||
*/
|
||||
public function setWhitelistAllDependencies($updateAllDependencies = true)
|
||||
{
|
||||
$this->whitelistAllDependencies = (bool) $updateAllDependencies;
|
||||
$this->updateAllowTransitiveDependencies = $updateAllowTransitiveDependencies;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
|
|
@ -250,14 +250,14 @@ abstract class BasePackage implements PackageInterface
|
|||
/**
|
||||
* Build a regexp from a package name, expanding * globs as required
|
||||
*
|
||||
* @param string $whiteListedPattern
|
||||
* @param string $allowPattern
|
||||
* @param string $wrap Wrap the cleaned string by the given string
|
||||
* @return string
|
||||
*/
|
||||
public static function packageNameToRegexp($whiteListedPattern, $wrap = '{^%s$}i')
|
||||
public static function packageNameToRegexp($allowPattern, $wrap = '{^%s$}i')
|
||||
{
|
||||
$cleanedWhiteListedPattern = str_replace('\\*', '.*', preg_quote($whiteListedPattern));
|
||||
$cleanedAllowPattern = str_replace('\\*', '.*', preg_quote($allowPattern));
|
||||
|
||||
return sprintf($wrap, $cleanedWhiteListedPattern);
|
||||
return sprintf($wrap, $cleanedAllowPattern);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@ use Composer\DependencyResolver\Pool;
|
|||
use Composer\DependencyResolver\PoolBuilder;
|
||||
use Composer\DependencyResolver\Request;
|
||||
use Composer\EventDispatcher\EventDispatcher;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\IO\NullIO;
|
||||
use Composer\Package\BasePackage;
|
||||
use Composer\Package\Version\VersionParser;
|
||||
use Composer\Repository\CompositeRepository;
|
||||
|
@ -185,9 +187,9 @@ class RepositorySet
|
|||
*
|
||||
* @return Pool
|
||||
*/
|
||||
public function createPool(Request $request, EventDispatcher $eventDispatcher = null)
|
||||
public function createPool(Request $request, IOInterface $io, EventDispatcher $eventDispatcher = null)
|
||||
{
|
||||
$poolBuilder = new PoolBuilder($this->acceptableStabilities, $this->stabilityFlags, $this->rootAliases, $this->rootReferences, $eventDispatcher);
|
||||
$poolBuilder = new PoolBuilder($this->acceptableStabilities, $this->stabilityFlags, $this->rootAliases, $this->rootReferences, $io, $eventDispatcher);
|
||||
|
||||
foreach ($this->repositories as $repo) {
|
||||
if (($repo instanceof InstalledRepositoryInterface || $repo instanceof InstalledRepository) && !$this->allowInstalledRepositories) {
|
||||
|
@ -236,6 +238,6 @@ class RepositorySet
|
|||
$request->requireName($packageName);
|
||||
}
|
||||
|
||||
return $this->createPool($request);
|
||||
return $this->createPool($request, new NullIO());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ class PoolBuilderTest extends TestCase
|
|||
$request->fixPackage($loadPackage($fixedPackage));
|
||||
}
|
||||
|
||||
$pool = $repositorySet->createPool($request);
|
||||
$pool = $repositorySet->createPool($request, new NullIO());
|
||||
for ($i = 1, $count = count($pool); $i <= $count; $i++) {
|
||||
$result[] = $pool->packageById($i);
|
||||
}
|
||||
|
|
|
@ -890,8 +890,9 @@ class SolverTest extends TestCase
|
|||
|
||||
protected function createSolver()
|
||||
{
|
||||
$this->pool = $this->repoSet->createPool($this->request);
|
||||
$this->solver = new Solver($this->policy, $this->pool, new NullIO());
|
||||
$io = new NullIO();
|
||||
$this->pool = $this->repoSet->createPool($this->request, $io);
|
||||
$this->solver = new Solver($this->policy, $this->pool, $io);
|
||||
}
|
||||
|
||||
protected function checkSolverResult(array $expected)
|
||||
|
|
|
@ -14,7 +14,7 @@ dependency of one the requirements that is whitelisted for update.
|
|||
{ "name": "a/a", "version": "1.0.0" },
|
||||
{ "name": "a/a", "version": "1.1.0" },
|
||||
{ "name": "b/b", "version": "1.0.0", "require": { "a/a": "~1.0" } },
|
||||
{ "name": "b/b", "version": "1.1.0", "require": { "a/b": "~1.1" } }
|
||||
{ "name": "b/b", "version": "1.1.0", "require": { "a/a": "~1.1" } }
|
||||
]
|
||||
}
|
||||
],
|
||||
|
@ -49,9 +49,9 @@ dependency of one the requirements that is whitelisted for update.
|
|||
update b/b --with-dependencies
|
||||
|
||||
--EXPECT-OUTPUT--
|
||||
<warning>Dependency "a/a" is also a root requirement, but is not explicitly whitelisted. Ignoring.</warning>
|
||||
Loading composer repositories with package information
|
||||
Updating dependencies
|
||||
<warning>Dependency "a/a" is also a root requirement. Package has not been listed as an update argument, so keeping locked at old version. Use --with-all-dependencies to include root dependencies.</warning>
|
||||
Nothing to modify in lock file
|
||||
Writing lock file
|
||||
Installing dependencies from lock file (including require-dev)
|
||||
|
|
|
@ -59,4 +59,4 @@ Updating dependencies
|
|||
Your requirements could not be resolved to an installable set of packages.
|
||||
|
||||
Problem 1
|
||||
- b/unstable is fixed to 1.1.0-alpha (lock file version) by a partial update but that version is rejected by your minimum-stability. Make sure you whitelist it for update.
|
||||
- b/unstable is fixed to 1.1.0-alpha (lock file version) by a partial update but that version is rejected by your minimum-stability. Make sure you list it as an argument for the update command.
|
|
@ -117,7 +117,7 @@ Your requirements could not be resolved to an installable set of packages.
|
|||
Problem 3
|
||||
- Root composer.json requires non-existent/pkg, it could not be found in any version, there may be a typo in the package name.
|
||||
Problem 4
|
||||
- Root composer.json requires stable-requiree-excluded/pkg 1.0.1, found stable-requiree-excluded/pkg[1.0.1] but the package is fixed to 1.0.0 (lock file version) by a partial update and that version does not match. Make sure you whitelist it for update.
|
||||
- Root composer.json requires stable-requiree-excluded/pkg 1.0.1, found stable-requiree-excluded/pkg[1.0.1] but the package is fixed to 1.0.0 (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.
|
||||
Problem 5
|
||||
- Root composer.json requires linked library lib-xml 1002.* but it has the wrong version installed or is missing from your system, make sure to load the extension providing it.
|
||||
Problem 6
|
||||
|
|
|
@ -70,7 +70,7 @@ Update with a package whitelist only updates those packages and their dependenci
|
|||
"platform-dev": []
|
||||
}
|
||||
--RUN--
|
||||
update whitelisted/pkg-* --with-dependencies
|
||||
update whitelisted/pkg-* foobar --with-dependencies
|
||||
--EXPECT--
|
||||
Upgrading dependency/pkg (1.0.0 => 1.1.0)
|
||||
Upgrading whitelisted/pkg-component2 (1.0.0 => 1.1.0)
|
|
@ -0,0 +1,58 @@
|
|||
--TEST--
|
||||
Verify that partial updates warn about using patterns in the argument which have no matches
|
||||
--COMPOSER--
|
||||
{
|
||||
"repositories": [
|
||||
{
|
||||
"type": "package",
|
||||
"package": [
|
||||
{ "name": "a/a", "version": "1.0.0" },
|
||||
{ "name": "b/b", "version": "1.0.0" },
|
||||
{ "name": "b/b", "version": "1.1.0" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"a/a": "~1.0",
|
||||
"b/b": "~1.0"
|
||||
}
|
||||
}
|
||||
|
||||
--INSTALLED--
|
||||
[
|
||||
{ "name": "a/a", "version": "1.0.0" },
|
||||
{ "name": "b/b", "version": "1.0.0" }
|
||||
]
|
||||
|
||||
--LOCK--
|
||||
{
|
||||
"packages": [
|
||||
{ "name": "a/a", "version": "1.0.0" },
|
||||
{ "name": "b/b", "version": "1.0.0" }
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "dev",
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": []
|
||||
}
|
||||
--RUN--
|
||||
update b/b foo/bar baz/* --with-dependencies
|
||||
|
||||
--EXPECT-OUTPUT--
|
||||
Loading composer repositories with package information
|
||||
Updating dependencies
|
||||
<warning>Package "foo/bar" listed for update is not locked.</warning>
|
||||
<warning>Pattern "baz/*" listed for update does not match any locked packages.</warning>
|
||||
Lock file operations: 0 installs, 1 update, 0 removals
|
||||
- Upgrading b/b (1.0.0 => 1.1.0)
|
||||
Writing lock file
|
||||
Installing dependencies from lock file (including require-dev)
|
||||
Package operations: 0 installs, 1 update, 0 removals
|
||||
Generating autoload files
|
||||
|
||||
--EXPECT--
|
||||
Upgrading b/b (1.0.0 => 1.1.0)
|
|
@ -0,0 +1,99 @@
|
|||
--TEST--
|
||||
Verify that a partial update with deps correctly keeps track of all aliases.
|
||||
--COMPOSER--
|
||||
{
|
||||
"repositories": [
|
||||
{
|
||||
"type": "package",
|
||||
"package": [
|
||||
{ "name": "current/pkg", "version": "1.0.0", "require": { "current/dep": "<1.2.0" } },
|
||||
{ "name": "current/dep", "version": "dev-master", "extra": {"branch-alias": {"dev-master": "1.0.x-dev"}}},
|
||||
{ "name": "current/dep", "version": "1.0.0" },
|
||||
{ "name": "current/dep", "version": "1.1.0", "require": {"current/dep2": "*"} },
|
||||
{ "name": "current/dep", "version": "1.2.0" },
|
||||
{ "name": "current/dep2", "version": "dev-foo", "extra": {"branch-alias": {"dev-foo": "1.0.x-dev"}}},
|
||||
{ "name": "current/dep2", "version": "dev-master", "extra": {"branch-alias": {"dev-master": "2.x-dev"}}},
|
||||
{ "name": "new/pkg", "version": "1.0.0", "require": { "current/dep": "^1.1", "current/dep2": "^1.1"} },
|
||||
{ "name": "new/pkg", "version": "1.1.0", "require": { "current/dep": "^1.2" } }
|
||||
]
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"current/dep": "dev-master as 1.1.0",
|
||||
"current/dep2": "dev-master as 1.1.2",
|
||||
"current/pkg": "1.0.0 as 2.0.0",
|
||||
"new/pkg": "1.*"
|
||||
},
|
||||
"minimum-stability": "dev"
|
||||
}
|
||||
--INSTALLED--
|
||||
[
|
||||
{ "name": "current/dep", "version": "dev-master", "extra": {"branch-alias": {"dev-master": "1.0.x-dev"}}},
|
||||
{ "name": "current/dep2", "version": "dev-foo", "extra": {"branch-alias": {"dev-foo": "1.0.x-dev"}}},
|
||||
{ "name": "current/pkg", "version": "1.0.0", "require": { "current/dep": "<1.2.0" } }
|
||||
]
|
||||
--LOCK--
|
||||
{
|
||||
"packages": [
|
||||
{ "name": "current/dep", "version": "dev-master", "extra": {"branch-alias": {"dev-master": "1.0.x-dev"}}, "type": "library"},
|
||||
{ "name": "current/dep2", "version": "dev-foo", "extra": {"branch-alias": {"dev-foo": "1.0.x-dev"}}, "type": "library"},
|
||||
{ "name": "current/pkg", "version": "1.0.0", "require": { "current/dep": "<1.2.0" } }
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [
|
||||
{
|
||||
"alias": "1.1.0",
|
||||
"alias_normalized": "1.1.0.0",
|
||||
"version": "dev-master",
|
||||
"package": "current/dep"
|
||||
}
|
||||
],
|
||||
"minimum-stability": "dev",
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": []
|
||||
}
|
||||
--RUN--
|
||||
update new/pkg --with-all-dependencies
|
||||
--EXPECT-LOCK--
|
||||
{
|
||||
"packages": [
|
||||
{ "name": "current/dep", "version": "dev-master", "extra": {"branch-alias": {"dev-master": "1.0.x-dev"}}, "type": "library"},
|
||||
{ "name": "current/dep2", "version": "dev-master", "extra": {"branch-alias": {"dev-master": "2.x-dev"}}, "type": "library"},
|
||||
{ "name": "current/pkg", "version": "1.0.0", "require": { "current/dep": "<1.2.0" }, "type": "library"},
|
||||
{ "name": "new/pkg", "version": "1.0.0", "require": { "current/dep": "^1.1", "current/dep2": "^1.1"}, "type": "library"}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [
|
||||
{
|
||||
"alias": "1.1.0",
|
||||
"alias_normalized": "1.1.0.0",
|
||||
"version": "dev-master",
|
||||
"package": "current/dep"
|
||||
},
|
||||
{
|
||||
"alias": "1.1.2",
|
||||
"alias_normalized": "1.1.2.0",
|
||||
"version": "dev-master",
|
||||
"package": "current/dep2"
|
||||
}
|
||||
],
|
||||
"minimum-stability": "dev",
|
||||
"stability-flags": {
|
||||
"current/dep": 20,
|
||||
"current/dep2": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": []
|
||||
}
|
||||
--EXPECT--
|
||||
Marking current/dep (1.1.0) as installed, alias of current/dep (dev-master)
|
||||
Upgrading current/dep2 (dev-foo => dev-master)
|
||||
Marking current/dep2 (1.1.2) as installed, alias of current/dep2 (dev-master)
|
||||
Marking current/dep2 (2.x-dev) as installed, alias of current/dep2 (dev-master)
|
||||
Installing new/pkg (1.0.0)
|
||||
Marking current/dep2 (1.0.x-dev) as uninstalled, alias of current/dep2 (dev-foo)
|
|
@ -0,0 +1,49 @@
|
|||
--TEST--
|
||||
When partially updating a package to a newer version and the new version has a new requirement for a package we already have installed, mark it for update
|
||||
--COMPOSER--
|
||||
{
|
||||
"repositories": [
|
||||
{
|
||||
"type": "package",
|
||||
"package": [
|
||||
{ "name": "root/pkg1", "version": "1.0.0", "require": { "current/dep": "^1.0" } },
|
||||
{ "name": "root/pkg1", "version": "1.2.0", "require": { "current/dep": "^1.0" } },
|
||||
{ "name": "current/dep", "version": "1.0.0" },
|
||||
{ "name": "current/dep", "version": "1.2.0" },
|
||||
{ "name": "root/pkg2", "version": "1.0.0" },
|
||||
{ "name": "root/pkg2", "version": "1.2.0", "require": { "current/dep": "^1.2" } }
|
||||
]
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"root/pkg1": "1.*",
|
||||
"root/pkg2": "1.*"
|
||||
}
|
||||
}
|
||||
--INSTALLED--
|
||||
[
|
||||
{ "name": "root/pkg1", "version": "1.0.0", "require": { "current/dep": "^1.0" } },
|
||||
{ "name": "current/dep", "version": "1.0.0" },
|
||||
{ "name": "root/pkg2", "version": "1.0.0" }
|
||||
]
|
||||
--LOCK--
|
||||
{
|
||||
"packages": [
|
||||
{ "name": "root/pkg1", "version": "1.0.0", "require": { "current/dep": "^1.0" } },
|
||||
{ "name": "current/dep", "version": "1.0.0" },
|
||||
{ "name": "root/pkg2", "version": "1.0.0" }
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "dev",
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": []
|
||||
}
|
||||
--RUN--
|
||||
update root/pkg2 --with-dependencies
|
||||
--EXPECT--
|
||||
Upgrading current/dep (1.0.0 => 1.2.0)
|
||||
Upgrading root/pkg2 (1.0.0 => 1.2.0)
|
|
@ -0,0 +1,50 @@
|
|||
--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": { "mutual/target": "*", "mutual/target-provide": "*" } },
|
||||
{ "name": "current/dep", "version": "1.0.0", "replace": { "mutual/target": "1.0.0" } },
|
||||
{ "name": "new/pkg", "version": "1.0.0", "replace": { "mutual/target": "1.0.0" } },
|
||||
{ "name": "current/dep-provide", "version": "1.0.0", "provide": { "mutual/target-provide": "1.0.0" } },
|
||||
{ "name": "new/pkg-provide", "version": "1.0.0", "provide": { "mutual/target-provide": "1.0.0" } }
|
||||
]
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"current/pkg": "1.*",
|
||||
"new/pkg": "1.*",
|
||||
"new/pkg-provide": "1.*"
|
||||
}
|
||||
}
|
||||
--INSTALLED--
|
||||
[
|
||||
{ "name": "current/pkg", "version": "1.0.0", "require": { "mutual/target": "*" } },
|
||||
{ "name": "current/dep", "version": "1.0.0", "replace": { "mutual/target": "1.0.0" } },
|
||||
{ "name": "current/dep-provide", "version": "1.0.0", "provide": { "mutual/target-provide": "1.0.0" } }
|
||||
]
|
||||
--LOCK--
|
||||
{
|
||||
"packages": [
|
||||
{ "name": "current/pkg", "version": "1.0.0", "require": { "mutual/target": "*" } },
|
||||
{ "name": "current/dep", "version": "1.0.0", "replace": { "mutual/target": "1.0.0" } },
|
||||
{ "name": "current/dep-provide", "version": "1.0.0", "provide": { "mutual/target-provide": "1.0.0" } }
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "dev",
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": []
|
||||
}
|
||||
--RUN--
|
||||
update new/pkg --with-dependencies
|
||||
--EXPECT--
|
||||
Removing current/dep (1.0.0)
|
||||
Installing new/pkg (1.0.0)
|
||||
Installing new/pkg-provide (1.0.0)
|
|
@ -0,0 +1,44 @@
|
|||
--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 --with-dependencies
|
||||
--EXPECT--
|
||||
Removing current/dep (1.0.0)
|
||||
Installing new/pkg (1.0.0)
|
|
@ -0,0 +1,48 @@
|
|||
--TEST--
|
||||
Require a new package in the composer.json and updating with its name as an argument and with-dependencies should update locked dependencies as far as possible
|
||||
--COMPOSER--
|
||||
{
|
||||
"repositories": [
|
||||
{
|
||||
"type": "package",
|
||||
"package": [
|
||||
{ "name": "current/pkg", "version": "1.0.0", "require": { "current/dep": "<1.2.0" } },
|
||||
{ "name": "current/pkg", "version": "1.1.0", "require": { "current/dep": "^1.0" } },
|
||||
{ "name": "current/dep", "version": "1.0.0" },
|
||||
{ "name": "current/dep", "version": "1.1.0" },
|
||||
{ "name": "current/dep", "version": "1.2.0" },
|
||||
{ "name": "new/pkg", "version": "1.0.0", "require": { "current/dep": "^1.1" } },
|
||||
{ "name": "new/pkg", "version": "1.1.0", "require": { "current/dep": "^1.2" } }
|
||||
]
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"current/pkg": "1.*",
|
||||
"new/pkg": "1.*"
|
||||
}
|
||||
}
|
||||
--INSTALLED--
|
||||
[
|
||||
{ "name": "current/pkg", "version": "1.0.0", "require": { "current/dep": "<1.2.0" } },
|
||||
{ "name": "current/dep", "version": "1.0.0" }
|
||||
]
|
||||
--LOCK--
|
||||
{
|
||||
"packages": [
|
||||
{ "name": "current/pkg", "version": "1.0.0", "require": { "current/dep": "<1.2.0" } },
|
||||
{ "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 --with-dependencies
|
||||
--EXPECT--
|
||||
Upgrading current/dep (1.0.0 => 1.1.0)
|
||||
Installing new/pkg (1.0.0)
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
namespace Composer\Test;
|
||||
|
||||
use Composer\DependencyResolver\Request;
|
||||
use Composer\Installer;
|
||||
use Composer\Console\Application;
|
||||
use Composer\IO\BufferIO;
|
||||
|
@ -279,14 +280,20 @@ class InstallerTest extends TestCase
|
|||
$updateMirrors = $input->getOption('lock') || count($filteredPackages) != count($packages);
|
||||
$packages = $filteredPackages;
|
||||
|
||||
$updateAllowTransitiveDependencies = Request::UPDATE_ONLY_LISTED;
|
||||
if ($input->getOption('with-all-dependencies')) {
|
||||
$updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS;
|
||||
} elseif ($input->getOption('with-dependencies')) {
|
||||
$updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE;
|
||||
}
|
||||
|
||||
$installer
|
||||
->setDevMode(!$input->getOption('no-dev'))
|
||||
->setUpdate(true)
|
||||
->setDryRun($input->getOption('dry-run'))
|
||||
->setUpdateMirrors($updateMirrors)
|
||||
->setUpdateWhitelist($packages)
|
||||
->setWhitelistTransitiveDependencies($input->getOption('with-dependencies'))
|
||||
->setWhitelistAllDependencies($input->getOption('with-all-dependencies'))
|
||||
->setUpdateAllowList($packages)
|
||||
->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies)
|
||||
->setPreferStable($input->getOption('prefer-stable'))
|
||||
->setPreferLowest($input->getOption('prefer-lowest'))
|
||||
->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'));
|
||||
|
|
Loading…
Reference in New Issue