Add option to run bump after update (#11942)
* Add option to run bump after update * Convert the option into a bool | string parameter and change a couple of texts * Apply suggestions from code review * Fix tests --------- Co-authored-by: Jordi Boggiano <j.boggiano@seld.be>pull/12114/head
parent
17930441a1
commit
c8838f198e
|
@ -234,6 +234,7 @@ php composer.phar update vendor/package:2.0.1 vendor/package2:3.0.*
|
||||||
changes to transitive dependencies. Can also be set via the COMPOSER_MINIMAL_CHANGES=1 env var.
|
changes to transitive dependencies. Can also be set via the COMPOSER_MINIMAL_CHANGES=1 env var.
|
||||||
* **--interactive:** Interactive interface with autocompletion to select the packages to update.
|
* **--interactive:** Interactive interface with autocompletion to select the packages to update.
|
||||||
* **--root-reqs:** Restricts the update to your first degree dependencies.
|
* **--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.
|
||||||
|
|
||||||
Specifying one of the words `mirrors`, `lock`, or `nothing` as an argument has the same effect as specifying the option `--lock`, for example `composer update mirrors` is exactly the same as `composer update --lock`.
|
Specifying one of the words `mirrors`, `lock`, or `nothing` as an argument has the same effect as specifying the option `--lock`, for example `composer update mirrors` is exactly the same as `composer update --lock`.
|
||||||
|
|
||||||
|
|
|
@ -481,6 +481,12 @@ throw, but you can set this config option to `["example.org"]` to allow using sv
|
||||||
URLs on that hostname. This is a better/safer alternative to disabling `secure-http`
|
URLs on that hostname. This is a better/safer alternative to disabling `secure-http`
|
||||||
altogether.
|
altogether.
|
||||||
|
|
||||||
|
## bump-after-update
|
||||||
|
|
||||||
|
Defaults to `false` and can be any of `true`, `false`, `"dev"` or `"no-dev"`. If
|
||||||
|
set to true, Composer will run the `bump` command after running the `update` command.
|
||||||
|
If set to `"dev"` or `"no-dev"` then only the corresponding dependencies will be bumped.
|
||||||
|
|
||||||
## allow-missing-requirements
|
## allow-missing-requirements
|
||||||
|
|
||||||
Defaults to `false`. Ignores error during `install` if there are any missing
|
Defaults to `false`. Ignores error during `install` if there are any missing
|
||||||
|
|
|
@ -663,6 +663,10 @@
|
||||||
"type": ["boolean", "string"],
|
"type": ["boolean", "string"],
|
||||||
"description": "Defaults to \"php-only\" which checks only the PHP version. Setting to true will also check the presence of required PHP extensions. If set to false, Composer will not create and require a platform_check.php file as part of the autoloader bootstrap."
|
"description": "Defaults to \"php-only\" which checks only the PHP version. Setting to true will also check the presence of required PHP extensions. If set to false, Composer will not create and require a platform_check.php file as part of the autoloader bootstrap."
|
||||||
},
|
},
|
||||||
|
"bump-after-update": {
|
||||||
|
"type": ["string", "boolean"],
|
||||||
|
"description": "Defaults to false and can be any of true, false, \"dev\"` or \"no-dev\"`. If set to true, Composer will run the bump command after running the update command. If set to \"dev\" or \"no-dev\" then only the corresponding dependencies will be bumped."
|
||||||
|
},
|
||||||
"allow-missing-requirements": {
|
"allow-missing-requirements": {
|
||||||
"type": ["boolean"],
|
"type": ["boolean"],
|
||||||
"description": "Defaults to false. If set to true, Composer will allow install when lock file is not up to date with the latest changes in composer.json."
|
"description": "Defaults to false. If set to true, Composer will allow install when lock file is not up to date with the latest changes in composer.json."
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
namespace Composer\Command;
|
namespace Composer\Command;
|
||||||
|
|
||||||
|
use Composer\IO\IOInterface;
|
||||||
use Composer\Package\AliasPackage;
|
use Composer\Package\AliasPackage;
|
||||||
use Composer\Package\BasePackage;
|
use Composer\Package\BasePackage;
|
||||||
use Composer\Package\Locker;
|
use Composer\Package\Locker;
|
||||||
|
@ -72,9 +73,28 @@ EOT
|
||||||
*/
|
*/
|
||||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
{
|
{
|
||||||
|
return $this->doBump(
|
||||||
|
$this->getIO(),
|
||||||
|
$input->getOption('dev-only'),
|
||||||
|
$input->getOption('no-dev-only'),
|
||||||
|
$input->getOption('dry-run'),
|
||||||
|
$input->getArgument('packages')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string[] $packagesFilter
|
||||||
|
* @throws \Seld\JsonLint\ParsingException
|
||||||
|
*/
|
||||||
|
public function doBump(
|
||||||
|
IOInterface $io,
|
||||||
|
bool $devOnly,
|
||||||
|
bool $noDevOnly,
|
||||||
|
bool $dryRun,
|
||||||
|
array $packagesFilter
|
||||||
|
): int {
|
||||||
/** @readonly */
|
/** @readonly */
|
||||||
$composerJsonPath = Factory::getComposerFile();
|
$composerJsonPath = Factory::getComposerFile();
|
||||||
$io = $this->getIO();
|
|
||||||
|
|
||||||
if (!Filesystem::isReadable($composerJsonPath)) {
|
if (!Filesystem::isReadable($composerJsonPath)) {
|
||||||
$io->writeError('<error>'.$composerJsonPath.' is not readable.</error>');
|
$io->writeError('<error>'.$composerJsonPath.' is not readable.</error>');
|
||||||
|
@ -112,7 +132,7 @@ EOT
|
||||||
$repo = $composer->getRepositoryManager()->getLocalRepository();
|
$repo = $composer->getRepositoryManager()->getLocalRepository();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($composer->getPackage()->getType() !== 'project' && !$input->getOption('dev-only')) {
|
if ($composer->getPackage()->getType() !== 'project' && !$devOnly) {
|
||||||
$io->writeError('<warning>Warning: Bumping dependency constraints is not recommended for libraries as it will narrow down your dependencies and may cause problems for your users.</warning>');
|
$io->writeError('<warning>Warning: Bumping dependency constraints is not recommended for libraries as it will narrow down your dependencies and may cause problems for your users.</warning>');
|
||||||
|
|
||||||
$contents = $composerJson->read();
|
$contents = $composerJson->read();
|
||||||
|
@ -125,14 +145,13 @@ EOT
|
||||||
|
|
||||||
$bumper = new VersionBumper();
|
$bumper = new VersionBumper();
|
||||||
$tasks = [];
|
$tasks = [];
|
||||||
if (!$input->getOption('dev-only')) {
|
if (!$devOnly) {
|
||||||
$tasks['require'] = $composer->getPackage()->getRequires();
|
$tasks['require'] = $composer->getPackage()->getRequires();
|
||||||
}
|
}
|
||||||
if (!$input->getOption('no-dev-only')) {
|
if (!$noDevOnly) {
|
||||||
$tasks['require-dev'] = $composer->getPackage()->getDevRequires();
|
$tasks['require-dev'] = $composer->getPackage()->getDevRequires();
|
||||||
}
|
}
|
||||||
|
|
||||||
$packagesFilter = $input->getArgument('packages');
|
|
||||||
if (count($packagesFilter) > 0) {
|
if (count($packagesFilter) > 0) {
|
||||||
$pattern = BasePackage::packageNamesToRegexp(array_unique(array_map('strtolower', $packagesFilter)));
|
$pattern = BasePackage::packageNamesToRegexp(array_unique(array_map('strtolower', $packagesFilter)));
|
||||||
foreach ($tasks as $key => $reqs) {
|
foreach ($tasks as $key => $reqs) {
|
||||||
|
@ -171,8 +190,6 @@ EOT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$dryRun = $input->getOption('dry-run');
|
|
||||||
|
|
||||||
if (!$dryRun && !$this->updateFileCleanly($composerJson, $updates)) {
|
if (!$dryRun && !$this->updateFileCleanly($composerJson, $updates)) {
|
||||||
$composerDefinition = $composerJson->read();
|
$composerDefinition = $composerJson->read();
|
||||||
foreach ($updates as $key => $packages) {
|
foreach ($updates as $key => $packages) {
|
||||||
|
|
|
@ -469,6 +469,18 @@ EOT
|
||||||
'prepend-autoloader' => [$booleanValidator, $booleanNormalizer],
|
'prepend-autoloader' => [$booleanValidator, $booleanNormalizer],
|
||||||
'disable-tls' => [$booleanValidator, $booleanNormalizer],
|
'disable-tls' => [$booleanValidator, $booleanNormalizer],
|
||||||
'secure-http' => [$booleanValidator, $booleanNormalizer],
|
'secure-http' => [$booleanValidator, $booleanNormalizer],
|
||||||
|
'bump-after-update' => [
|
||||||
|
static function ($val): bool {
|
||||||
|
return in_array($val, ['dev', 'no-dev', 'true', 'false', '1', '0'], true);
|
||||||
|
},
|
||||||
|
static function ($val) {
|
||||||
|
if ('dev' === $val || 'no-dev' === $val) {
|
||||||
|
return $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $val !== 'false' && (bool) $val;
|
||||||
|
},
|
||||||
|
],
|
||||||
'cafile' => [
|
'cafile' => [
|
||||||
static function ($val): bool {
|
static function ($val): bool {
|
||||||
return file_exists($val) && Filesystem::isReadable($val);
|
return file_exists($val) && Filesystem::isReadable($val);
|
||||||
|
|
|
@ -85,6 +85,7 @@ class UpdateCommand extends BaseCommand
|
||||||
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('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('interactive', 'i', InputOption::VALUE_NONE, 'Interactive interface with autocompletion to select the packages to update.'),
|
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('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']),
|
||||||
])
|
])
|
||||||
->setHelp(
|
->setHelp(
|
||||||
<<<EOT
|
<<<EOT
|
||||||
|
@ -255,7 +256,28 @@ EOT
|
||||||
$install->disablePlugins();
|
$install->disablePlugins();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $install->run();
|
$result = $install->run();
|
||||||
|
|
||||||
|
if ($result === 0) {
|
||||||
|
$bumpAfterUpdate = $input->getOption('bump-after-update');
|
||||||
|
if (false === $bumpAfterUpdate) {
|
||||||
|
$bumpAfterUpdate = $composer->getConfig()->get('bump-after-update');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false !== $bumpAfterUpdate) {
|
||||||
|
$io->writeError('<info>Bumping dependencies</info>');
|
||||||
|
$bumpCommand = new BumpCommand();
|
||||||
|
$bumpCommand->setComposer($composer);
|
||||||
|
$result = $bumpCommand->doBump(
|
||||||
|
$io,
|
||||||
|
$bumpAfterUpdate === 'dev',
|
||||||
|
$bumpAfterUpdate === 'no-dev',
|
||||||
|
$input->getOption('dry-run'),
|
||||||
|
$input->getArgument('packages')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -84,6 +84,7 @@ class Config
|
||||||
'gitlab-token' => [],
|
'gitlab-token' => [],
|
||||||
'http-basic' => [],
|
'http-basic' => [],
|
||||||
'bearer' => [],
|
'bearer' => [],
|
||||||
|
'bump-after-update' => false,
|
||||||
'allow-missing-requirements' => false,
|
'allow-missing-requirements' => false,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -24,10 +24,14 @@ class UpdateCommandTest extends TestCase
|
||||||
* @param array<mixed> $composerJson
|
* @param array<mixed> $composerJson
|
||||||
* @param array<mixed> $command
|
* @param array<mixed> $command
|
||||||
*/
|
*/
|
||||||
public function testUpdate(array $composerJson, array $command, string $expected): void
|
public function testUpdate(array $composerJson, array $command, string $expected, bool $createLock = false): void
|
||||||
{
|
{
|
||||||
$this->initTempComposer($composerJson);
|
$this->initTempComposer($composerJson);
|
||||||
|
|
||||||
|
if ($createLock) {
|
||||||
|
$this->createComposerLock();
|
||||||
|
}
|
||||||
|
|
||||||
$appTester = $this->getApplicationTester();
|
$appTester = $this->getApplicationTester();
|
||||||
$appTester->run(array_merge(['command' => 'update', '--dry-run' => true, '--no-audit' => true], $command));
|
$appTester->run(array_merge(['command' => 'update', '--dry-run' => true, '--no-audit' => true], $command));
|
||||||
|
|
||||||
|
@ -126,6 +130,62 @@ The temporary constraint "^2" for "root/req" must be a subset of the constraint
|
||||||
Run `composer require root/req` or `composer require root/req:^2` instead to replace the constraint
|
Run `composer require root/req` or `composer require root/req:^2` instead to replace the constraint
|
||||||
OUTPUT
|
OUTPUT
|
||||||
];
|
];
|
||||||
|
|
||||||
|
yield 'update & bump' => [
|
||||||
|
$rootDepAndTransitiveDep,
|
||||||
|
['--bump-after-update' => true],
|
||||||
|
<<<OUTPUT
|
||||||
|
Loading composer repositories with package information
|
||||||
|
Updating dependencies
|
||||||
|
Lock file operations: 2 installs, 0 updates, 0 removals
|
||||||
|
- Locking dep/pkg (1.0.2)
|
||||||
|
- Locking root/req (1.0.0)
|
||||||
|
Installing dependencies from lock file (including require-dev)
|
||||||
|
Package operations: 2 installs, 0 updates, 0 removals
|
||||||
|
- Installing dep/pkg (1.0.2)
|
||||||
|
- Installing root/req (1.0.0)
|
||||||
|
Bumping dependencies
|
||||||
|
<warning>Warning: Bumping dependency constraints is not recommended for libraries as it will narrow down your dependencies and may cause problems for your users.</warning>
|
||||||
|
<warning>If your package is not a library, you can explicitly specify the "type" by using "composer config type project".</warning>
|
||||||
|
<warning>Alternatively you can use --dev-only to only bump dependencies within "require-dev".</warning>
|
||||||
|
No requirements to update in ./composer.json.
|
||||||
|
OUTPUT
|
||||||
|
, true
|
||||||
|
];
|
||||||
|
|
||||||
|
yield 'update & bump dev only' => [
|
||||||
|
$rootDepAndTransitiveDep,
|
||||||
|
['--bump-after-update' => 'dev'],
|
||||||
|
<<<OUTPUT
|
||||||
|
Loading composer repositories with package information
|
||||||
|
Updating dependencies
|
||||||
|
Lock file operations: 2 installs, 0 updates, 0 removals
|
||||||
|
- Locking dep/pkg (1.0.2)
|
||||||
|
- Locking root/req (1.0.0)
|
||||||
|
Installing dependencies from lock file (including require-dev)
|
||||||
|
Package operations: 2 installs, 0 updates, 0 removals
|
||||||
|
- Installing dep/pkg (1.0.2)
|
||||||
|
- Installing root/req (1.0.0)
|
||||||
|
Bumping dependencies
|
||||||
|
No requirements to update in ./composer.json.
|
||||||
|
OUTPUT
|
||||||
|
, true
|
||||||
|
];
|
||||||
|
|
||||||
|
yield 'update & dump with failing update' => [
|
||||||
|
$rootDepAndTransitiveDep,
|
||||||
|
['--with' => ['dep/pkg:^2'], '--bump-after-update' => true],
|
||||||
|
<<<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.* -> satisfiable by root/req[1.0.0].
|
||||||
|
- 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 testInteractiveModeThrowsIfNoPackageToUpdate(): void
|
public function testInteractiveModeThrowsIfNoPackageToUpdate(): void
|
||||||
|
|
Loading…
Reference in New Issue