Merge pull request #8860 from Seldaek/update-with
Add --with to update command to allow downgrading to a specific version / applying custom temporary constraintspull/8880/head
commit
5d5515348a
|
@ -142,6 +142,26 @@ You can also use wildcards to update a bunch of packages at once:
|
||||||
php composer.phar update "vendor/*"
|
php composer.phar update "vendor/*"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
If you want to downgrade a package to a specific version without changing your
|
||||||
|
composer.json you can use `--with` and provide a custom version constraint:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
php composer.phar update --with vendor/package:2.0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
The custom constraint has to be a subset of the existing constraint you have,
|
||||||
|
and this feature is only available for your root package dependencies.
|
||||||
|
|
||||||
|
If you only want to update the package(s) for which you provide custom constraints
|
||||||
|
using `--with`, you can skip `--with` and just use constraints with the partial
|
||||||
|
update syntax:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
php composer.phar update vendor/package:2.0.1 vendor/package2:3.0.*
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
* **--prefer-source:** Install packages from `source` when available.
|
* **--prefer-source:** Install packages from `source` when available.
|
||||||
|
@ -152,6 +172,7 @@ php composer.phar update "vendor/*"
|
||||||
* **--no-install:** Does not run the install step after updating the composer.lock file.
|
* **--no-install:** Does not run the install step after updating the composer.lock file.
|
||||||
* **--lock:** Only updates the lock file hash to suppress warning about the
|
* **--lock:** Only updates the lock file hash to suppress warning about the
|
||||||
lock file being out of date.
|
lock file being out of date.
|
||||||
|
* **--with:** Temporary version constraint to add, e.g. foo/bar:1.0.0 or foo/bar=1.0.0
|
||||||
* **--no-autoloader:** Skips autoloader generation.
|
* **--no-autoloader:** Skips autoloader generation.
|
||||||
* **--no-scripts:** Skips execution of scripts defined in `composer.json`.
|
* **--no-scripts:** Skips execution of scripts defined in `composer.json`.
|
||||||
* **--no-progress:** Removes the progress display that can mess with some
|
* **--no-progress:** Removes the progress display that can mess with some
|
||||||
|
|
|
@ -19,6 +19,7 @@ use Composer\Factory;
|
||||||
use Composer\IO\IOInterface;
|
use Composer\IO\IOInterface;
|
||||||
use Composer\IO\NullIO;
|
use Composer\IO\NullIO;
|
||||||
use Composer\Plugin\PreCommandRunEvent;
|
use Composer\Plugin\PreCommandRunEvent;
|
||||||
|
use Composer\Package\Version\VersionParser;
|
||||||
use Composer\Plugin\PluginEvents;
|
use Composer\Plugin\PluginEvents;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
@ -180,4 +181,25 @@ abstract class BaseCommand extends Command
|
||||||
|
|
||||||
return array($preferSource, $preferDist);
|
return array($preferSource, $preferDist);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function formatRequirements(array $requirements)
|
||||||
|
{
|
||||||
|
$requires = array();
|
||||||
|
$requirements = $this->normalizeRequirements($requirements);
|
||||||
|
foreach ($requirements as $requirement) {
|
||||||
|
if (!isset($requirement['version'])) {
|
||||||
|
throw new \UnexpectedValueException('Option '.$requirement['name'] .' is missing a version constraint, use e.g. '.$requirement['name'].':^1.0');
|
||||||
|
}
|
||||||
|
$requires[$requirement['name']] = $requirement['version'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $requires;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function normalizeRequirements(array $requirements)
|
||||||
|
{
|
||||||
|
$parser = new VersionParser();
|
||||||
|
|
||||||
|
return $parser->parseNameVersionPairs($requirements);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -585,17 +585,6 @@ EOT
|
||||||
return array($this->parseAuthorString($author));
|
return array($this->parseAuthorString($author));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function formatRequirements(array $requirements)
|
|
||||||
{
|
|
||||||
$requires = array();
|
|
||||||
$requirements = $this->normalizeRequirements($requirements);
|
|
||||||
foreach ($requirements as $requirement) {
|
|
||||||
$requires[$requirement['name']] = $requirement['version'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $requires;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getGitConfig()
|
protected function getGitConfig()
|
||||||
{
|
{
|
||||||
if (null !== $this->gitConfig) {
|
if (null !== $this->gitConfig) {
|
||||||
|
@ -660,13 +649,6 @@ EOT
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function normalizeRequirements(array $requirements)
|
|
||||||
{
|
|
||||||
$parser = new VersionParser();
|
|
||||||
|
|
||||||
return $parser->parseNameVersionPairs($requirements);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function addVendorIgnore($ignoreFile, $vendor = '/vendor/')
|
protected function addVendorIgnore($ignoreFile, $vendor = '/vendor/')
|
||||||
{
|
{
|
||||||
$contents = "";
|
$contents = "";
|
||||||
|
|
|
@ -18,6 +18,9 @@ use Composer\Installer;
|
||||||
use Composer\IO\IOInterface;
|
use Composer\IO\IOInterface;
|
||||||
use Composer\Plugin\CommandEvent;
|
use Composer\Plugin\CommandEvent;
|
||||||
use Composer\Plugin\PluginEvents;
|
use Composer\Plugin\PluginEvents;
|
||||||
|
use Composer\Package\Version\VersionParser;
|
||||||
|
use Composer\Semver\Constraint\MultiConstraint;
|
||||||
|
use Composer\Package\Link;
|
||||||
use Symfony\Component\Console\Helper\Table;
|
use Symfony\Component\Console\Helper\Table;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
@ -39,6 +42,7 @@ class UpdateCommand extends BaseCommand
|
||||||
->setDescription('Upgrades your dependencies to the latest version according to composer.json, and updates the composer.lock file.')
|
->setDescription('Upgrades your dependencies to the latest version according to composer.json, and updates the composer.lock file.')
|
||||||
->setDefinition(array(
|
->setDefinition(array(
|
||||||
new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Packages that should be updated, if not provided all packages are.'),
|
new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Packages that should be updated, if not provided all packages are.'),
|
||||||
|
new InputOption('with', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Temporary version constraint to add, e.g. foo/bar:1.0.0 or foo/bar=1.0.0'),
|
||||||
new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'),
|
new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'),
|
||||||
new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'),
|
new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'),
|
||||||
new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'),
|
new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'),
|
||||||
|
@ -80,6 +84,14 @@ from a specific vendor:
|
||||||
|
|
||||||
<info>php composer.phar update vendor/package1 foo/* [...]</info>
|
<info>php composer.phar update vendor/package1 foo/* [...]</info>
|
||||||
|
|
||||||
|
To run an update with more restrictive constraints you can use:
|
||||||
|
|
||||||
|
<info>php composer.phar update --with vendor/package:1.0.*</info>
|
||||||
|
|
||||||
|
To run a partial update with more restrictive constraints you can use the shorthand:
|
||||||
|
|
||||||
|
<info>php composer.phar update vendor/package:1.0.*</info>
|
||||||
|
|
||||||
To select packages names interactively with auto-completion use <info>-i</info>.
|
To select packages names interactively with auto-completion use <info>-i</info>.
|
||||||
|
|
||||||
Read more at https://getcomposer.org/doc/03-cli.md#update-u
|
Read more at https://getcomposer.org/doc/03-cli.md#update-u
|
||||||
|
@ -101,22 +113,54 @@ EOT
|
||||||
$composer = $this->getComposer(true, $input->getOption('no-plugins'));
|
$composer = $this->getComposer(true, $input->getOption('no-plugins'));
|
||||||
|
|
||||||
$packages = $input->getArgument('packages');
|
$packages = $input->getArgument('packages');
|
||||||
|
$reqs = $this->formatRequirements($input->getOption('with'));
|
||||||
|
|
||||||
|
// extract --with shorthands from the allowlist
|
||||||
|
if ($packages) {
|
||||||
|
$allowlistPackagesWithRequirements = array_filter($packages, function ($pkg) {
|
||||||
|
return preg_match('{\S+[ =:]\S+}', $pkg) > 0;
|
||||||
|
});
|
||||||
|
foreach ($this->formatRequirements($allowlistPackagesWithRequirements) as $package => $constraint) {
|
||||||
|
var_Dump($package, $constraint);
|
||||||
|
$reqs[$package] = $constraint;
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace the foo/bar:req by foo/bar in the allowlist
|
||||||
|
foreach ($allowlistPackagesWithRequirements as $package) {
|
||||||
|
$packageName = preg_replace('{^([^ =:]+)[ =:].*$}', '$1', $package);
|
||||||
|
$index = array_search($package, $packages);
|
||||||
|
$packages[$index] = $packageName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$rootRequires = $composer->getPackage()->getRequires();
|
||||||
|
$rootDevRequires = $composer->getPackage()->getDevRequires();
|
||||||
|
foreach ($reqs as $package => $constraint) {
|
||||||
|
if (isset($rootRequires[$package])) {
|
||||||
|
$rootRequires[$package] = $this->appendConstraintToLink($rootRequires[$package], $constraint);
|
||||||
|
} elseif (isset($rootDevRequires[$package])) {
|
||||||
|
$rootDevRequires[$package] = $this->appendConstraintToLink($rootDevRequires[$package], $constraint);
|
||||||
|
} else {
|
||||||
|
throw new \UnexpectedValueException('Only root package requirements can receive temporary constraints and '.$package.' is not one');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$composer->getPackage()->setRequires($rootRequires);
|
||||||
|
$composer->getPackage()->setDevRequires($rootDevRequires);
|
||||||
|
|
||||||
if ($input->getOption('interactive')) {
|
if ($input->getOption('interactive')) {
|
||||||
$packages = $this->getPackagesInteractively($io, $input, $output, $composer, $packages);
|
$packages = $this->getPackagesInteractively($io, $input, $output, $composer, $packages);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($input->getOption('root-reqs')) {
|
if ($input->getOption('root-reqs')) {
|
||||||
$require = array_keys($composer->getPackage()->getRequires());
|
$requires = array_keys($rootRequires);
|
||||||
if (!$input->getOption('no-dev')) {
|
if (!$input->getOption('no-dev')) {
|
||||||
$requireDev = array_keys($composer->getPackage()->getDevRequires());
|
$requires = array_merge($requires, array_keys($rootDevRequires));
|
||||||
$require = array_merge($require, $requireDev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($packages)) {
|
if (!empty($packages)) {
|
||||||
$packages = array_intersect($packages, $require);
|
$packages = array_intersect($packages, $requires);
|
||||||
} else {
|
} else {
|
||||||
$packages = $require;
|
$packages = $requires;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,4 +286,19 @@ EOT
|
||||||
|
|
||||||
throw new \RuntimeException('Installation aborted.');
|
throw new \RuntimeException('Installation aborted.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function appendConstraintToLink(Link $link, $constraint)
|
||||||
|
{
|
||||||
|
$parser = new VersionParser;
|
||||||
|
$oldPrettyString = $link->getConstraint()->getPrettyString();
|
||||||
|
$newConstraint = MultiConstraint::create(array($link->getConstraint(), $parser->parseConstraints($constraint)));
|
||||||
|
$newConstraint->setPrettyString($oldPrettyString.' && '.$constraint);
|
||||||
|
return new Link(
|
||||||
|
$link->getSource(),
|
||||||
|
$link->getTarget(),
|
||||||
|
$newConstraint,
|
||||||
|
$link->getDescription(),
|
||||||
|
$link->getPrettyConstraint() . ' && ' . $constraint
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue