-
-
Notifications
You must be signed in to change notification settings - Fork 9.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature #51845 [AssetMapper] Add outdated command (Maelan LE BORGNE)
This PR was squashed before being merged into the 6.4 branch. Discussion ---------- [AssetMapper] Add outdated command | Q | A | ------------- | --- | Branch? | 6.4 | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | | License | MIT As suggested by `@WebMamba`, I added a new command for the AssetMapper component to list outdated 3rd party packages. It is inspired by the `composer outdated` command so I tried to replicate its options as much as I could. It reads the importmap.php and extract packages version to query the https://registry.npmjs.org/%package% API and read the latest version from metadata. ![image](https://github.com/symfony/symfony/assets/11990607/189f66a0-dda0-4916-a91b-988ebe8f9fb3) :warning: The code is base on #51650 branch so it is not ready to be merged yet, but I'd be happy to get some reviews and feedback in the meantime. This is my first PR on symfony, so there will probably be a lot to say ! - [ ] gather feedback - [x] wait for #51650 to be merged and rebase - [ ] write documentation Commits ------- 4d32a35 [AssetMapper] Add outdated command
- Loading branch information
Showing
13 changed files
with
571 additions
and
33 deletions.
There are no files selected for viewing
This file contains 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 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 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 <fabien@symfony.com> | ||
* | ||
* 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 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 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 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 <fabien@symfony.com> | ||
* | ||
* 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; | ||
} | ||
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 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 <fabien@symfony.com> | ||
* | ||
* 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 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.