Show/outdated command fixes (#11000)
* Fix show command showing the split of direct/transitive deps from outdated, fixes #10999 * Fix a few minor edge cases in show command, add tests for show and outdated commandspull/11003/head
parent
9b6d27f810
commit
f95471f221
|
@ -291,7 +291,7 @@ EOT
|
|||
$hint .= ', try using --platform (-p) to show platform packages';
|
||||
}
|
||||
if (!$input->getOption('all')) {
|
||||
$hint .= ', try using --all (-a) to show all available packages';
|
||||
$hint .= ', try using --available (-a) to show all available packages';
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException('Package "' . $packageFilter . '" not found'.$hint.'.');
|
||||
|
@ -374,7 +374,7 @@ EOT
|
|||
$packageFilterRegex = '{^'.str_replace('\\*', '.*?', preg_quote($packageFilter)).'$}i';
|
||||
}
|
||||
|
||||
$packageListFilter = array();
|
||||
$packageListFilter = null;
|
||||
if ($input->getOption('direct')) {
|
||||
$packageListFilter = $this->getRootRequires();
|
||||
}
|
||||
|
@ -384,7 +384,7 @@ EOT
|
|||
$input->setOption('path', false);
|
||||
}
|
||||
|
||||
foreach ($repos->getRepositories() as $repo) {
|
||||
foreach (RepositoryUtils::flattenRepositories($repos) as $repo) {
|
||||
if ($repo === $platformRepo) {
|
||||
$type = 'platform';
|
||||
} elseif ($lockedRepo !== null && $repo === $lockedRepo) {
|
||||
|
@ -408,7 +408,7 @@ EOT
|
|||
$package = $package->getAliasOf();
|
||||
}
|
||||
if (!$packageFilterRegex || Preg::isMatch($packageFilterRegex, $package->getName())) {
|
||||
if (!$packageListFilter || in_array($package->getName(), $packageListFilter, true)) {
|
||||
if (null === $packageListFilter || in_array($package->getName(), $packageListFilter, true)) {
|
||||
$packages[$type][$package->getName()] = $package;
|
||||
}
|
||||
}
|
||||
|
@ -535,6 +535,7 @@ EOT
|
|||
'nameLength' => $nameLength,
|
||||
'versionLength' => $versionLength,
|
||||
'latestLength' => $latestLength,
|
||||
'writeLatest' => $writeLatest,
|
||||
);
|
||||
if ($input->getOption('strict') && $hasOutdatedPackages) {
|
||||
$exitCode = 1;
|
||||
|
@ -570,12 +571,13 @@ EOT
|
|||
$nameLength = $viewMetaData[$type]['nameLength'];
|
||||
$versionLength = $viewMetaData[$type]['versionLength'];
|
||||
$latestLength = $viewMetaData[$type]['latestLength'];
|
||||
$writeLatest = $viewMetaData[$type]['writeLatest'];
|
||||
|
||||
$writeVersion = $nameLength + $versionLength + 3 <= $width;
|
||||
$writeLatest = $nameLength + $versionLength + $latestLength + 3 <= $width;
|
||||
$writeDescription = $nameLength + $versionLength + $latestLength + 24 <= $width;
|
||||
$versionFits = $nameLength + $versionLength + 3 <= $width;
|
||||
$latestFits = $nameLength + $versionLength + $latestLength + 3 <= $width;
|
||||
$descriptionFits = $nameLength + $versionLength + $latestLength + 24 <= $width;
|
||||
|
||||
if ($writeLatest && !$io->isDecorated()) {
|
||||
if ($latestFits && !$io->isDecorated()) {
|
||||
$latestLength += 2;
|
||||
}
|
||||
|
||||
|
@ -601,19 +603,19 @@ EOT
|
|||
$io->write('');
|
||||
$io->write('<info>Direct dependencies:</>');
|
||||
if (\count($directDeps) > 0) {
|
||||
$this->printPackages($io, $directDeps, $indent, $writeVersion, $writeLatest, $writeDescription, $width, $versionLength, $nameLength, $latestLength);
|
||||
$this->printPackages($io, $directDeps, $indent, $versionFits, $latestFits, $descriptionFits, $width, $versionLength, $nameLength, $latestLength);
|
||||
} else {
|
||||
$io->write('Everything up to date');
|
||||
}
|
||||
$io->write('');
|
||||
$io->write('<info>Transitive dependencies:</>');
|
||||
if (\count($transitiveDeps) > 0) {
|
||||
$this->printPackages($io, $transitiveDeps, $indent, $writeVersion, $writeLatest, $writeDescription, $width, $versionLength, $nameLength, $latestLength);
|
||||
$this->printPackages($io, $transitiveDeps, $indent, $versionFits, $latestFits, $descriptionFits, $width, $versionLength, $nameLength, $latestLength);
|
||||
} else {
|
||||
$io->write('Everything up to date');
|
||||
}
|
||||
} else {
|
||||
$this->printPackages($io, $packages, $indent, $writeVersion, $writeLatest, $writeDescription, $width, $versionLength, $nameLength, $latestLength);
|
||||
$this->printPackages($io, $packages, $indent, $versionFits, $latestFits, $descriptionFits, $width, $versionLength, $nameLength, $latestLength);
|
||||
}
|
||||
|
||||
if ($showAllTypes) {
|
||||
|
|
|
@ -49,4 +49,30 @@ class RepositoryUtils
|
|||
|
||||
return $bucket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwraps CompositeRepository, InstalledRepository and optionally FilterRepository to get a flat array of pure repository instances
|
||||
*
|
||||
* @return RepositoryInterface[]
|
||||
*/
|
||||
public static function flattenRepositories(RepositoryInterface $repo, bool $unwrapFilterRepos = true): array
|
||||
{
|
||||
// unwrap filter repos
|
||||
if ($unwrapFilterRepos && $repo instanceof FilterRepository) {
|
||||
$repo = $repo->getRepository();
|
||||
}
|
||||
|
||||
if (!$repo instanceof CompositeRepository) {
|
||||
return [$repo];
|
||||
}
|
||||
|
||||
$repos = [];
|
||||
foreach ($repo->getRepositories() as $r) {
|
||||
foreach (self::flattenRepositories($r, $unwrapFilterRepos) as $r2) {
|
||||
$repos[] = $r2;
|
||||
}
|
||||
}
|
||||
|
||||
return $repos;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,236 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Test\Command;
|
||||
|
||||
use Composer\Pcre\Preg;
|
||||
use Composer\Pcre\Regex;
|
||||
use Composer\Repository\PlatformRepository;
|
||||
use Composer\Test\TestCase;
|
||||
|
||||
class ShowCommandTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideShow
|
||||
* @param array<mixed> $command
|
||||
* @param array<string, string> $requires
|
||||
*/
|
||||
public function testShow(array $command, string $expected, array $requires = []): void
|
||||
{
|
||||
$this->initTempComposer([
|
||||
'repositories' => [
|
||||
'packages' => [
|
||||
'type' => 'package',
|
||||
'package' => [
|
||||
['name' => 'vendor/package', 'description' => 'generic description', 'version' => '1.0.0'],
|
||||
|
||||
['name' => 'outdated/major', 'description' => 'outdated/major v1.0.0 description', 'version' => '1.0.0'],
|
||||
['name' => 'outdated/major', 'description' => 'outdated/major v1.0.1 description', 'version' => '1.0.1'],
|
||||
['name' => 'outdated/major', 'description' => 'outdated/major v1.1.0 description', 'version' => '1.1.0'],
|
||||
['name' => 'outdated/major', 'description' => 'outdated/major v1.1.1 description', 'version' => '1.1.1'],
|
||||
['name' => 'outdated/major', 'description' => 'outdated/major v2.0.0 description', 'version' => '2.0.0'],
|
||||
|
||||
['name' => 'outdated/minor', 'description' => 'outdated/minor v1.0.0 description', 'version' => '1.0.0'],
|
||||
['name' => 'outdated/minor', 'description' => 'outdated/minor v1.0.1 description', 'version' => '1.0.1'],
|
||||
['name' => 'outdated/minor', 'description' => 'outdated/minor v1.1.0 description', 'version' => '1.1.0'],
|
||||
['name' => 'outdated/minor', 'description' => 'outdated/minor v1.1.1 description', 'version' => '1.1.1'],
|
||||
|
||||
['name' => 'outdated/patch', 'description' => 'outdated/patch v1.0.0 description', 'version' => '1.0.0'],
|
||||
['name' => 'outdated/patch', 'description' => 'outdated/patch v1.0.1 description', 'version' => '1.0.1'],
|
||||
],
|
||||
],
|
||||
],
|
||||
'require' => $requires === [] ? new \stdClass : $requires,
|
||||
]);
|
||||
|
||||
$pkg = $this->getPackage('vendor/package', '1.0.0');
|
||||
$pkg->setDescription('description of installed package');
|
||||
|
||||
$this->createInstalledJson([
|
||||
$pkg,
|
||||
$this->getPackage('outdated/major', '1.0.0'),
|
||||
$this->getPackage('outdated/minor', '1.0.0'),
|
||||
$this->getPackage('outdated/patch', '1.0.0'),
|
||||
]);
|
||||
|
||||
$appTester = $this->getApplicationTester();
|
||||
$appTester->run(array_merge(['command' => 'show'], $command));
|
||||
self::assertSame(trim($expected), trim($appTester->getDisplay(true)));
|
||||
}
|
||||
|
||||
public function provideShow(): \Generator
|
||||
{
|
||||
yield 'default shows installed with version and description' => [
|
||||
[],
|
||||
'outdated/major 1.0.0
|
||||
outdated/minor 1.0.0
|
||||
outdated/patch 1.0.0
|
||||
vendor/package 1.0.0 description of installed package',
|
||||
];
|
||||
|
||||
yield 'with -a show available packages with description but no version' => [
|
||||
['-a' => true],
|
||||
'outdated/major outdated/major v2.0.0 description
|
||||
outdated/minor outdated/minor v1.1.1 description
|
||||
outdated/patch outdated/patch v1.0.1 description
|
||||
vendor/package generic description',
|
||||
];
|
||||
|
||||
yield 'show with --direct shows nothing if no deps' => [
|
||||
['--direct' => true],
|
||||
'',
|
||||
];
|
||||
|
||||
yield 'show with --direct shows only root deps' => [
|
||||
['--direct' => true],
|
||||
'outdated/major 1.0.0',
|
||||
['outdated/major' => '*'],
|
||||
];
|
||||
|
||||
yield 'outdated deps' => [
|
||||
['command' => 'outdated'],
|
||||
'Legend:
|
||||
! patch or minor release available - update recommended
|
||||
~ major release available - update possible
|
||||
|
||||
Direct dependencies:
|
||||
Everything up to date
|
||||
|
||||
Transitive dependencies:
|
||||
outdated/major 1.0.0 ~ 2.0.0
|
||||
outdated/minor 1.0.0 <highlight>! 1.1.1</highlight>
|
||||
outdated/patch 1.0.0 <highlight>! 1.0.1</highlight>',
|
||||
];
|
||||
|
||||
yield 'outdated deps with --direct only show direct deps with updated' => [
|
||||
['command' => 'outdated', '--direct' => true],
|
||||
'Legend:
|
||||
! patch or minor release available - update recommended
|
||||
~ major release available - update possible
|
||||
outdated/major 1.0.0 ~ 2.0.0',
|
||||
[
|
||||
'vendor/package' => '*',
|
||||
'outdated/major' => '*',
|
||||
],
|
||||
];
|
||||
|
||||
yield 'outdated deps with --major-only only shows major updates' => [
|
||||
['command' => 'outdated', '--major-only' => true],
|
||||
'Legend:
|
||||
! patch or minor release available - update recommended
|
||||
~ major release available - update possible
|
||||
|
||||
Direct dependencies:
|
||||
Everything up to date
|
||||
|
||||
Transitive dependencies:
|
||||
outdated/major 1.0.0 ~ 2.0.0',
|
||||
];
|
||||
|
||||
yield 'outdated deps with --minor-only only shows minor updates' => [
|
||||
['command' => 'outdated', '--minor-only' => true],
|
||||
'Legend:
|
||||
! patch or minor release available - update recommended
|
||||
~ major release available - update possible
|
||||
|
||||
Direct dependencies:
|
||||
outdated/minor 1.0.0 <highlight>! 1.1.1</highlight>
|
||||
|
||||
Transitive dependencies:
|
||||
outdated/major 1.0.0 <highlight>! 1.1.1</highlight>
|
||||
outdated/patch 1.0.0 <highlight>! 1.0.1</highlight>',
|
||||
['outdated/minor' => '*'],
|
||||
];
|
||||
|
||||
yield 'outdated deps with --patch-only only shows patch updates' => [
|
||||
['command' => 'outdated', '--patch-only' => true],
|
||||
'Legend:
|
||||
! patch or minor release available - update recommended
|
||||
~ major release available - update possible
|
||||
|
||||
Direct dependencies:
|
||||
Everything up to date
|
||||
|
||||
Transitive dependencies:
|
||||
outdated/major 1.0.0 <highlight>! 1.0.1</highlight>
|
||||
outdated/minor 1.0.0 <highlight>! 1.0.1</highlight>
|
||||
outdated/patch 1.0.0 <highlight>! 1.0.1</highlight>',
|
||||
];
|
||||
}
|
||||
|
||||
public function testShowPlatformOnlyShowsPlatformPackages(): void
|
||||
{
|
||||
$this->initTempComposer([
|
||||
'repositories' => [
|
||||
'packages' => [
|
||||
'type' => 'package',
|
||||
'package' => [
|
||||
['name' => 'vendor/package', 'description' => 'generic description', 'version' => '1.0.0'],
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->createInstalledJson([
|
||||
$this->getPackage('vendor/package', '1.0.0'),
|
||||
]);
|
||||
|
||||
$appTester = $this->getApplicationTester();
|
||||
$appTester->run(['command' => 'show', '-p' => true]);
|
||||
$output = trim($appTester->getDisplay(true));
|
||||
foreach (Regex::matchAll('{^(\w+)}m', $output)->matches as $m) {
|
||||
self::assertTrue(PlatformRepository::isPlatformPackage((string) $m[1]));
|
||||
}
|
||||
}
|
||||
|
||||
public function testShowAllShowsAllSections(): void
|
||||
{
|
||||
$this->initTempComposer([
|
||||
'repositories' => [
|
||||
'packages' => [
|
||||
'type' => 'package',
|
||||
'package' => [
|
||||
['name' => 'vendor/available', 'description' => 'generic description', 'version' => '1.0.0'],
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
$pkg = $this->getPackage('vendor/installed', '2.0.0');
|
||||
$pkg->setDescription('description of installed package');
|
||||
$this->createInstalledJson([
|
||||
$pkg,
|
||||
]);
|
||||
|
||||
$pkg = $this->getPackage('vendor/locked', '3.0.0');
|
||||
$pkg->setDescription('description of locked package');
|
||||
$this->createComposerLock([
|
||||
$pkg,
|
||||
]);
|
||||
|
||||
$appTester = $this->getApplicationTester();
|
||||
$appTester->run(['command' => 'show', '--all' => true]);
|
||||
$output = trim($appTester->getDisplay(true));
|
||||
$output = Preg::replace('{platform:(\n .*)+}', 'platform: wiped', $output);
|
||||
|
||||
self::assertSame('platform: wiped
|
||||
|
||||
locked:
|
||||
vendor/locked 3.0.0 description of locked package
|
||||
|
||||
available:
|
||||
vendor/available generic description
|
||||
|
||||
installed:
|
||||
vendor/installed 2.0.0 description of installed package', $output);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue