Skip to content

Commit

Permalink
Tweak relative date logic and add --sort-by-age flag
Browse files Browse the repository at this point in the history
  • Loading branch information
Seldaek committed Dec 20, 2023
1 parent ee997f8 commit b235f93
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 48 deletions.
2 changes: 2 additions & 0 deletions doc/03-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,7 @@ php composer.phar show monolog/monolog 1.0.2
* **--major-only (-M):** Use with --latest or --outdated. Only shows packages that have major SemVer-compatible updates.
* **--minor-only (-m):** Use with --latest or --outdated. Only shows packages that have minor SemVer-compatible updates.
* **--patch-only:** Use with --latest or --outdated. Only shows packages that have patch-level SemVer-compatible updates.
* **--sort-by-age (-A):** Sort by and display current release age. Use with the --latest or --outdated option.
* **--direct (-D):** Restricts the list of packages to your direct dependencies.
* **--strict:** Return a non-zero exit code when there are outdated packages.
* **--format (-f):** Lets you pick between text (default) or json output format.
Expand Down Expand Up @@ -589,6 +590,7 @@ The color coding is as such:
* **--major-only (-M):** Only shows packages that have major SemVer-compatible updates.
* **--minor-only (-m):** Only shows packages that have minor SemVer-compatible updates.
* **--patch-only (-p):** Only shows packages that have patch-level SemVer-compatible updates.
* **--sort-by-age (-A):** Sort by and display current release age.
* **--format (-f):** Lets you pick between text (default) or json output format.
* **--no-dev:** Do not show outdated dev dependencies.
* **--locked:** Shows updates for packages from the lock file, regardless of what is currently in vendor dir.
Expand Down
4 changes: 4 additions & 0 deletions src/Composer/Command/OutdatedCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ protected function configure(): void
new InputOption('major-only', 'M', InputOption::VALUE_NONE, 'Show only packages that have major SemVer-compatible updates.'),
new InputOption('minor-only', 'm', InputOption::VALUE_NONE, 'Show only packages that have minor SemVer-compatible updates.'),
new InputOption('patch-only', 'p', InputOption::VALUE_NONE, 'Show only packages that have patch SemVer-compatible updates.'),
new InputOption('sort-by-age', 'A', InputOption::VALUE_NONE, 'Sort by and display current release age.'),
new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the output: text or json', 'text', ['json', 'text']),
new InputOption('ignore', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Ignore specified package(s). Use it if you don\'t want to be informed about new versions of some packages.', null, $this->suggestInstalledPackage(false)),
new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables search in require-dev packages.'),
Expand Down Expand Up @@ -97,6 +98,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int
if ($input->getOption('no-dev')) {
$args['--no-dev'] = true;
}
if ($input->getOption('sort-by-age')) {
$args['--sort-by-age'] = true;
}
$args['--ignore-platform-req'] = $input->getOption('ignore-platform-req');
if ($input->getOption('ignore-platform-reqs')) {
$args['--ignore-platform-reqs'] = true;
Expand Down
57 changes: 40 additions & 17 deletions src/Composer/Command/ShowCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ protected function configure()
new InputOption('major-only', 'M', InputOption::VALUE_NONE, 'Show only packages that have major SemVer-compatible updates. Use with the --latest or --outdated option.'),
new InputOption('minor-only', 'm', InputOption::VALUE_NONE, 'Show only packages that have minor SemVer-compatible updates. Use with the --latest or --outdated option.'),
new InputOption('patch-only', null, InputOption::VALUE_NONE, 'Show only packages that have patch SemVer-compatible updates. Use with the --latest or --outdated option.'),
new InputOption('sort-by-age', 'A', InputOption::VALUE_NONE, 'Sort by and display current release age. Use with the --latest or --outdated option.'),
new InputOption('direct', 'D', InputOption::VALUE_NONE, 'Shows only packages that are directly required by the root package'),
new InputOption('strict', null, InputOption::VALUE_NONE, 'Return a non-zero exit code when there are outdated packages'),
new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the output: text or json', 'text', ['json', 'text']),
Expand Down Expand Up @@ -469,9 +470,20 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$writeVersion = !$input->getOption('name-only') && !$input->getOption('path') && $showVersion;
$writeLatest = $writeVersion && $showLatest;
$writeDescription = !$input->getOption('name-only') && !$input->getOption('path');
$writeReleaseDate = $writeLatest && $input->getOption('sort-by-age');

$hasOutdatedPackages = false;

if ($input->getOption('sort-by-age')) {
usort($packages[$type], function ($a, $b) {
if (is_object($a) && is_object($b)) {
return $a->getReleaseDate() <=> $b->getReleaseDate();
}

return 0;
});
}

$viewData[$type] = [];
foreach ($packages[$type] as $package) {
$packageViewData = [];
Expand Down Expand Up @@ -505,15 +517,20 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$packageViewData['version'] = $package->getFullPrettyVersion();
$versionLength = max($versionLength, strlen($package->getFullPrettyVersion()));
}
if ($writeReleaseDate) {
if ($package->getReleaseDate() !== null) {
$packageViewData['release-age'] = str_replace(' ago', ' old', $this->getRelativeTime($package->getReleaseDate()));
if (!str_contains($packageViewData['release-age'], ' old')) {
$packageViewData['release-age'] = 'from '.$packageViewData['release-age'];
}
$releaseDateLength = max($releaseDateLength, strlen($packageViewData['release-age']));
} else {
$packageViewData['release-age'] = '';
}
}
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 @@ -560,6 +577,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
'latestLength' => $latestLength,
'releaseDateLength' => $releaseDateLength,
'writeLatest' => $writeLatest,
'writeReleaseDate' => $writeReleaseDate,
];
if ($input->getOption('strict') && $hasOutdatedPackages) {
$exitCode = 1;
Expand Down Expand Up @@ -597,6 +615,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$latestLength = $viewMetaData[$type]['latestLength'];
$releaseDateLength = $viewMetaData[$type]['releaseDateLength'];
$writeLatest = $viewMetaData[$type]['writeLatest'];
$writeReleaseDate = $viewMetaData[$type]['writeReleaseDate'];

$versionFits = $nameLength + $versionLength + 3 <= $width;
$latestFits = $nameLength + $versionLength + $latestLength + 3 <= $width;
Expand Down Expand Up @@ -629,22 +648,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, $releaseDateFits, $releaseDateLength);
$this->printPackages($io, $directDeps, $indent, $writeVersion && $versionFits, $latestFits, $writeDescription && $descriptionFits, $width, $versionLength, $nameLength, $latestLength, $writeReleaseDate && $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, $releaseDateFits, $releaseDateLength);
$this->printPackages($io, $transitiveDeps, $indent, $writeVersion && $versionFits, $latestFits, $writeDescription && $descriptionFits, $width, $versionLength, $nameLength, $latestLength, $writeReleaseDate && $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, $writeLatest && $releaseDateFits, $releaseDateLength);
$this->printPackages($io, $packages, $indent, $writeVersion && $versionFits, $writeLatest && $latestFits, $writeDescription && $descriptionFits, $width, $versionLength, $nameLength, $latestLength, $writeReleaseDate && $releaseDateFits, $releaseDateLength);
}
}

