Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to run bump after update #11942

Merged
merged 5 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/03-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
* **--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.

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

Expand Down
6 changes: 6 additions & 0 deletions doc/06-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
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

Defaults to `false`. Ignores error during `install` if there are any missing
Expand Down
4 changes: 4 additions & 0 deletions res/composer-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,10 @@
"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."
},
"bump-after-update": {
"type": ["string", "boolean"],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should use enum to restrict the supported strings

"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": {
"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."
Expand Down
31 changes: 24 additions & 7 deletions src/Composer/Command/BumpCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

namespace Composer\Command;

use Composer\IO\IOInterface;
use Composer\Package\AliasPackage;
use Composer\Package\BasePackage;
use Composer\Package\Locker;
Expand Down Expand Up @@ -72,9 +73,28 @@ protected function configure(): void
*/
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 */
$composerJsonPath = Factory::getComposerFile();
$io = $this->getIO();

if (!Filesystem::isReadable($composerJsonPath)) {
$io->writeError('<error>'.$composerJsonPath.' is not readable.</error>');
Expand Down Expand Up @@ -112,7 +132,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$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>');

$contents = $composerJson->read();
Expand All @@ -125,14 +145,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int

$bumper = new VersionBumper();
$tasks = [];
if (!$input->getOption('dev-only')) {
if (!$devOnly) {
$tasks['require'] = $composer->getPackage()->getRequires();
}
if (!$input->getOption('no-dev-only')) {
if (!$noDevOnly) {
$tasks['require-dev'] = $composer->getPackage()->getDevRequires();
}

$packagesFilter = $input->getArgument('packages');
if (count($packagesFilter) > 0) {
$pattern = BasePackage::packageNamesToRegexp(array_unique(array_map('strtolower', $packagesFilter)));
foreach ($tasks as $key => $reqs) {
Expand Down Expand Up @@ -171,8 +190,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}
}

$dryRun = $input->getOption('dry-run');

if (!$dryRun && !$this->updateFileCleanly($composerJson, $updates)) {
$composerDefinition = $composerJson->read();
foreach ($updates as $key => $packages) {
Expand Down
12 changes: 12 additions & 0 deletions src/Composer/Command/ConfigCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,18 @@ static function ($val) {
'prepend-autoloader' => [$booleanValidator, $booleanNormalizer],
'disable-tls' => [$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' => [
static function ($val): bool {
return file_exists($val) && Filesystem::isReadable($val);
Expand Down
24 changes: 23 additions & 1 deletion src/Composer/Command/UpdateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ protected function configure()
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('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']),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using InputOption::VALUE_OPTIONAL is confusing here, because it makes composer update --bump-after-update valid during argument validation, but this would still make it use the default value (i.e. false). This should probably be using VALUE_REQUIRED instead.

])
->setHelp(
<<<EOT
Expand Down Expand Up @@ -255,7 +256,28 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$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;
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/Composer/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ class Config
'gitlab-token' => [],
'http-basic' => [],
'bearer' => [],
'bump-after-update' => false,
'allow-missing-requirements' => false,
];

Expand Down
62 changes: 61 additions & 1 deletion tests/Composer/Test/Command/UpdateCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,14 @@ class UpdateCommandTest extends TestCase
* @param array<mixed> $composerJson
* @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);

if ($createLock) {
Copy link
Contributor Author

@carlos-granados carlos-granados Apr 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bump command needs that a lock file exists, so when testing the bump-after-update version we need to create one

$this->createComposerLock();
}

$appTester = $this->getApplicationTester();
$appTester->run(array_merge(['command' => 'update', '--dry-run' => true, '--no-audit' => true], $command));

Expand Down Expand Up @@ -126,6 +130,62 @@ public static function provideUpdates(): \Generator
Run `composer require root/req` or `composer require root/req:^2` instead to replace the constraint
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 the dev-only option to only bump dependencies within "require-dev".</warning>
Seldaek marked this conversation as resolved.
Show resolved Hide resolved
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
Expand Down
Loading