class BumpCommand
@author Jordi Boggiano <j.boggiano@seld.be>
Hierarchy
- class \Symfony\Component\Console\Command\Command
- class \Composer\Command\BaseCommand extends \Symfony\Component\Console\Command\Command
- class \Composer\Command\BumpCommand extends \Composer\Command\BaseCommand uses \Composer\Command\CompletionTrait
- class \Composer\Command\BaseCommand extends \Symfony\Component\Console\Command\Command
Expanded class hierarchy of BumpCommand
File
-
vendor/
composer/ composer/ src/ Composer/ Command/ BumpCommand.php, line 35
Namespace
Composer\CommandView source
final class BumpCommand extends BaseCommand {
private const ERROR_GENERIC = 1;
private const ERROR_LOCK_OUTDATED = 2;
use CompletionTrait;
protected function configure() : void {
$this->setName('bump')
->setDescription('Increases the lower limit of your composer.json requirements to the currently installed versions')
->setDefinition([
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(<<<EOT
The <info>bump</info> command increases the lower limit of your composer.json requirements
to the currently installed versions. This helps to ensure your dependencies do not
accidentally get downgraded due to some other conflict, and can slightly improve
dependency resolution performance as it limits the amount of package versions
Composer has to look at.
Running this blindly on libraries is **NOT** recommended as it will narrow down
your allowed dependencies, which may cause dependency hell for your users.
Running it with <info>--dev-only</info> on libraries may be fine however as dev requirements
are local to the library and do not affect consumers of the package.
EOT
);
}
/**
* @throws \Seld\JsonLint\ParsingException
*/
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();
if (!Filesystem::isReadable($composerJsonPath)) {
$io->writeError('<error>' . $composerJsonPath . ' is not readable.</error>');
return self::ERROR_GENERIC;
}
$composerJson = new JsonFile($composerJsonPath);
$contents = file_get_contents($composerJson->getPath());
if (false === $contents) {
$io->writeError('<error>' . $composerJsonPath . ' is not readable.</error>');
return self::ERROR_GENERIC;
}
// check for writability by writing to the file as is_writable can not be trusted on network-mounts
// see https://github.com/composer/composer/issues/8231 and https://bugs.php.net/bug.php?id=68926
if (!is_writable($composerJsonPath) && false === Silencer::call('file_put_contents', $composerJsonPath, $contents)) {
$io->writeError('<error>' . $composerJsonPath . ' is not writable.</error>');
return self::ERROR_GENERIC;
}
unset($contents);
$composer = $this->requireComposer();
if ($composer->getLocker()
->isLocked()) {
if (!$composer->getLocker()
->isFresh()) {
$io->writeError('<error>The lock file is not up to date with the latest changes in composer.json. Run the appropriate `update` to fix that before you use the `bump` command.</error>');
return self::ERROR_LOCK_OUTDATED;
}
$repo = $composer->getLocker()
->getLockedRepository(true);
}
else {
$repo = $composer->getRepositoryManager()
->getLocalRepository();
}
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>');
}
unset($contents);
}
$bumper = new VersionBumper();
$tasks = [];
if (!$devOnly) {
$tasks['require'] = $composer->getPackage()
->getRequires();
}
if (!$noDevOnly) {
$tasks['require-dev'] = $composer->getPackage()
->getDevRequires();
}
if (count($packagesFilter) > 0) {
$pattern = BasePackage::packageNamesToRegexp(array_unique(array_map('strtolower', $packagesFilter)));
foreach ($tasks as $key => $reqs) {
foreach ($reqs as $pkgName => $link) {
if (!Preg::isMatch($pattern, $pkgName)) {
unset($tasks[$key][$pkgName]);
}
}
}
}
$updates = [];
foreach ($tasks as $key => $reqs) {
foreach ($reqs as $pkgName => $link) {
if (PlatformRepository::isPlatformPackage($pkgName)) {
continue;
}
$currentConstraint = $link->getPrettyConstraint();
$package = $repo->findPackage($pkgName, '*');
// name must be provided or replaced
if (null === $package) {
continue;
}
while ($package instanceof AliasPackage) {
$package = $package->getAliasOf();
}
$bumped = $bumper->bumpRequirement($link->getConstraint(), $package);
if ($bumped === $currentConstraint) {
continue;
}
$updates[$key][$pkgName] = $bumped;
}
}
if (!$dryRun && !$this->updateFileCleanly($composerJson, $updates)) {
$composerDefinition = $composerJson->read();
foreach ($updates as $key => $packages) {
foreach ($packages as $package => $version) {
$composerDefinition[$key][$package] = $version;
}
}
$composerJson->write($composerDefinition);
}
$changeCount = array_sum(array_map('count', $updates));
if ($changeCount > 0) {
if ($dryRun) {
$io->write('<info>' . $composerJsonPath . ' would be updated with:</info>');
foreach ($updates as $requireType => $packages) {
foreach ($packages as $package => $version) {
$io->write(sprintf('<info> - %s.%s: %s</info>', $requireType, $package, $version));
}
}
}
else {
$io->write('<info>' . $composerJsonPath . ' has been updated (' . $changeCount . ' changes).</info>');
}
}
else {
$io->write('<info>No requirements to update in ' . $composerJsonPath . '.</info>');
}
if (!$dryRun && $composer->getLocker()
->isLocked() && $composer->getConfig()
->get('lock') && $changeCount > 0) {
$composer->getLocker()
->updateHash($composerJson);
}
if ($dryRun && $changeCount > 0) {
return self::ERROR_GENERIC;
}
return 0;
}
/**
* @param array<'require'|'require-dev', array<string, string>> $updates
*/
private function updateFileCleanly(JsonFile $json, array $updates) : bool {
$contents = file_get_contents($json->getPath());
if (false === $contents) {
throw new \RuntimeException('Unable to read ' . $json->getPath() . ' contents.');
}
$manipulator = new JsonManipulator($contents);
foreach ($updates as $key => $packages) {
foreach ($packages as $package => $version) {
if (!$manipulator->addLink($key, $package, $version)) {
return false;
}
}
}
if (false === file_put_contents($json->getPath(), $manipulator->getContents())) {
throw new \RuntimeException('Unable to write new ' . $json->getPath() . ' contents.');
}
return true;
}
}
Members
Title Sort descending | Deprecated | Modifiers | Object type | Summary | Overriden Title | Overrides |
---|---|---|---|---|---|---|
BaseCommand::$composer | private | property | ||||
BaseCommand::$io | private | property | ||||
BaseCommand::complete | public | function | @inheritdoc | Overrides Command::complete | 1 | |
BaseCommand::createComposerInstance | protected | function | Calls { | |||
BaseCommand::formatRequirements | protected | function | ||||
BaseCommand::getApplication | public | function | Gets the application instance for this command. | Overrides Command::getApplication | ||
BaseCommand::getAuditFormat | protected | function | @internal | |||
BaseCommand::getComposer | Deprecated | public | function | |||
BaseCommand::getIO | public | function | ||||
BaseCommand::getPlatformRequirementFilter | protected | function | ||||
BaseCommand::getPreferredInstallOptions | protected | function | Returns preferSource and preferDist values based on the configuration. | |||
BaseCommand::getTerminalWidth | protected | function | ||||
BaseCommand::initialize | protected | function | @inheritDoc | Overrides Command::initialize | 1 | |
BaseCommand::isProxyCommand | public | function | Whether or not this command is meant to call another command. | 2 | ||
BaseCommand::normalizeRequirements | protected | function | ||||
BaseCommand::renderTable | protected | function | ||||
BaseCommand::resetComposer | public | function | Removes the cached composer instance | |||
BaseCommand::setComposer | public | function | ||||
BaseCommand::setIO | public | function | ||||
BaseCommand::tryComposer | public | function | Retrieves the default Composer\Composer instance or null | |||
BumpCommand::configure | protected | function | Configures the current command. | Overrides Command::configure | ||
BumpCommand::doBump | public | function | ||||
BumpCommand::ERROR_GENERIC | private | constant | ||||
BumpCommand::ERROR_LOCK_OUTDATED | private | constant | ||||
BumpCommand::execute | protected | function | Overrides Command::execute | |||
BumpCommand::updateFileCleanly | private | function | ||||
Command::$aliases | private | property | 1 | |||
Command::$application | private | property | ||||
Command::$code | private | property | ||||
Command::$definition | private | property | ||||
Command::$description | private | property | 1 | |||
Command::$fullDefinition | private | property | ||||
Command::$help | private | property | ||||
Command::$helperSet | private | property | ||||
Command::$hidden | private | property | ||||
Command::$ignoreValidationErrors | private | property | 2 | |||
Command::$name | private | property | ||||
Command::$processTitle | private | property | ||||
Command::$synopsis | private | property | ||||
Command::$usages | private | property | ||||
Command::addArgument | public | function | Adds an argument. | 2 | ||
Command::addOption | public | function | Adds an option. | 2 | ||
Command::addUsage | public | function | Add a command usage example, it'll be prefixed with the command name. | 2 | ||
Command::FAILURE | public | constant | ||||
Command::getAliases | public | function | Returns the aliases for the command. | |||
Command::getDefaultDescription | public static | function | ||||
Command::getDefaultName | public static | function | ||||
Command::getDefinition | public | function | Gets the InputDefinition attached to this Command. | 2 | ||
Command::getDescription | public | function | Returns the description for the command. | |||
Command::getHelp | public | function | Returns the help for the command. | 2 | ||
Command::getHelper | public | function | Gets a helper instance by name. | 2 | ||
Command::getHelperSet | public | function | Gets the helper set. | 1 | ||
Command::getName | public | function | Returns the command name. | |||
Command::getNativeDefinition | public | function | Gets the InputDefinition to be used to create representations of this Command. | 2 | ||
Command::getProcessedHelp | public | function | Returns the processed help for the command replacing the %command.name% and %command.full_name% patterns with the real values dynamically. |
2 | ||
Command::getSynopsis | public | function | Returns the synopsis for the command. | 2 | ||
Command::getUsages | public | function | Returns alternative usages of the command. | 2 | ||
Command::ignoreValidationErrors | public | function | Ignores validation errors. | 2 | ||
Command::interact | protected | function | Interacts with the user. | 5 | ||
Command::INVALID | public | constant | ||||
Command::isEnabled | public | function | Checks whether the command is enabled or not in the current environment. | 2 | ||
Command::isHidden | public | function | ||||
Command::mergeApplicationDefinition | public | function | Merges the application definition with the command definition. | 2 | ||
Command::run | public | function | Runs the command. | 4 | ||
Command::setAliases | public | function | Sets the aliases for the command. | |||
Command::setApplication | public | function | 2 | |||
Command::setCode | public | function | Sets the code to execute when running this command. | 2 | ||
Command::setDefinition | public | function | Sets an array of argument and option instances. | 2 | ||
Command::setDescription | public | function | Sets the description for the command. | |||
Command::setHelp | public | function | Sets the help for the command. | 2 | ||
Command::setHelperSet | public | function | 2 | |||
Command::setHidden | public | function | ||||
Command::setName | public | function | Sets the name of the command. | |||
Command::setProcessTitle | public | function | Sets the process title of the command. | 2 | ||
Command::SUCCESS | public | constant | ||||
Command::validateName | private | function | Validates a command name. | |||
Command::__construct | public | function | 15 | |||
CompletionTrait::requireComposer | abstract public | function | ||||
CompletionTrait::suggestAvailablePackage | private | function | Suggest package names available on all configured repositories. | |||
CompletionTrait::suggestAvailablePackageInclPlatform | private | function | Suggest package names available on all configured repositories or platform packages from the ones available on the currently-running PHP |
|||
CompletionTrait::suggestInstalledPackage | private | function | Suggest package names from installed. | |||
CompletionTrait::suggestInstalledPackageTypes | private | function | Suggest package names from installed. | |||
CompletionTrait::suggestPlatformPackage | private | function | Suggest platform packages from the ones available on the currently-running PHP | |||
CompletionTrait::suggestPreferInstall | private | function | Suggestion values for "prefer-install" option | |||
CompletionTrait::suggestRootRequirement | private | function | Suggest package names from root requirements. |