-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[AssetMapper] Add outdated command #51845
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
fabpot
merged 1 commit into
symfony:6.4
from
maelanleborgne:feature/assetmapper-outdated
Oct 10, 2023
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
106 changes: 106 additions & 0 deletions
106
src/Symfony/Component/AssetMapper/Command/ImportMapOutdatedCommand.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\AssetMapper\Command; | ||
|
||
use Symfony\Component\AssetMapper\ImportMap\ImportMapUpdateChecker; | ||
use Symfony\Component\AssetMapper\ImportMap\PackageUpdateInfo; | ||
use Symfony\Component\Console\Attribute\AsCommand; | ||
use Symfony\Component\Console\Command\Command; | ||
use Symfony\Component\Console\Input\InputArgument; | ||
use Symfony\Component\Console\Input\InputInterface; | ||
use Symfony\Component\Console\Input\InputOption; | ||
use Symfony\Component\Console\Output\OutputInterface; | ||
use Symfony\Component\Console\Style\SymfonyStyle; | ||
|
||
#[AsCommand(name: 'importmap:outdated', description: 'List outdated JavaScript packages and their latest versions')] | ||
final class ImportMapOutdatedCommand extends Command | ||
{ | ||
private const COLOR_MAPPING = [ | ||
'update-possible' => 'yellow', | ||
'semver-safe-update' => 'red', | ||
]; | ||
|
||
public function __construct( | ||
private readonly ImportMapUpdateChecker $updateChecker, | ||
) { | ||
parent::__construct(); | ||
} | ||
|
||
protected function configure(): void | ||
{ | ||
$this | ||
->addArgument( | ||
name: 'packages', | ||
mode: InputArgument::IS_ARRAY | InputArgument::OPTIONAL, | ||
description: 'A list of packages to check', | ||
) | ||
->addOption( | ||
name: 'format', | ||
mode: InputOption::VALUE_REQUIRED, | ||
description: sprintf('The output format ("%s")', implode(', ', $this->getAvailableFormatOptions())), | ||
default: 'txt', | ||
) | ||
->setHelp(<<<'EOT' | ||
The <info>%command.name%</info> command will list the latest updates available for the 3rd party packages in <comment>importmap.php</comment>. | ||
Versions showing in <fg=red>red</> are semver compatible versions and you should upgrading. | ||
Versions showing in <fg=yellow>yellow</> are major updates that include backward compatibility breaks according to semver. | ||
|
||
<info>php %command.full_name%</info> | ||
|
||
Or specific packages only: | ||
|
||
<info>php %command.full_name% <packages></info> | ||
EOT | ||
); | ||
} | ||
|
||
protected function execute(InputInterface $input, OutputInterface $output): int | ||
{ | ||
$io = new SymfonyStyle($input, $output); | ||
$packages = $input->getArgument('packages'); | ||
$packagesUpdateInfos = $this->updateChecker->getAvailableUpdates($packages); | ||
$packagesUpdateInfos = array_filter($packagesUpdateInfos, fn ($packageUpdateInfo) => $packageUpdateInfo->hasUpdate()); | ||
if (0 === \count($packagesUpdateInfos)) { | ||
return Command::SUCCESS; | ||
} | ||
|
||
$displayData = array_map(fn ($importName, $packageUpdateInfo) => [ | ||
'name' => $importName, | ||
'current' => $packageUpdateInfo->currentVersion, | ||
'latest' => $packageUpdateInfo->latestVersion, | ||
'latest-status' => PackageUpdateInfo::UPDATE_TYPE_MAJOR === $packageUpdateInfo->updateType ? 'update-possible' : 'semver-safe-update', | ||
], array_keys($packagesUpdateInfos), $packagesUpdateInfos); | ||
|
||
if ('json' === $input->getOption('format')) { | ||
$io->writeln(json_encode($displayData, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES)); | ||
} else { | ||
$table = $io->createTable(); | ||
$table->setHeaders(['Package', 'Current', 'Latest']); | ||
foreach ($displayData as $datum) { | ||
$color = self::COLOR_MAPPING[$datum['latest-status']] ?? 'default'; | ||
$table->addRow([ | ||
sprintf('<fg=%s>%s</>', $color, $datum['name']), | ||
$datum['current'], | ||
sprintf('<fg=%s>%s</>', $color, $datum['latest']), | ||
]); | ||
} | ||
$table->render(); | ||
} | ||
|
||
return Command::FAILURE; | ||
} | ||
|
||
private function getAvailableFormatOptions(): array | ||
{ | ||
return ['txt', 'json']; | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
90 changes: 90 additions & 0 deletions
90
src/Symfony/Component/AssetMapper/ImportMap/ImportMapUpdateChecker.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\AssetMapper\ImportMap; | ||
|
||
use Symfony\Contracts\HttpClient\HttpClientInterface; | ||
|
||
class ImportMapUpdateChecker | ||
{ | ||
private const URL_PACKAGE_METADATA = 'https://registry.npmjs.org/%s'; | ||
|
||
public function __construct( | ||
private readonly ImportMapConfigReader $importMapConfigReader, | ||
private readonly HttpClientInterface $httpClient, | ||
) { | ||
} | ||
|
||
/** | ||
* @param string[] $packages | ||
* | ||
* @return PackageUpdateInfo[] | ||
*/ | ||
public function getAvailableUpdates(array $packages = []): array | ||
{ | ||
$entries = $this->importMapConfigReader->getEntries(); | ||
$updateInfos = []; | ||
$responses = []; | ||
foreach ($entries as $entry) { | ||
if (null === $entry->packageName || null === $entry->version) { | ||
continue; | ||
} | ||
maelanleborgne marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (\count($packages) && !\in_array($entry->packageName, $packages, true)) { | ||
continue; | ||
} | ||
|
||
$responses[$entry->importName] = $this->httpClient->request('GET', sprintf(self::URL_PACKAGE_METADATA, $entry->packageName), ['headers' => ['Accept' => 'application/vnd.npm.install-v1+json']]); | ||
} | ||
|
||
foreach ($responses as $importName => $response) { | ||
$entry = $entries->get($importName); | ||
if (200 !== $response->getStatusCode()) { | ||
throw new \RuntimeException(sprintf('Unable to get latest version for package "%s".', $entry->packageName)); | ||
} | ||
$updateInfo = new PackageUpdateInfo($entry->packageName, $entry->version); | ||
try { | ||
$updateInfo->latestVersion = json_decode($response->getContent(), true)['dist-tags']['latest']; | ||
$updateInfo->updateType = $this->getUpdateType($updateInfo->currentVersion, $updateInfo->latestVersion); | ||
} catch (\Exception $e) { | ||
throw new \RuntimeException(sprintf('Unable to get latest version for package "%s".', $entry->packageName), 0, $e); | ||
} | ||
$updateInfos[$importName] = $updateInfo; | ||
} | ||
|
||
return $updateInfos; | ||
} | ||
|
||
private function getVersionPart(string $version, int $part): ?string | ||
{ | ||
return explode('.', $version)[$part] ?? $version; | ||
} | ||
|
||
private function getUpdateType(string $currentVersion, string $latestVersion): string | ||
{ | ||
if (version_compare($currentVersion, $latestVersion, '>')) { | ||
return PackageUpdateInfo::UPDATE_TYPE_DOWNGRADE; | ||
} | ||
if (version_compare($currentVersion, $latestVersion, '==')) { | ||
return PackageUpdateInfo::UPDATE_TYPE_UP_TO_DATE; | ||
} | ||
if ($this->getVersionPart($currentVersion, 0) < $this->getVersionPart($latestVersion, 0)) { | ||
return PackageUpdateInfo::UPDATE_TYPE_MAJOR; | ||
} | ||
if ($this->getVersionPart($currentVersion, 1) < $this->getVersionPart($latestVersion, 1)) { | ||
return PackageUpdateInfo::UPDATE_TYPE_MINOR; | ||
} | ||
if ($this->getVersionPart($currentVersion, 2) < $this->getVersionPart($latestVersion, 2)) { | ||
return PackageUpdateInfo::UPDATE_TYPE_PATCH; | ||
} | ||
|
||
throw new \LogicException(sprintf('Unable to determine update type for "%s" and "%s".', $currentVersion, $latestVersion)); | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
src/Symfony/Component/AssetMapper/ImportMap/PackageUpdateInfo.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\AssetMapper\ImportMap; | ||
|
||
class PackageUpdateInfo | ||
{ | ||
public const UPDATE_TYPE_DOWNGRADE = 'downgrade'; | ||
public const UPDATE_TYPE_UP_TO_DATE = 'up-to-date'; | ||
public const UPDATE_TYPE_MAJOR = 'major'; | ||
public const UPDATE_TYPE_MINOR = 'minor'; | ||
public const UPDATE_TYPE_PATCH = 'patch'; | ||
|
||
public function __construct( | ||
public readonly string $packageName, | ||
public readonly string $currentVersion, | ||
public ?string $latestVersion = null, | ||
public ?string $updateType = null, | ||
) { | ||
} | ||
|
||
public function hasUpdate(): bool | ||
{ | ||
return !\in_array($this->updateType, [self::UPDATE_TYPE_DOWNGRADE, self::UPDATE_TYPE_DOWNGRADE, self::UPDATE_TYPE_UP_TO_DATE]); | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be used in the update command too ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain more? Do you mean: in the
update
command, we would first use this function to determine which updates are possible and then loop over and make them? If so, that's a different way than we're doing it now - but I can't think of any big difference in final behavior.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes that was the idea, but i'm not talking about "right now" ... but in a near future, as we already said :)