diff --git a/doc/03-cli.md b/doc/03-cli.md index 221cae101..a65a99d60 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -355,6 +355,7 @@ are local to the library and do not affect consumers of the package. * **--dev-only:** Only bump requirements in "require-dev". * **--no-dev-only:** Only bump requirements in "require". +* **--dry-run:** Outputs the packages to bump, but will not execute anything. ## reinstall diff --git a/src/Composer/Command/BumpCommand.php b/src/Composer/Command/BumpCommand.php index b36908983..99fe9eaba 100644 --- a/src/Composer/Command/BumpCommand.php +++ b/src/Composer/Command/BumpCommand.php @@ -47,6 +47,7 @@ final class BumpCommand extends BaseCommand new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Optional package name(s) to restrict which packages are bumped.', null, $this->suggestRootRequirement()), new InputOption('dev-only', 'D', InputOption::VALUE_NONE, 'Only bump requirements in "require-dev".'), new InputOption('no-dev-only', 'R', InputOption::VALUE_NONE, 'Only bump requirements in "require".'), + new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the packages to bump, but will not execute anything.'), ]) ->setHelp( <<getOption('no-dev-only')) { - $tasks['require-dev'] = $composer->getPackage()->getDevRequires(); - } if (!$input->getOption('dev-only')) { $tasks['require'] = $composer->getPackage()->getRequires(); } + if (!$input->getOption('no-dev-only')) { + $tasks['require-dev'] = $composer->getPackage()->getDevRequires(); + } $packagesFilter = $input->getArgument('packages'); if (count($packagesFilter) > 0) { @@ -170,7 +171,9 @@ EOT } } - if (!$this->updateFileCleanly($composerJson, $updates)) { + $dryRun = $input->getOption('dry-run'); + + if (!$dryRun && !$this->updateFileCleanly($composerJson, $updates)) { $composerDefinition = $composerJson->read(); foreach ($updates as $key => $packages) { foreach ($packages as $package => $version) { @@ -182,12 +185,21 @@ EOT $changeCount = array_sum(array_map('count', $updates)); if ($changeCount > 0) { - $io->write(''.$composerJsonPath.' has been updated ('.$changeCount.' changes).'); + if ($dryRun) { + $io->write('' . $composerJsonPath . ' would be updated with:'); + foreach ($updates as $requireType => $packages) { + foreach ($packages as $package => $version) { + $io->write(sprintf(' - %s.%s: %s', $requireType, $package, $version)); + } + } + } else { + $io->write('' . $composerJsonPath . ' has been updated (' . $changeCount . ' changes).'); + } } else { $io->write('No requirements to update in '.$composerJsonPath.'.'); } - if ($composer->getLocker()->isLocked() && $changeCount > 0) { + if (!$dryRun && $composer->getLocker()->isLocked() && $changeCount > 0) { $contents = file_get_contents($composerJson->getPath()); if (false === $contents) { throw new \RuntimeException('Unable to read '.$composerJson->getPath().' contents to update the lock file hash.'); @@ -198,6 +210,10 @@ EOT $lock->write($lockData); } + if ($dryRun && $changeCount > 0) { + return self::ERROR_GENERIC; + } + return 0; } diff --git a/tests/Composer/Test/Command/BumpCommandTest.php b/tests/Composer/Test/Command/BumpCommandTest.php index cf0dc3723..2953c8971 100644 --- a/tests/Composer/Test/Command/BumpCommandTest.php +++ b/tests/Composer/Test/Command/BumpCommandTest.php @@ -23,7 +23,7 @@ class BumpCommandTest extends TestCase * @param array $command * @param array $expected */ - public function testBump(array $composerJson, array $command, array $expected, bool $lock = true): void + public function testBump(array $composerJson, array $command, array $expected, bool $lock = true, int $exitCode = 0): void { $this->initTempComposer($composerJson); @@ -41,7 +41,7 @@ class BumpCommandTest extends TestCase } $appTester = $this->getApplicationTester(); - $appTester->run(array_merge(['command' => 'bump'], $command)); + $this->assertSame($exitCode, $appTester->run(array_merge(['command' => 'bump'], $command))); $json = new JsonFile('./composer.json'); $this->assertSame($expected, $json->read()); @@ -153,5 +153,53 @@ class BumpCommandTest extends TestCase ], false, ]; + + yield 'bump with --dry-run with packages to bump' => [ + [ + 'require' => [ + 'first/pkg' => '^2.0', + 'second/pkg' => '3.*', + ], + 'require-dev' => [ + 'dev/pkg' => '~2.0', + ], + ], + ['--dry-run' => true], + [ + 'require' => [ + 'first/pkg' => '^2.0', + 'second/pkg' => '3.*', + ], + 'require-dev' => [ + 'dev/pkg' => '~2.0', + ], + ], + true, + 1, + ]; + + yield 'bump with --dry-run without packages to bump' => [ + [ + 'require' => [ + 'first/pkg' => '^2.3.4', + 'second/pkg' => '^3.4', + ], + 'require-dev' => [ + 'dev/pkg' => '^2.3.4.5', + ], + ], + ['--dry-run' => true], + [ + 'require' => [ + 'first/pkg' => '^2.3.4', + 'second/pkg' => '^3.4', + ], + 'require-dev' => [ + 'dev/pkg' => '^2.3.4.5', + ], + ], + true, + 0, + ]; } }