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 2 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.
Seldaek marked this conversation as resolved.
Show resolved Hide resolved

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
5 changes: 5 additions & 0 deletions doc/06-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -476,4 +476,9 @@ 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.
Seldaek marked this conversation as resolved.
Show resolved Hide resolved

← [Repositories](05-repositories.md) | [Runtime](07-runtime.md) →
4 changes: 4 additions & 0 deletions res/composer-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,10 @@
"platform-check": {
"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."
Seldaek marked this conversation as resolved.
Show resolved Hide resolved
}
}
},
Expand Down
33 changes: 25 additions & 8 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,27 +132,26 @@ 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();
if (!isset($contents['type'])) {
$io->writeError('<warning>If your package is not a library, you can explicitly specify the "type" by using "composer config type project".</warning>');
$io->writeError('<warning>Alternatively you can use --dev-only to only bump dependencies within "require-dev".</warning>');
$io->writeError('<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
}
unset($contents);
}

$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
23 changes: 22 additions & 1 deletion src/Composer/Command/UpdateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,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_NONE, 'Runs Bump after performing the update.'),
Seldaek marked this conversation as resolved.
Show resolved Hide resolved
])
->setHelp(
<<<EOT
Expand Down Expand Up @@ -248,7 +249,27 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$install->disablePlugins();
}

return $install->run();
$result = $install->run();

if ($result === 0) {
$bumpAfterUpdate = (bool) $input->getOption('bump-after-update') || (bool) $composer->getConfig()->get('bump-after-update');

if ($bumpAfterUpdate) {
$io->writeError('<info>Bumping dependencies</info>');
$bumpCommand = new BumpCommand();
$bumpCommand->setComposer($composer);
$result = $bumpCommand->doBump(
$io,
$input->getOption('bump-after-update') === 'dev'
|| $composer->getConfig()->get('bump-after-update') === 'dev',
$input->getOption('bump-after-update') === 'no-dev'
|| $composer->getConfig()->get('bump-after-update') === 'no-dev',
$input->getOption('dry-run'),
$input->getArgument('packages')
);
}
Seldaek marked this conversation as resolved.
Show resolved Hide resolved
}
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,
];

/** @var array<string, mixed> */
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 @@ -22,10 +22,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 @@ -124,6 +128,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 testInteractiveModeThrowsIfNoPackageEntered(): void
Expand Down
Loading