Expand Down Expand Up @@ -684,8 +703,8 @@ 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 ($writeReleaseDate && isset($package['release-age'])) {
$io->write(' '.str_pad($package['release-age'], ($padReleaseDate ? $releaseDateLength : 0), ' '), false);
}
}
if (isset($package['description']) && $writeDescription) {
Expand Down Expand Up @@ -826,17 +845,17 @@ protected function printMeta(CompletePackageInterface $package, array $versions,
$io->write('<info>descrip.</info> : ' . $package->getDescription());
$io->write('<info>keywords</info> : ' . implode(', ', $package->getKeywords() ?: []));
$this->printVersions($package, $versions, $installedRepo);
if ($isInstalledPackage && $package->getReleaseDate() !== null) {
$io->write('<info>released</info> : ' . $package->getReleaseDate()->format('Y-m-d') . ', ' . $this->getRelativeTime($package->getReleaseDate()));
}
if ($latestPackage) {
$style = $this->getVersionStyle($latestPackage, $package);
$releasedTime = $latestPackage->getReleaseDate() === null ? '' : ' released '.$this->getRelativeTime($latestPackage->getReleaseDate());
$releasedTime = $latestPackage->getReleaseDate() === null ? '' : ' released ' . $latestPackage->getReleaseDate()->format('Y-m-d') . ', ' . $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()));
Expand Down Expand Up @@ -1486,10 +1505,14 @@ private function getRelativeTime(\DateTimeInterface $releaseDate): string
return 'last week';
}

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

return $releaseDate->format('Y-m-d');
if ($diff->y < 1) {
return $diff->m . ' month' . ($diff->m > 1 ? 's' : '') . ' ago';
}

return $diff->y . ' year' . ($diff->y > 1 ? 's' : '') . ' ago';
}
}

0 comments on commit b235f93

Please sign in to comment.