Merge remote-tracking branch 'xelaris/json-output-for-show-cmd'
commit
f5e026c6c5
|
@ -14,6 +14,7 @@ namespace Composer\Command;
|
|||
|
||||
use Composer\DependencyResolver\Pool;
|
||||
use Composer\DependencyResolver\DefaultPolicy;
|
||||
use Composer\Json\JsonFile;
|
||||
use Composer\Package\CompletePackageInterface;
|
||||
use Composer\Package\Version\VersionParser;
|
||||
use Composer\Package\BasePackage;
|
||||
|
@ -74,6 +75,7 @@ class ShowCommand extends BaseCommand
|
|||
new InputOption('minor-only', 'm', InputOption::VALUE_NONE, 'Show only packages that have minor SemVer-compatible updates. Use with the --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'),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
The show command displays detailed information about a package, or
|
||||
|
@ -114,6 +116,13 @@ EOT
|
|||
return 1;
|
||||
}
|
||||
|
||||
$format = $input->getOption('format');
|
||||
if (!in_array($format, array('text', 'json'))) {
|
||||
$io->writeError(sprintf('Unsupported format "%s". See help for supported formats.', $format));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// init repos
|
||||
$platformOverrides = array();
|
||||
if ($composer) {
|
||||
|
@ -163,6 +172,9 @@ EOT
|
|||
|
||||
// show single package or single version
|
||||
if (($packageFilter && false === strpos($packageFilter, '*')) || !empty($package)) {
|
||||
if ('json' === $format) {
|
||||
$io->writeError('Format "json" is only supported for package listings, falling back to format "text"');
|
||||
}
|
||||
if (empty($package)) {
|
||||
list($package, $versions) = $this->getPackage($installedRepo, $repos, $input->getArgument('package'), $input->getArgument('version'));
|
||||
|
||||
|
@ -205,6 +217,9 @@ EOT
|
|||
|
||||
// show tree view if requested
|
||||
if ($input->getOption('tree')) {
|
||||
if ('json' === $format) {
|
||||
$io->writeError('Format "json" is only supported for package listings, falling back to format "text"');
|
||||
}
|
||||
$rootRequires = $this->getRootRequires();
|
||||
foreach ($installedRepo->getPackages() as $package) {
|
||||
if (in_array($package->getName(), $rootRequires, true)) {
|
||||
|
@ -232,16 +247,31 @@ EOT
|
|||
$packageListFilter = $this->getRootRequires();
|
||||
}
|
||||
|
||||
list($width) = $this->getApplication()->getTerminalDimensions();
|
||||
if (null === $width) {
|
||||
// In case the width is not detected, we're probably running the command
|
||||
// outside of a real terminal, use space without a limit
|
||||
$width = PHP_INT_MAX;
|
||||
}
|
||||
if (Platform::isWindows()) {
|
||||
$width--;
|
||||
}
|
||||
|
||||
if ($input->getOption('path') && null === $composer) {
|
||||
$io->writeError('No composer.json found in the current directory, disabling "path" option');
|
||||
$input->setOption('path', false);
|
||||
}
|
||||
|
||||
foreach ($repos as $repo) {
|
||||
if ($repo === $platformRepo) {
|
||||
$type = '<info>platform</info>:';
|
||||
$type = 'platform';
|
||||
} elseif (
|
||||
$repo === $installedRepo
|
||||
|| ($installedRepo instanceof CompositeRepository && in_array($repo, $installedRepo->getRepositories(), true))
|
||||
) {
|
||||
$type = '<info>installed</info>:';
|
||||
$type = 'installed';
|
||||
} else {
|
||||
$type = '<comment>available</comment>:';
|
||||
$type = 'available';
|
||||
}
|
||||
if ($repo instanceof ComposerRepository && $repo->hasProviders()) {
|
||||
foreach ($repo->getProviderNames() as $name) {
|
||||
|
@ -270,11 +300,11 @@ EOT
|
|||
$showMinorOnly = $input->getOption('minor-only');
|
||||
$indent = $showAllTypes ? ' ' : '';
|
||||
$latestPackages = array();
|
||||
foreach (array('<info>platform</info>:' => true, '<comment>available</comment>:' => false, '<info>installed</info>:' => true) as $type => $showVersion) {
|
||||
$exitCode = 0;
|
||||
$viewData = array();
|
||||
$viewMetaData = array();
|
||||
foreach (array('platform' => true, 'available' => false, 'installed' => true) as $type => $showVersion) {
|
||||
if (isset($packages[$type])) {
|
||||
if ($showAllTypes) {
|
||||
$io->write($type);
|
||||
}
|
||||
ksort($packages[$type]);
|
||||
|
||||
$nameLength = $versionLength = $latestLength = 0;
|
||||
|
@ -298,101 +328,137 @@ EOT
|
|||
$nameLength = max($nameLength, strlen($package));
|
||||
}
|
||||
}
|
||||
list($width) = $this->getApplication()->getTerminalDimensions();
|
||||
if (null === $width) {
|
||||
// In case the width is not detected, we're probably running the command
|
||||
// outside of a real terminal, use space without a limit
|
||||
$width = PHP_INT_MAX;
|
||||
}
|
||||
if (Platform::isWindows()) {
|
||||
$width--;
|
||||
}
|
||||
|
||||
if ($input->getOption('path') && null === $composer) {
|
||||
$io->writeError('No composer.json found in the current directory, disabling "path" option');
|
||||
$input->setOption('path', false);
|
||||
}
|
||||
|
||||
$writePath = !$input->getOption('name-only') && $input->getOption('path');
|
||||
$writeVersion = !$input->getOption('name-only') && !$input->getOption('path') && $showVersion && ($nameLength + $versionLength + 3 <= $width);
|
||||
$writeLatest = $writeVersion && $showLatest && ($nameLength + $versionLength + $latestLength + 3 <= $width);
|
||||
$writeDescription = !$input->getOption('name-only') && !$input->getOption('path') && ($nameLength + $versionLength + $latestLength + 24 <= $width);
|
||||
if ($writeLatest && !$io->isDecorated()) {
|
||||
$latestLength += 2;
|
||||
}
|
||||
$writeVersion = !$input->getOption('name-only') && !$input->getOption('path') && $showVersion;
|
||||
$writeLatest = $writeVersion && $showLatest;
|
||||
$writeDescription = !$input->getOption('name-only') && !$input->getOption('path');
|
||||
|
||||
$hasOutdatedPackages = false;
|
||||
|
||||
$viewData[$type] = array();
|
||||
$viewMetaData[$type] = array(
|
||||
'nameLength' => $nameLength,
|
||||
'versionLength' => $versionLength,
|
||||
'latestLength' => $latestLength,
|
||||
);
|
||||
foreach ($packages[$type] as $package) {
|
||||
$packageViewData = array();
|
||||
if (is_object($package)) {
|
||||
$latestPackackage = null;
|
||||
$latestPackage = null;
|
||||
if ($showLatest && isset($latestPackages[$package->getPrettyName()])) {
|
||||
$latestPackackage = $latestPackages[$package->getPrettyName()];
|
||||
$latestPackage = $latestPackages[$package->getPrettyName()];
|
||||
}
|
||||
if ($input->getOption('outdated') && $latestPackackage && $latestPackackage->getFullPrettyVersion() === $package->getFullPrettyVersion() && !$latestPackackage->isAbandoned()) {
|
||||
if ($input->getOption('outdated') && $latestPackage && $latestPackage->getFullPrettyVersion() === $package->getFullPrettyVersion() && !$latestPackage->isAbandoned()) {
|
||||
continue;
|
||||
} elseif ($input->getOption('outdated')) {
|
||||
$hasOutdatedPackages = true;
|
||||
}
|
||||
|
||||
$io->write($indent . str_pad($package->getPrettyName(), $nameLength, ' '), false);
|
||||
|
||||
$packageViewData['name'] = $package->getPrettyName();
|
||||
if ($writeVersion) {
|
||||
$io->write(' ' . str_pad($package->getFullPrettyVersion(), $versionLength, ' '), false);
|
||||
$packageViewData['version'] = $package->getFullPrettyVersion();
|
||||
}
|
||||
|
||||
if ($writeLatest && $latestPackackage) {
|
||||
$latestVersion = $latestPackackage->getFullPrettyVersion();
|
||||
$style = $this->getVersionStyle($latestPackackage, $package);
|
||||
if (!$io->isDecorated()) {
|
||||
$latestVersion = str_replace(array('info', 'highlight', 'comment'), array('=', '!', '~'), $style) . ' ' . $latestVersion;
|
||||
}
|
||||
$io->write(' <'.$style.'>' . str_pad($latestVersion, $latestLength, ' ') . '</'.$style.'>', false);
|
||||
if ($writeLatest && $latestPackage) {
|
||||
$packageViewData['latest'] = $latestPackage->getFullPrettyVersion();
|
||||
$packageViewData['status'] = $this->getUpdateStatus($latestPackage, $package);
|
||||
}
|
||||
|
||||
if ($writeDescription) {
|
||||
$description = strtok($package->getDescription(), "\r\n");
|
||||
$remaining = $width - $nameLength - $versionLength - 4;
|
||||
if ($writeLatest) {
|
||||
$remaining -= $latestLength;
|
||||
}
|
||||
if (strlen($description) > $remaining) {
|
||||
$description = substr($description, 0, $remaining - 3) . '...';
|
||||
}
|
||||
$io->write(' ' . $description, false);
|
||||
$packageViewData['description'] = $package->getDescription();
|
||||
}
|
||||
|
||||
if ($writePath) {
|
||||
$path = strtok(realpath($composer->getInstallationManager()->getInstallPath($package)), "\r\n");
|
||||
$io->write(' ' . $path, false);
|
||||
$packageViewData['path'] = strtok(realpath($composer->getInstallationManager()->getInstallPath($package)), "\r\n");
|
||||
}
|
||||
|
||||
if ($latestPackackage && $latestPackackage->isAbandoned()) {
|
||||
$replacement = (is_string($latestPackackage->getReplacementPackage()))
|
||||
? 'Use ' . $latestPackackage->getReplacementPackage() . ' instead'
|
||||
if ($latestPackage && $latestPackage->isAbandoned()) {
|
||||
$replacement = (is_string($latestPackage->getReplacementPackage()))
|
||||
? 'Use ' . $latestPackage->getReplacementPackage() . ' instead'
|
||||
: 'No replacement was suggested';
|
||||
|
||||
$io->writeError('');
|
||||
$io->writeError(
|
||||
sprintf(
|
||||
"<warning>Package %s is abandoned, you should avoid using it. %s.</warning>",
|
||||
$package->getPrettyName(),
|
||||
$replacement
|
||||
),
|
||||
false
|
||||
$packageWarning = sprintf(
|
||||
'Package %s is abandoned, you should avoid using it. %s.',
|
||||
$package->getPrettyName(),
|
||||
$replacement
|
||||
);
|
||||
$packageViewData['warning'] = $packageWarning;
|
||||
}
|
||||
} else {
|
||||
$io->write($indent . $package, false);
|
||||
$packageViewData['name'] = $package;
|
||||
}
|
||||
$io->write('');
|
||||
}
|
||||
if ($showAllTypes) {
|
||||
$io->write('');
|
||||
$viewData[$type][] = $packageViewData;
|
||||
}
|
||||
if ($input->getOption('strict') && $hasOutdatedPackages) {
|
||||
return 1;
|
||||
$exitCode = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ('json' === $format) {
|
||||
$io->write(JsonFile::encode($viewData));
|
||||
} else {
|
||||
foreach ($viewData as $type => $packages) {
|
||||
$nameLength = $viewMetaData[$type]['nameLength'];
|
||||
$versionLength = $viewMetaData[$type]['versionLength'];
|
||||
$latestLength = $viewMetaData[$type]['latestLength'];
|
||||
|
||||
$writeVersion = $nameLength + $versionLength + 3 <= $width;
|
||||
$writeLatest = $nameLength + $versionLength + $latestLength + 3 <= $width;
|
||||
$writeDescription = $nameLength + $versionLength + $latestLength + 24 <= $width;
|
||||
|
||||
if ($writeLatest && !$io->isDecorated()) {
|
||||
$latestLength += 2;
|
||||
}
|
||||
|
||||
if ($showAllTypes) {
|
||||
if ('available' === $type) {
|
||||
$io->write('<comment>' . $type . '</comment>:');
|
||||
} else {
|
||||
$io->write('<info>' . $type . '</info>:');
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($packages as $package) {
|
||||
$io->write($indent . str_pad($package['name'], $nameLength, ' '), false);
|
||||
if (isset($package['version']) && $writeVersion) {
|
||||
$io->write(' ' . str_pad($package['version'], $versionLength, ' '), false);
|
||||
}
|
||||
if (isset($package['latest']) && $writeLatest) {
|
||||
$latestVersion = $package['latest'];
|
||||
$updateStatus = $package['status'];
|
||||
$style = $this->updateStatusToVersionStyle($updateStatus);
|
||||
if (!$io->isDecorated()) {
|
||||
$latestVersion = str_replace(array('up-to-date', 'update-recommended', 'update-possible'), array('=', '!', '~'), $updateStatus) . ' ' . $latestVersion;
|
||||
}
|
||||
$io->write(' <' . $style . '>' . str_pad($latestVersion, $latestLength, ' ') . '</' . $style . '>', false);
|
||||
}
|
||||
if (isset($package['description']) && $writeDescription) {
|
||||
$description = strtok($package['description'], "\r\n");
|
||||
$remaining = $width - $nameLength - $versionLength - 4;
|
||||
if ($writeLatest) {
|
||||
$remaining -= $latestLength;
|
||||
}
|
||||
if (strlen($description) > $remaining) {
|
||||
$description = substr($description, 0, $remaining - 3) . '...';
|
||||
}
|
||||
$io->write(' ' . $description, false);
|
||||
}
|
||||
if (isset($package['path'])) {
|
||||
$io->write(' ' . $package['path'], false);
|
||||
}
|
||||
if (isset($package['warning'])) {
|
||||
$io->writeError('');
|
||||
$io->writeError('<warning>' . $package['warning'] . '</warning>', false);
|
||||
}
|
||||
$io->write('');
|
||||
}
|
||||
|
||||
if ($showAllTypes) {
|
||||
$io->write('');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $exitCode;
|
||||
}
|
||||
|
||||
protected function getRootRequires()
|
||||
|
@ -406,22 +472,7 @@ EOT
|
|||
|
||||
protected function getVersionStyle(PackageInterface $latestPackage, PackageInterface $package)
|
||||
{
|
||||
if ($latestPackage->getFullPrettyVersion() === $package->getFullPrettyVersion()) {
|
||||
// print green as it's up to date
|
||||
return 'info';
|
||||
}
|
||||
|
||||
$constraint = $package->getVersion();
|
||||
if (0 !== strpos($constraint, 'dev-')) {
|
||||
$constraint = '^'.$constraint;
|
||||
}
|
||||
if ($latestPackage->getVersion() && Semver::satisfies($latestPackage->getVersion(), $constraint)) {
|
||||
// print red as it needs an immediate semver-compliant upgrade
|
||||
return 'highlight';
|
||||
}
|
||||
|
||||
// print yellow as it needs an upgrade but has potential BC breaks so is not urgent
|
||||
return 'comment';
|
||||
return $this->updateStatusToVersionStyle($this->getUpdateStatus($latestPackage, $package));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -716,6 +767,32 @@ EOT
|
|||
}
|
||||
}
|
||||
|
||||
private function updateStatusToVersionStyle($updateStatus)
|
||||
{
|
||||
// 'up-to-date' is printed green
|
||||
// 'update-recommended' is printed red
|
||||
// 'upgrade-possible' is printed yellow
|
||||
return str_replace(array('up-to-date', 'update-recommended', 'update-possible'), array('info', 'highlight', 'comment'), $updateStatus);
|
||||
}
|
||||
|
||||
private function getUpdateStatus(PackageInterface $latestPackage, PackageInterface $package) {
|
||||
if ($latestPackage->getFullPrettyVersion() === $package->getFullPrettyVersion()) {
|
||||
return 'up-to-date';
|
||||
}
|
||||
|
||||
$constraint = $package->getVersion();
|
||||
if (0 !== strpos($constraint, 'dev-')) {
|
||||
$constraint = '^'.$constraint;
|
||||
}
|
||||
if ($latestPackage->getVersion() && Semver::satisfies($latestPackage->getVersion(), $constraint)) {
|
||||
// it needs an immediate semver-compliant upgrade
|
||||
return 'update-recommended';
|
||||
}
|
||||
|
||||
// it needs an upgrade but has potential BC breaks so is not urgent
|
||||
return 'update-possible';
|
||||
}
|
||||
|
||||
private function writeTreeLine($line)
|
||||
{
|
||||
$io = $this->getIO();
|
||||
|
|
Loading…
Reference in New Issue