Skip to content

Commit

Permalink
Add prompt suggesting using --dev when require command is used with d…
Browse files Browse the repository at this point in the history
…ev packages (#10960)

* Add prompt suggesting using --dev when require command is used with packages which appear to be dev, fixes #10939
  • Loading branch information
Seldaek committed Jul 21, 2022
1 parent 595c47f commit e59812c
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 1 deletion.
4 changes: 4 additions & 0 deletions doc/04-schema.md
Expand Up @@ -119,6 +119,10 @@ Examples:
- redis
- templating

> **Note**: Some special keywords trigger `composer require` without the
> `--dev` option to prompt users if they would like to add these packages to
> `require-dev` instead of `require`. These are: `dev`, `testing`, `static analysis`.
Optional.

### homepage
Expand Down
33 changes: 32 additions & 1 deletion src/Composer/Command/RequireCommand.php
Expand Up @@ -13,7 +13,10 @@
namespace Composer\Command;

use Composer\DependencyResolver\Request;
use Composer\Package\CompletePackageInterface;
use Composer\Package\PackageInterface;
use Composer\Util\Filesystem;
use Composer\Util\PackageSorter;
use Seld\Signal\SignalHandler;
use Symfony\Component\Console\Input\InputInterface;
use Composer\Console\Input\InputArgument;
Expand Down Expand Up @@ -218,9 +221,36 @@ protected function execute(InputInterface $input, OutputInterface $output)
throw $e;
}

$requirements = $this->formatRequirements($requirements);

if (!$input->getOption('dev') && $io->isInteractive()) {
$devPackages = [];
$devTags = ['dev', 'testing', 'static analysis'];
$currentRequiresByKey = $this->getPackagesByRequireKey();
foreach ($requirements as $name => $version) {
// skip packages which are already in the composer.json as those have already been decided
if (isset($currentRequiresByKey[$name])) {
continue;
}

$pkg = PackageSorter::getMostCurrentVersion($this->getRepos()->findPackages($name));
if ($pkg instanceof CompletePackageInterface && count(array_intersect($devTags, array_map('strtolower', $pkg->getKeywords()))) > 0) {
$devPackages[] = $name;
}
}

if (count($devPackages) === count($requirements)) {
$plural = count($requirements) > 1 ? 's' : '';
$plural2 = count($requirements) > 1 ? 'are' : 'is';
$io->warning('The package'.$plural.' you required '.$plural2.' recommended to be placed in require-dev but you did not use --dev.');
if ($io->askConfirmation('<info>Do you want to re-run the command with --dev?</> [<comment>yes</>]? ')) {
$input->setOption('dev', true);
}
}
}

$requireKey = $input->getOption('dev') ? 'require-dev' : 'require';
$removeKey = $input->getOption('dev') ? 'require' : 'require-dev';
$requirements = $this->formatRequirements($requirements);

// validate requirements format
$versionParser = new VersionParser();
Expand Down Expand Up @@ -254,6 +284,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
return 0;
}

$input->setOption('dev', true);
list($requireKey, $removeKey) = array($removeKey, $requireKey);
}
}
Expand Down
28 changes: 28 additions & 0 deletions src/Composer/Util/PackageSorter.php
Expand Up @@ -17,6 +17,34 @@

class PackageSorter
{
/**
* Returns the most recent version of a set of packages
*
* This is ideally the default branch version, or failing that it will return the package with the highest version
*
* @template T of PackageInterface
* @param array<T> $packages
* @return ($packages is non-empty-array<T> ? T : T|null)
*/
public static function getMostCurrentVersion(array $packages): ?PackageInterface
{
return array_reduce($packages, function ($carry, $pkg) {
if ($carry === null) {
return $pkg;
}

if ($pkg->isDefaultBranch()) {
return $pkg;
}

if (!$carry->isDefaultBranch() && version_compare($carry->getVersion(), $pkg->getVersion(), '<')) {
return $pkg;
}

return $carry;
});
}

/**
* Sorts packages by name
*
Expand Down

0 comments on commit e59812c

Please sign in to comment.