Add --patch-only flag to update command to restrict updates to patch versions and make them safer (#12122)
Fixes #11446pull/12129/head
parent
6b81140f81
commit
c8bd0e6278
|
@ -232,6 +232,7 @@ php composer.phar update vendor/package:2.0.1 vendor/package2:3.0.*
|
|||
COMPOSER_PREFER_LOWEST=1 env var.
|
||||
* **--minimal-changes (-m):** During a partial update with `-w`/`-W`, only perform absolutely necessary
|
||||
changes to transitive dependencies. Can also be set via the COMPOSER_MINIMAL_CHANGES=1 env var.
|
||||
* **--patch-only:** Only allow patch version updates for currently installed dependencies.
|
||||
* **--interactive:** Interactive interface with autocompletion to select the packages to update.
|
||||
* **--root-reqs:** Restricts the update to your first degree dependencies.
|
||||
* **--bump-after-update:** Runs `bump` after performing the update. Set to `dev` or `no-dev` to only bump those dependencies.
|
||||
|
|
|
@ -28,6 +28,7 @@ use Composer\Repository\CompositeRepository;
|
|||
use Composer\Repository\PlatformRepository;
|
||||
use Composer\Repository\RepositoryInterface;
|
||||
use Composer\Repository\RepositorySet;
|
||||
use Composer\Semver\Constraint\MultiConstraint;
|
||||
use Composer\Semver\Intervals;
|
||||
use Composer\Util\HttpDownloader;
|
||||
use Composer\Advisory\Auditor;
|
||||
|
@ -83,6 +84,7 @@ class UpdateCommand extends BaseCommand
|
|||
new InputOption('prefer-stable', null, InputOption::VALUE_NONE, 'Prefer stable versions of dependencies (can also be set via the COMPOSER_PREFER_STABLE=1 env var).'),
|
||||
new InputOption('prefer-lowest', null, InputOption::VALUE_NONE, 'Prefer lowest versions of dependencies (can also be set via the COMPOSER_PREFER_LOWEST=1 env var).'),
|
||||
new InputOption('minimal-changes', 'm', InputOption::VALUE_NONE, 'During a partial update with -w/-W, only perform absolutely necessary changes to transitive dependencies (can also be set via the COMPOSER_MINIMAL_CHANGES=1 env var).'),
|
||||
new InputOption('patch-only', null, InputOption::VALUE_NONE, 'Only allow patch version updates for currently installed dependencies.'),
|
||||
new InputOption('interactive', 'i', InputOption::VALUE_NONE, 'Interactive interface with autocompletion to select the packages to update.'),
|
||||
new InputOption('root-reqs', null, InputOption::VALUE_NONE, 'Restricts the update to your first degree dependencies.'),
|
||||
new InputOption('bump-after-update', null, InputOption::VALUE_OPTIONAL, 'Runs bump after performing the update.', false, ['dev', 'no-dev', 'all']),
|
||||
|
@ -175,6 +177,26 @@ EOT
|
|||
}
|
||||
}
|
||||
|
||||
if ($input->getOption('patch-only')) {
|
||||
if (!$composer->getLocker()->isLocked()) {
|
||||
throw new \InvalidArgumentException('patch-only can only be used with a lock file present');
|
||||
}
|
||||
foreach ($composer->getLocker()->getLockedRepository(true)->getCanonicalPackages() as $package) {
|
||||
if ($package->isDev()) {
|
||||
continue;
|
||||
}
|
||||
if (!Preg::isMatch('{^(\d+\.\d+\.\d+)}', $package->getVersion(), $match)) {
|
||||
continue;
|
||||
}
|
||||
$constraint = $parser->parseConstraints('~'.$match[1]);
|
||||
if (isset($temporaryConstraints[$package->getName()])) {
|
||||
$temporaryConstraints[$package->getName()] = MultiConstraint::create([$temporaryConstraints[$package->getName()], $constraint], true);
|
||||
} else {
|
||||
$temporaryConstraints[$package->getName()] = $constraint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($input->getOption('interactive')) {
|
||||
$packages = $this->getPackagesInteractively($io, $input, $output, $composer, $packages);
|
||||
}
|
||||
|
|
|
@ -185,7 +185,69 @@ Your requirements could not be resolved to an installable set of packages.
|
|||
- root/req 1.0.0 requires dep/pkg ^1 -> found dep/pkg[1.0.0, 1.0.1, 1.0.2] but it conflicts with your temporary update constraint (dep/pkg:^2).
|
||||
OUTPUT
|
||||
];
|
||||
}
|
||||
|
||||
public function testUpdateWithPatchOnly(): void
|
||||
{
|
||||
$this->initTempComposer([
|
||||
'repositories' => [
|
||||
'packages' => [
|
||||
'type' => 'package',
|
||||
'package' => [
|
||||
['name' => 'root/req', 'version' => '1.0.0'],
|
||||
['name' => 'root/req', 'version' => '1.0.1'],
|
||||
['name' => 'root/req', 'version' => '1.1.0'],
|
||||
['name' => 'root/req2', 'version' => '1.0.0'],
|
||||
['name' => 'root/req2', 'version' => '1.0.1'],
|
||||
['name' => 'root/req2', 'version' => '1.1.0'],
|
||||
['name' => 'root/req3', 'version' => '1.0.0'],
|
||||
['name' => 'root/req3', 'version' => '1.0.1'],
|
||||
['name' => 'root/req3', 'version' => '1.1.0'],
|
||||
],
|
||||
],
|
||||
],
|
||||
'require' => [
|
||||
'root/req' => '1.*',
|
||||
'root/req2' => '1.*',
|
||||
'root/req3' => '1.*',
|
||||
],
|
||||
]);
|
||||
|
||||
$package = self::getPackage('root/req', '1.0.0');
|
||||
$package2 = self::getPackage('root/req2', '1.0.0');
|
||||
$package3 = self::getPackage('root/req3', '1.0.0');
|
||||
$this->createComposerLock([$package, $package2, $package3]);
|
||||
|
||||
$appTester = $this->getApplicationTester();
|
||||
// root/req fails because of incompatible --with requirement
|
||||
$appTester->run(array_merge(['command' => 'update', '--dry-run' => true, '--no-audit' => true, '--no-install' => true, '--patch-only' => true, '--with' => ['root/req:^1.1']]));
|
||||
|
||||
$expected = <<<OUTPUT
|
||||
Loading composer repositories with package information
|
||||
Updating dependencies
|
||||
Your requirements could not be resolved to an installable set of packages.
|
||||
|
||||
Problem 1
|
||||
- Root composer.json requires root/req 1.*, found root/req[1.0.0, 1.0.1, 1.1.0] but it conflicts with your temporary update constraint (root/req:[[>= 1.1.0.0-dev < 2.0.0.0-dev] [>= 1.0.0.0-dev < 1.1.0.0-dev]]).
|
||||
OUTPUT;
|
||||
|
||||
self::assertStringMatchesFormat(trim($expected), trim($appTester->getDisplay(true)));
|
||||
|
||||
$appTester = $this->getApplicationTester();
|
||||
// root/req upgrades to 1.0.1 as that is compatible with the --with requirement now
|
||||
// root/req2 upgrades to 1.0.1 only due to --patch-only
|
||||
// root/req3 does not update as it is not in the allowlist
|
||||
$appTester->run(array_merge(['command' => 'update', '--dry-run' => true, '--no-audit' => true, '--no-install' => true, '--patch-only' => true, '--with' => ['root/req:^1.0.1'], 'packages' => ['root/req', 'root/req2']]));
|
||||
|
||||
$expected = <<<OUTPUT
|
||||
Loading composer repositories with package information
|
||||
Updating dependencies
|
||||
Lock file operations: 0 installs, 2 updates, 0 removals
|
||||
- Upgrading root/req (1.0.0 => 1.0.1)
|
||||
- Upgrading root/req2 (1.0.0 => 1.0.1)
|
||||
OUTPUT;
|
||||
|
||||
self::assertStringMatchesFormat(trim($expected), trim($appTester->getDisplay(true)));
|
||||
}
|
||||
|
||||
public function testInteractiveModeThrowsIfNoPackageToUpdate(): void
|
||||
|
|
Loading…
Reference in New Issue