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';
|
$hint .= ', try using --platform (-p) to show platform packages';
|
||||||
}
|
}
|
||||||
if (!$input->getOption('all')) {
|
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.'.');
|
throw new \InvalidArgumentException('Package "' . $packageFilter . '" not found'.$hint.'.');
|
||||||
|
@ -374,7 +374,7 @@ EOT
|
||||||
$packageFilterRegex = '{^'.str_replace('\\*', '.*?', preg_quote($packageFilter)).'$}i';
|
$packageFilterRegex = '{^'.str_replace('\\*', '.*?', preg_quote($packageFilter)).'$}i';
|
||||||
}
|
}
|
||||||
|
|
||||||
$packageListFilter = array();
|
$packageListFilter = null;
|
||||||
if ($input->getOption('direct')) {
|
if ($input->getOption('direct')) {
|
||||||
$packageListFilter = $this->getRootRequires();
|
$packageListFilter = $this->getRootRequires();
|
||||||
}
|
}
|
||||||
|
@ -384,7 +384,7 @@ EOT
|
||||||
$input->setOption('path', false);
|
$input->setOption('path', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($repos->getRepositories() as $repo) {
|
foreach (RepositoryUtils::flattenRepositories($repos) as $repo) {
|
||||||
if ($repo === $platformRepo) {
|
if ($repo === $platformRepo) {
|
||||||
$type = 'platform';
|
$type = 'platform';
|
||||||
} elseif ($lockedRepo !== null && $repo === $lockedRepo) {
|
} elseif ($lockedRepo !== null && $repo === $lockedRepo) {
|
||||||
|
@ -408,7 +408,7 @@ EOT
|
||||||
$package = $package->getAliasOf();
|
$package = $package->getAliasOf();
|
||||||
}
|
}
|
||||||
if (!$packageFilterRegex || Preg::isMatch($packageFilterRegex, $package->getName())) {
|
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;
|
$packages[$type][$package->getName()] = $package;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -535,6 +535,7 @@ EOT
|
||||||
'nameLength' => $nameLength,
|
'nameLength' => $nameLength,
|
||||||
'versionLength' => $versionLength,
|
'versionLength' => $versionLength,
|
||||||
'latestLength' => $latestLength,
|
'latestLength' => $latestLength,
|
||||||
|
'writeLatest' => $writeLatest,
|
||||||
);
|
);
|
||||||
if ($input->getOption('strict') && $hasOutdatedPackages) {
|
if ($input->getOption('strict') && $hasOutdatedPackages) {
|
||||||
$exitCode = 1;
|
$exitCode = 1;
|
||||||
|
@ -570,12 +571,13 @@ EOT
|
||||||
$nameLength = $viewMetaData[$type]['nameLength'];
|
$nameLength = $viewMetaData[$type]['nameLength'];
|
||||||
$versionLength = $viewMetaData[$type]['versionLength'];
|
$versionLength = $viewMetaData[$type]['versionLength'];
|
||||||
$latestLength = $viewMetaData[$type]['latestLength'];
|
$latestLength = $viewMetaData[$type]['latestLength'];
|
||||||
|
$writeLatest = $viewMetaData[$type]['writeLatest'];
|
||||||
|
|
||||||
$writeVersion = $nameLength + $versionLength + 3 <= $width;
|
$versionFits = $nameLength + $versionLength + 3 <= $width;
|
||||||
$writeLatest = $nameLength + $versionLength + $latestLength + 3 <= $width;
|
$latestFits = $nameLength + $versionLength + $latestLength + 3 <= $width;
|
||||||
$writeDescription = $nameLength + $versionLength + $latestLength + 24 <= $width;
|
$descriptionFits = $nameLength + $versionLength + $latestLength + 24 <= $width;
|
||||||
|
|
||||||
if ($writeLatest && !$io->isDecorated()) {
|
if ($latestFits && !$io->isDecorated()) {
|
||||||
$latestLength += 2;
|
$latestLength += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -601,19 +603,19 @@ EOT
|
||||||
$io->write('');
|
$io->write('');
|
||||||
$io->write('<info>Direct dependencies:</>');
|
$io->write('<info>Direct dependencies:</>');
|
||||||
if (\count($directDeps) > 0) {
|
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 {
|
} else {
|
||||||
$io->write('Everything up to date');
|
$io->write('Everything up to date');
|
||||||
}
|
}
|
||||||
$io->write('');
|
$io->write('');
|
||||||
$io->write('<info>Transitive dependencies:</>');
|
$io->write('<info>Transitive dependencies:</>');
|
||||||
if (\count($transitiveDeps) > 0) {
|
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 {
|
} else {
|
||||||
$io->write('Everything up to date');
|
$io->write('Everything up to date');
|
||||||
}
|
}
|
||||||
} else {
|
} 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) {
|
if ($showAllTypes) {
|
||||||
|
|
|
@ -49,4 +49,30 @@ class RepositoryUtils
|
||||||
|
|
||||||
return $bucket;
|
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