Skip to content

Commit

Permalink
Add release date to show command, and also release date for latest pa…
Browse files Browse the repository at this point in the history
…ckage in --latest/outdated mode, fixes #11737
  • Loading branch information
Seldaek committed Dec 19, 2023
1 parent e0f7527 commit 0c17b82
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 39 deletions.
69 changes: 57 additions & 12 deletions src/Composer/Command/ShowCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
if (isset($packages[$type])) {
ksort($packages[$type]);

$nameLength = $versionLength = $latestLength = 0;
$nameLength = $versionLength = $latestLength = $releaseDateLength = 0;

if ($showLatest && $showVersion) {
foreach ($packages[$type] as $package) {
Expand Down Expand Up @@ -508,6 +508,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int
if ($writeLatest && $latestPackage) {
$packageViewData['latest'] = $latestPackage->getFullPrettyVersion();
$packageViewData['latest-status'] = $this->getUpdateStatus($latestPackage, $package);
if ($latestPackage->getReleaseDate() !== null) {
$packageViewData['latest-released'] = $this->getRelativeTime($latestPackage->getReleaseDate());
$releaseDateLength = max($releaseDateLength, strlen($packageViewData['latest-released']));
} else {
$packageViewData['latest-released'] = '';
}
$latestLength = max($latestLength, strlen($packageViewData['latest']));
} elseif ($writeLatest) {
$packageViewData['latest'] = '[none matched]';
Expand Down Expand Up @@ -552,6 +558,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
'nameLength' => $nameLength,
'versionLength' => $versionLength,
'latestLength' => $latestLength,
'releaseDateLength' => $releaseDateLength,
'writeLatest' => $writeLatest,
];
if ($input->getOption('strict') && $hasOutdatedPackages) {
Expand Down Expand Up @@ -588,11 +595,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$nameLength = $viewMetaData[$type]['nameLength'];
$versionLength = $viewMetaData[$type]['versionLength'];
$latestLength = $viewMetaData[$type]['latestLength'];
$releaseDateLength = $viewMetaData[$type]['releaseDateLength'];
$writeLatest = $viewMetaData[$type]['writeLatest'];

$versionFits = $nameLength + $versionLength + 3 <= $width;
$latestFits = $nameLength + $versionLength + $latestLength + 3 <= $width;
$descriptionFits = $nameLength + $versionLength + $latestLength + 24 <= $width;
$releaseDateFits = $nameLength + $versionLength + $latestLength + $releaseDateLength + 3 <= $width;
$descriptionFits = $nameLength + $versionLength + $latestLength + $releaseDateLength + 24 <= $width;

if ($latestFits && !$io->isDecorated()) {
$latestLength += 2;
Expand Down Expand Up @@ -620,22 +629,22 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$io->writeError('');
$io->writeError('<info>Direct dependencies required in composer.json:</>');
if (\count($directDeps) > 0) {
$this->printPackages($io, $directDeps, $indent, $writeVersion && $versionFits, $latestFits, $writeDescription && $descriptionFits, $width, $versionLength, $nameLength, $latestLength);
$this->printPackages($io, $directDeps, $indent, $writeVersion && $versionFits, $latestFits, $writeDescription && $descriptionFits, $width, $versionLength, $nameLength, $latestLength, $releaseDateFits, $releaseDateLength);
} else {
$io->writeError('Everything up to date');
}
$io->writeError('');
$io->writeError('<info>Transitive dependencies not required in composer.json:</>');
if (\count($transitiveDeps) > 0) {
$this->printPackages($io, $transitiveDeps, $indent, $writeVersion && $versionFits, $latestFits, $writeDescription && $descriptionFits, $width, $versionLength, $nameLength, $latestLength);
$this->printPackages($io, $transitiveDeps, $indent, $writeVersion && $versionFits, $latestFits, $writeDescription && $descriptionFits, $width, $versionLength, $nameLength, $latestLength, $releaseDateFits, $releaseDateLength);
} else {
$io->writeError('Everything up to date');
}
} else {
if ($writeLatest && \count($packages) === 0) {
$io->writeError('All your direct dependencies are up to date');
} else {
$this->printPackages($io, $packages, $indent, $writeVersion && $versionFits, $writeLatest && $latestFits, $writeDescription && $descriptionFits, $width, $versionLength, $nameLength, $latestLength);
$this->printPackages($io, $packages, $indent, $writeVersion && $versionFits, $writeLatest && $latestFits, $writeDescription && $descriptionFits, $width, $versionLength, $nameLength, $latestLength, $writeLatest && $releaseDateFits, $releaseDateLength);
}
}

Expand All @@ -651,11 +660,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int
/**
* @param array<array{name: string, direct-dependency?: bool, version?: string, latest?: string, latest-status?: string, description?: string|null, path?: string|null, source?: string|null, homepage?: string|null, warning?: string, abandoned?: bool|string}> $packages
*/
private function printPackages(IOInterface $io, array $packages, string $indent, bool $writeVersion, bool $writeLatest, bool $writeDescription, int $width, int $versionLength, int $nameLength, int $latestLength): void
private function printPackages(IOInterface $io, array $packages, string $indent, bool $writeVersion, bool $writeLatest, bool $writeDescription, int $width, int $versionLength, int $nameLength, int $latestLength, bool $writeReleaseDate, int $releaseDateLength): void
{
$padName = $writeVersion || $writeLatest || $writeDescription;
$padVersion = $writeLatest || $writeDescription;
$padLatest = $writeDescription;
$padName = $writeVersion || $writeLatest || $writeReleaseDate || $writeDescription;
$padVersion = $writeLatest || $writeReleaseDate || $writeDescription;
$padLatest = $writeDescription || $writeReleaseDate;
$padReleaseDate = $writeDescription;
foreach ($packages as $package) {
$link = $package['source'] ?? $package['homepage'] ?? '';
if ($link !== '') {
Expand All @@ -674,10 +684,13 @@ private function printPackages(IOInterface $io, array $packages, string $indent,
$latestVersion = str_replace(['up-to-date', 'semver-safe-update', 'update-possible'], ['=', '!', '~'], $updateStatus) . ' ' . $latestVersion;
}
$io->write(' <' . $style . '>' . str_pad($latestVersion, ($padLatest ? $latestLength : 0), ' ') . '</' . $style . '>', false);
if ($writeReleaseDate && isset($package['latest-released'])) {
$io->write(' '.str_pad($package['latest-released'], ($padReleaseDate ? $releaseDateLength : 0), ' '), false);
}
}
if (isset($package['description']) && $writeDescription) {
$description = strtok($package['description'], "\r\n");
$remaining = $width - $nameLength - $versionLength - 4;
$remaining = $width - $nameLength - $versionLength - $releaseDateLength - 4;
if ($writeLatest) {
$remaining -= $latestLength;
}
Expand Down Expand Up @@ -806,23 +819,29 @@ protected function printPackageInfo(CompletePackageInterface $package, array $ve
*/
protected function printMeta(CompletePackageInterface $package, array $versions, InstalledRepository $installedRepo, ?PackageInterface $latestPackage = null): void
{
$isInstalledPackage = !PlatformRepository::isPlatformPackage($package->getName()) && $installedRepo->hasPackage($package);

$io = $this->getIO();
$io->write('<info>name</info> : ' . $package->getPrettyName());
$io->write('<info>descrip.</info> : ' . $package->getDescription());
$io->write('<info>keywords</info> : ' . implode(', ', $package->getKeywords() ?: []));
$this->printVersions($package, $versions, $installedRepo);
if ($latestPackage) {
$style = $this->getVersionStyle($latestPackage, $package);
$io->write('<info>latest</info> : <'.$style.'>' . $latestPackage->getPrettyVersion() . '</'.$style.'>');
$releasedTime = $latestPackage->getReleaseDate() === null ? '' : ' released '.$this->getRelativeTime($latestPackage->getReleaseDate());
$io->write('<info>latest</info> : <'.$style.'>' . $latestPackage->getPrettyVersion() . '</'.$style.'>' . $releasedTime);
} else {
$latestPackage = $package;
}
$io->write('<info>type</info> : ' . $package->getType());
if ($isInstalledPackage && $package->getReleaseDate() !== null) {
$io->write('<info>released</info> : ' . $this->getRelativeTime($package->getReleaseDate()));
}
$this->printLicenses($package);
$io->write('<info>homepage</info> : ' . $package->getHomepage());
$io->write('<info>source</info> : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getSourceType(), $package->getSourceUrl(), $package->getSourceReference()));
$io->write('<info>dist</info> : ' . sprintf('[%s] <comment>%s</comment> %s', $package->getDistType(), $package->getDistUrl(), $package->getDistReference()));
if (!PlatformRepository::isPlatformPackage($package->getName()) && $installedRepo->hasPackage($package)) {
if ($isInstalledPackage) {
$path = $this->requireComposer()->getInstallationManager()->getInstallPath($package);
if (is_string($path)) {
$io->write('<info>path</info> : ' . realpath($path));
Expand Down Expand Up @@ -993,6 +1012,10 @@ protected function printPackageInfoAsJson(CompletePackageInterface $package, arr
} else {
$json['path'] = null;
}

if ($package->getReleaseDate() !== null) {
$json['released'] = $package->getReleaseDate()->format(DATE_ATOM);
}
}

if ($latestPackage instanceof CompletePackageInterface && $latestPackage->isAbandoned()) {
Expand Down Expand Up @@ -1447,4 +1470,26 @@ private function getRepositorySet(Composer $composer): RepositorySet

return $this->repositorySet;
}

private function getRelativeTime(\DateTimeInterface $releaseDate): string
{
if ($releaseDate->format('Ymd') === date('Ymd')) {
return 'today';
}

$diff = $releaseDate->diff(new \DateTimeImmutable());
if ($diff->days < 7) {
return 'this week';
}

if ($diff->days < 14) {
return 'last week';
}

if ($diff->days < 30) {
return floor($diff->days / 7) . ' weeks ago';
}

return $releaseDate->format('Y-m-d');
}
}
55 changes: 28 additions & 27 deletions tests/Composer/Test/Command/ShowCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,19 @@ public function testShow(array $command, string $expected, array $requires = [])
'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'],
['name' => 'outdated/major', 'description' => 'outdated/major v1.0.0 description', 'version' => '1.0.0', 'time' => '2019-01-02 01:00:00'],
['name' => 'outdated/major', 'description' => 'outdated/major v1.0.1 description', 'version' => '1.0.1', 'time' => '2019-01-02 01:00:00'],
['name' => 'outdated/major', 'description' => 'outdated/major v1.1.0 description', 'version' => '1.1.0', 'time' => '2019-01-02 01:00:00'],
['name' => 'outdated/major', 'description' => 'outdated/major v1.1.1 description', 'version' => '1.1.1', 'time' => '2019-01-02 01:00:00'],
['name' => 'outdated/major', 'description' => 'outdated/major v2.0.0 description', 'version' => '2.0.0', 'time' => '2019-01-02 01:00:00'],

['name' => 'outdated/minor', 'description' => 'outdated/minor v1.0.0 description', 'version' => '1.0.0', 'time' => date('Y-m-d H:i:s', strtotime('-2 weeks'))],
['name' => 'outdated/minor', 'description' => 'outdated/minor v1.0.1 description', 'version' => '1.0.1', 'time' => date('Y-m-d H:i:s', strtotime('-2 weeks'))],
['name' => 'outdated/minor', 'description' => 'outdated/minor v1.1.0 description', 'version' => '1.1.0', 'time' => date('Y-m-d H:i:s', strtotime('-2 weeks'))],
['name' => 'outdated/minor', 'description' => 'outdated/minor v1.1.1 description', 'version' => '1.1.1', 'time' => date('Y-m-d H:i:s', strtotime('-2 weeks'))],

['name' => 'outdated/patch', 'description' => 'outdated/patch v1.0.0 description', 'version' => '1.0.0', 'time' => date('Y-m-d H:i:s')],
['name' => 'outdated/patch', 'description' => 'outdated/patch v1.0.1 description', 'version' => '1.0.1', 'time' => date('Y-m-d H:i:s')],
],
],
],
Expand Down Expand Up @@ -107,17 +107,17 @@ public static function provideShow(): \Generator
Everything up to date
Transitive dependencies not required in composer.json:
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>',
outdated/major 1.0.0 ~ 2.0.0 2019-01-02 '.'
outdated/minor 1.0.0 <highlight>! 1.1.1</highlight> 2 weeks ago
outdated/patch 1.0.0 <highlight>! 1.0.1</highlight> today',
];

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',
outdated/major 1.0.0 ~ 2.0.0 2019-01-02',
[
'vendor/package' => '*',
'outdated/major' => '*',
Expand All @@ -142,7 +142,7 @@ public static function provideShow(): \Generator
Everything up to date
Transitive dependencies not required in composer.json:
outdated/major 1.0.0 ~ 2.0.0',
outdated/major 1.0.0 ~ 2.0.0 2019-01-02',
];

yield 'outdated deps with --minor-only only shows minor updates' => [
Expand All @@ -152,11 +152,11 @@ public static function provideShow(): \Generator
~ major release available - update possible
Direct dependencies required in composer.json:
outdated/minor 1.0.0 <highlight>! 1.1.1</highlight>
outdated/minor 1.0.0 <highlight>! 1.1.1</highlight> 2 weeks ago
Transitive dependencies not required in composer.json:
outdated/major 1.0.0 <highlight>! 1.1.1</highlight>
outdated/patch 1.0.0 <highlight>! 1.0.1</highlight>',
outdated/major 1.0.0 <highlight>! 1.1.1</highlight> 2019-01-02 '.'
outdated/patch 1.0.0 <highlight>! 1.0.1</highlight> today',
['outdated/minor' => '*'],
];

Expand All @@ -170,9 +170,9 @@ public static function provideShow(): \Generator
Everything up to date
Transitive dependencies not required in composer.json:
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>',
outdated/major 1.0.0 <highlight>! 1.0.1</highlight> 2019-01-02 '.'
outdated/minor 1.0.0 <highlight>! 1.0.1</highlight> 2 weeks ago
outdated/patch 1.0.0 <highlight>! 1.0.1</highlight> today',
];
}

Expand Down Expand Up @@ -351,7 +351,7 @@ public function testOutdatedWithZeroMajor(): void
'Legend:
! patch or minor release available - update recommended
~ major release available - update possible
zero/minor 0.1.0 <highlight>! 0.1.2 </highlight>
zero/minor 0.1.0 <highlight>! 0.1.2 </highlight> '.'
zero/patch 0.1.2 <highlight>! 0.1.2.1</highlight>', trim($appTester->getDisplay(true)));

$appTester = $this->getApplicationTester();
Expand All @@ -360,7 +360,7 @@ public function testOutdatedWithZeroMajor(): void
'Legend:
! patch or minor release available - update recommended
~ major release available - update possible
zero/major 0.1.0 ~ 0.2.0
zero/major 0.1.0 ~ 0.2.0 '.'
zerozero/major 0.0.1 ~ 0.0.2', trim($appTester->getDisplay(true)));
}

Expand Down Expand Up @@ -533,7 +533,7 @@ public function testSelfAndPackageCombination(): void

public function testSelf(): void
{
$this->initTempComposer(['name' => 'vendor/package']);
$this->initTempComposer(['name' => 'vendor/package', 'time' => date('Y-m-d')]);

$appTester = $this->getApplicationTester();
$appTester->run(['command' => 'show', '--self' => true]);
Expand All @@ -543,6 +543,7 @@ public function testSelf(): void
'keywords' => '',
'versions' => '* 1.0.0+no-version-set',
'type' => 'library',
'released' => 'today',
'homepage' => '',
'source' => '[] ',
'dist' => '[] ',
Expand Down

0 comments on commit 0c17b82

Please sign in to comment.