1
0
Fork 0

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
Carlos Granados 2024-09-18 15:34:25 +02:00 committed by GitHub
parent 17930441a1
commit c8838f198e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 132 additions and 9 deletions

View File

@ -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`.

View File

@ -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

View File

@ -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."

View File

@ -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) {

View File

@ -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);

View File

@ -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;
} }
/** /**

View File

@ -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,
]; ];

View File

@ -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