From e3717fc4437ab912a17aca8ddef04f1282b5c237 Mon Sep 17 00:00:00 2001 From: Santeri Hurnanen Date: Mon, 16 Dec 2024 08:58:11 +0200 Subject: [PATCH 1/6] Remove direct dependencies to developer tools This simplifies dependencies, since these tools are already provided by drupal/core-dev metapackage. For full list of core-dev dependencies, see: https://packagist.org/packages/drupal/core-dev. After this change, developer tools are kept up to date by running `composer update drupal/core-dev --with-all-dependencies`. --- src/Update/migrations.php | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/Update/migrations.php b/src/Update/migrations.php index 04f2d8a..6eefdc5 100644 --- a/src/Update/migrations.php +++ b/src/Update/migrations.php @@ -244,3 +244,31 @@ function drupal_tools_update_12() : UpdateResult { 'Re-installed dg/bypass-finals as dev dependency.', ]); } + +/** + * Remove direct dependency to packages provided by drupal/core-dev. + */ +function drupal_tools_update_13() : UpdateResult { + // Ensure drupal/core-dev is required. + (new Process(['composer', 'require', 'drupal/core-dev:^10', '--dev', '-W'])) + ->run(); + + // For full list of drupal/core-dev dependencies, + // see: https://packagist.org/packages/drupal/core-dev. + // Already provided by drupal/core-dev: + $remove = [ + 'phpstan/phpstan', + 'phpstan/extension-installer', + 'mglaman/phpstan-drupal', + 'phpunit/phpunit', + 'drupal/coder', + 'phpspec/prophecy-phpunit', + ]; + + (new Process(array_merge(['composer', 'remove', '--dev'], $remove))) + ->run(); + + return new UpdateResult([ + 'Removed direct dependency to ' . implode(', ', $remove), + ]); +} From a1d1b42df003cd24f9d37b7589fc4443ec1fbcb6 Mon Sep 17 00:00:00 2001 From: Santeri Hurnanen Date: Mon, 16 Dec 2024 08:58:15 +0200 Subject: [PATCH 2/6] Simplify helfi:tools:check-composer-versions --- README.md | 2 +- .../Commands/PackageScannerDrushCommands.php | 20 ++++---- tests/fixtures/composer.lock | 50 ------------------- .../CheckPackageVersionsCommandsTest.php | 46 ++++++++--------- 4 files changed, 31 insertions(+), 87 deletions(-) delete mode 100644 tests/fixtures/composer.lock diff --git a/README.md b/README.md index bc1e643..bf8de3d 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,7 @@ $fileManager->updateFiles($options, [ ``` ## Checking composer package versions -Checks whether HELfi packages are up-to-date. This is used as a part of our [Automatic update workflow](https://github.com/City-of-Helsinki/drupal-helfi-platform/blob/main/documentation/automatic-updates.md) to warn if composer was unable to update any of our dependencies. +Checks ackages are up-to-date. This is used as a part of our [Automatic update workflow](https://github.com/City-of-Helsinki/drupal-helfi-platform/blob/main/documentation/automatic-updates.md) to warn if composer was unable to update any of our dependencies. Run `drush helfi:tools:check-composer-versions /path/to/composer.lock`. diff --git a/src/Drush/Commands/PackageScannerDrushCommands.php b/src/Drush/Commands/PackageScannerDrushCommands.php index 5ce1e26..c694c8b 100644 --- a/src/Drush/Commands/PackageScannerDrushCommands.php +++ b/src/Drush/Commands/PackageScannerDrushCommands.php @@ -4,7 +4,6 @@ namespace DrupalTools\Drush\Commands; -use ComposerLockParser\ComposerInfo; use Consolidation\AnnotatedCommand\CommandResult; use Consolidation\OutputFormatters\StructuredData\RowsOfFields; use Drupal\Component\DependencyInjection\ContainerInterface; @@ -31,6 +30,7 @@ final class PackageScannerDrushCommands extends DrushCommands { public function __construct( private readonly VersionChecker $versionChecker, ) { + parent::__construct(); } /** @@ -55,6 +55,8 @@ public static function create(ContainerInterface $container, ?DrushContainer $dr /** * Checks whether Composer packages are up-to-date. * + * Wrapper for `composer outdated` command. + * * @param string $file * The path to composer.lock file. * @param array $options @@ -70,22 +72,20 @@ public static function create(ContainerInterface $container, ?DrushContainer $dr 'version' => 'Current version', 'latest' => 'Latest version', ])] - public function checkVersions(string $file, array $options = ['format' => 'table']) : CommandResult { - $info = new ComposerInfo($file); - + public function checkVersions(?string $file = NULL, array $options = ['format' => 'table']) : CommandResult { $rows = []; - /** @var \Composer\Package\Package $package */ - foreach (iterator_to_array($info->getPackages()) as $package) { - $version = $this->versionChecker->get($package->getName(), $package->getVersion()); + foreach ($this->versionChecker->getOutdated($file) as $version) { + /** @var \Drupal\helfi_api_base\Package\Version $version */ // Skip dev versions since we can't easily verify the latest // version. - if (!$version || $version->isLatest || str_starts_with($package->getVersion(), 'dev-')) { + if ($version->isLatest || str_starts_with($version->version, 'dev-')) { continue; } + $rows[] = [ - 'name' => $package->getName(), - 'version' => $package->getVersion(), + 'name' => $version->name, + 'version' => $version->version, 'latest' => $version->latestVersion, ]; } diff --git a/tests/fixtures/composer.lock b/tests/fixtures/composer.lock deleted file mode 100644 index ba9dcba..0000000 --- a/tests/fixtures/composer.lock +++ /dev/null @@ -1,50 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "packages": [ - { - "name": "drupal/helfi_api_base", - "version": "1.0.18", - "source": { - "type": "git", - "url": "https://github.com/City-of-Helsinki/drupal-module-helfi-api-base.git", - "reference": "e22755ca1b95a9ee142f2edeadc4a1d99a98cb17" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/City-of-Helsinki/drupal-module-helfi-api-base/zipball/e22755ca1b95a9ee142f2edeadc4a1d99a98cb17", - "reference": "e22755ca1b95a9ee142f2edeadc4a1d99a98cb17", - "shasum": "" - }, - "require": { - "drupal/api_tools": "~1.0.0" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "drupal/coder": "^8.3" - }, - "type": "drupal-custom-module", - "license": [ - "GPL-2.0-or-later" - ], - "description": "Helfi - API Base", - "support": { - "source": "https://github.com/City-of-Helsinki/drupal-module-helfi-api-base/tree/1.0.18", - "issues": "https://github.com/City-of-Helsinki/drupal-module-helfi-api-base/issues" - }, - "time": "2021-04-29T07:34:21+00:00" - } - ], - "packages-dev": [], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": [], - "platform-dev": [], - "plugin-api-version": "2.6.0" -} diff --git a/tests/src/Kernel/CheckPackageVersionsCommandsTest.php b/tests/src/Kernel/CheckPackageVersionsCommandsTest.php index 1a0edee..4019a17 100644 --- a/tests/src/Kernel/CheckPackageVersionsCommandsTest.php +++ b/tests/src/Kernel/CheckPackageVersionsCommandsTest.php @@ -5,14 +5,15 @@ namespace Drupal\Tests\helfi_drupal_tools\Kernel; use Consolidation\OutputFormatters\StructuredData\RowsOfFields; +use Drupal\helfi_api_base\Package\ComposerOutdatedProcess; use Drupal\KernelTests\KernelTestBase; use Drupal\Tests\helfi_api_base\Traits\ApiTestTrait; use DrupalTools\Drush\Commands\PackageScannerDrushCommands; use DrupalTools\OutputFormatters\MarkdownTableFormatter; use Drush\Commands\DrushCommands; use Drush\Formatters\DrushFormatterManager; -use GuzzleHttp\Psr7\Response; use League\Container\Container; +use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; use Psr\Container\ContainerInterface; @@ -65,32 +66,29 @@ public function testInvalidComposerFileException() : void { * Tests version check. */ public function testVersionCheck() : void { - $this->setupMockHttpClient([ - new Response(body: json_encode([ - 'packages' => [ - 'drupal/helfi_api_base' => [ - [ - 'name' => 'drupal/helfi_api_base', - 'version' => '1.1.0', - ], + $process = $this->prophesize(ComposerOutdatedProcess::class); + $process->run(Argument::any()) + ->willReturn([ + // No packages need updating. + ], [ + 'installed' => [ + [ + 'name' => 'drupal/helfi_api_base', + 'latest' => '1.1.0', + 'version' => '1.0.18', ], ], - ])), - new Response(body: json_encode([ - 'packages' => [ - 'drupal/helfi_api_base' => [ - [ - 'name' => 'drupal/helfi_api_base', - 'version' => '1.0.18', - ], - ], - ], - ])), - ]); + ]); + $this->container->set(ComposerOutdatedProcess::class, $process->reveal()); $sut = PackageScannerDrushCommands::create($this->container, $this->getDrushContainer()); + + // Test with up-to-date version and make sure we exit with success. + $return = $sut->checkVersions(); + $this->assertEquals(DrushCommands::EXIT_SUCCESS, $return->getExitCode()); + // Test with old version and make sure we exit with failure. - $return = $sut->checkVersions(__DIR__ . '/../../fixtures/composer.lock'); + $return = $sut->checkVersions(); $this->assertEquals(DrushCommands::EXIT_FAILURE_WITH_CLARITY, $return->getExitCode()); $rows = $return->getOutputData(); $this->assertInstanceOf(RowsOfFields::class, $rows); @@ -101,10 +99,6 @@ public function testVersionCheck() : void { 'latest' => '1.1.0', ], ], $rows->getArrayCopy()); - - // Test with up-to-date version and make sure we exit with success. - $return = $sut->checkVersions(__DIR__ . '/../../fixtures/composer.lock'); - $this->assertEquals(DrushCommands::EXIT_SUCCESS, $return->getExitCode()); } } From 099ba39dc3cf529a5800cfb4f277305fc33c2c7c Mon Sep 17 00:00:00 2001 From: Santeri Hurnanen Date: Mon, 16 Dec 2024 08:58:16 +0200 Subject: [PATCH 3/6] Sync dependabot config --- src/Drush/Commands/UpdateDrushCommands.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Drush/Commands/UpdateDrushCommands.php b/src/Drush/Commands/UpdateDrushCommands.php index c2b8128..4a3ed78 100644 --- a/src/Drush/Commands/UpdateDrushCommands.php +++ b/src/Drush/Commands/UpdateDrushCommands.php @@ -266,6 +266,7 @@ private function updateDefaultFiles(UpdateOptions $options) : self { 'tests/dtt/src/ExistingSite/ModuleEnabledTest.php', ]) ->updateFiles($options, [ + '.github/dependabot.yml.dist' => '.github/dependabot.yml', '.github/workflows/test.yml.dist' => '.github/workflows/test.yml', '.github/workflows/artifact.yml.dist' => '.github/workflows/artifact.yml', '.github/workflows/update-config.yml.dist' => '.github/workflows/update-config.yml', From f671eb0bb5cbb52aab5565d06737252bc37993b7 Mon Sep 17 00:00:00 2001 From: Santeri Hurnanen Date: Mon, 16 Dec 2024 08:58:22 +0200 Subject: [PATCH 4/6] Allow selecting platform branch for helfi:tools:update-platform command --- src/Drush/Commands/UpdateDrushCommands.php | 4 +++- src/HttpFileManager.php | 9 ++++++--- src/Update/FileManager.php | 10 ++++++---- src/Update/UpdateOptions.php | 3 +++ tests/src/Unit/FileManagerTest.php | 14 ++++++++------ tests/src/Unit/HttpFileManagerTest.php | 3 ++- 6 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/Drush/Commands/UpdateDrushCommands.php b/src/Drush/Commands/UpdateDrushCommands.php index 4a3ed78..b4dd6f6 100644 --- a/src/Drush/Commands/UpdateDrushCommands.php +++ b/src/Drush/Commands/UpdateDrushCommands.php @@ -27,7 +27,7 @@ #[Bootstrap(DrupalBootLevels::NONE)] final class UpdateDrushCommands extends DrushCommands { - private const BASE_URL = 'https://raw.githubusercontent.com/City-of-Helsinki/drupal-helfi-platform/main/'; + private const BASE_URL = 'https://raw.githubusercontent.com/City-of-Helsinki/drupal-helfi-platform/'; /** * The git root. @@ -37,6 +37,7 @@ final class UpdateDrushCommands extends DrushCommands { private ?string $gitRoot = NULL; public const DEFAULT_OPTIONS = [ + 'branch' => 'main', 'ignore-files' => TRUE, 'update-external-packages' => TRUE, 'self-update' => TRUE, @@ -124,6 +125,7 @@ private function parseOptions(array $options, string $root) : UpdateOptions { return new UpdateOptions( ignoreFiles: $ignoreFiles, + branch: $options['branch'], updateExternalPackages: $options['update-external-packages'], selfUpdate: $options['self-update'], runMigrations: $options['run-migrations'], diff --git a/src/HttpFileManager.php b/src/HttpFileManager.php index 6b240c1..14f4014 100644 --- a/src/HttpFileManager.php +++ b/src/HttpFileManager.php @@ -4,6 +4,7 @@ namespace DrupalTools; +use DrupalTools\Update\UpdateOptions; use GuzzleHttp\ClientInterface; use GuzzleHttp\Psr7\Utils; @@ -18,7 +19,7 @@ class HttpFileManager { * @param \GuzzleHttp\ClientInterface $client * The HTTP client. */ - public function __construct(private ClientInterface $client) { + public function __construct(private readonly ClientInterface $client) { } /** @@ -28,12 +29,14 @@ public function __construct(private ClientInterface $client) { * The source. * @param string $destination * The file destination. + * @param \DrupalTools\Update\UpdateOptions $options + * The update options. * * @throws \GuzzleHttp\Exception\GuzzleException */ - public function copyFile(string $source, string $destination) : void { + public function copyFile(string $source, string $destination, UpdateOptions $options) : void { $resource = Utils::tryFopen($destination, 'w'); - $this->client->request('GET', $source, ['sink' => $resource]); + $this->client->request('GET', $options->branch . '/' . $source, ['sink' => $resource]); } } diff --git a/src/Update/FileManager.php b/src/Update/FileManager.php index 14be17d..282e132 100644 --- a/src/Update/FileManager.php +++ b/src/Update/FileManager.php @@ -102,7 +102,7 @@ public function updateFiles(UpdateOptions $options, array $map) : self { $destination = $source; } } - $this->copyFile($source, $destination); + $this->copyFile($source, $destination, $options); } return $this; @@ -136,13 +136,15 @@ private function ensureFolder(string $destination) : void { * The source. * @param string $destination * The destination. + * @param \DrupalTools\Update\UpdateOptions $options + * The update options. * * @throws \GuzzleHttp\Exception\GuzzleException * @throws \Symfony\Component\Filesystem\Exception\IOException */ - private function copyFile(string $source, string $destination) : void { + private function copyFile(string $source, string $destination, UpdateOptions $options) : void { $this->ensureFolder($source); - $this->httpFileManager->copyFile($source, $destination); + $this->httpFileManager->copyFile($source, $destination, $options); } /** @@ -235,7 +237,7 @@ public function addFiles(UpdateOptions $options, array $map) : self { if ($this->filesystem->exists($destination)) { continue; } - $this->copyFile($source, $destination); + $this->copyFile($source, $destination, $options); continue; } diff --git a/src/Update/UpdateOptions.php b/src/Update/UpdateOptions.php index b3f3747..bb3dd5b 100644 --- a/src/Update/UpdateOptions.php +++ b/src/Update/UpdateOptions.php @@ -14,6 +14,8 @@ final class UpdateOptions { * * @param array $ignoreFiles * Files to ignore. Leave empty to never ignore anything. + * @param string $branch + * Helfi platform branch for file updates. * @param bool $updateExternalPackages * Whether to update external packages. * @param bool $selfUpdate @@ -25,6 +27,7 @@ final class UpdateOptions { */ public function __construct( public array $ignoreFiles = [], + public string $branch = 'main', public bool $updateExternalPackages = TRUE, public bool $selfUpdate = TRUE, public bool $runMigrations = TRUE, diff --git a/tests/src/Unit/FileManagerTest.php b/tests/src/Unit/FileManagerTest.php index 3dc2349..afb2d38 100644 --- a/tests/src/Unit/FileManagerTest.php +++ b/tests/src/Unit/FileManagerTest.php @@ -8,6 +8,7 @@ use DrupalTools\Update\FileManager; use DrupalTools\Update\UpdateOptions; use PHPUnit\Framework\TestCase; +use Prophecy\Argument; use Prophecy\PhpUnit\ProphecyTrait; use Prophecy\Prediction\CallPrediction; use Prophecy\Prediction\NoCallsPrediction; @@ -35,7 +36,7 @@ public function testUpdateFilesIgnore(array $ignoreFiles, array $files) : void { // Make sure ensureFolder() is called. $filesystem->mkdir(dirname($name), 0755) ->should($prediction); - $client->copyFile($name, $name) + $client->copyFile($name, $name, Argument::any()) ->should($prediction); } @@ -87,13 +88,14 @@ public function testUpdateFilesIsDist() : void { ->shouldBeCalled() ->willReturn(FALSE); + $options = new UpdateOptions(); + $client = $this->prophesize(HttpFileManager::class); - $client->copyFile('test1.yml.dist', 'test1.yml') + $client->copyFile('test1.yml.dist', 'test1.yml', Argument::is($options)) ->shouldBeCalled(); - $client->copyFile('test2.yml.dist', 'test2.yml.dist') + $client->copyFile('test2.yml.dist', 'test2.yml.dist', Argument::is($options)) ->shouldBeCalled(); - $options = new UpdateOptions(); $sut = new FileManager($client->reveal(), $filesystem->reveal()); // The test1.yml.dist should be updated as test1.yml because // test1.yml already exists. @@ -172,9 +174,9 @@ public function addFilesIgnoreFilesData() : array { */ public function testRemoteFile() : void { $client = $this->prophesize(HttpFileManager::class); - $client->copyFile('Dockerfile', 'Dockerfile') + $client->copyFile('Dockerfile', 'Dockerfile', Argument::any()) ->shouldNotBeCalled(); - $client->copyFile('Dockerfile.3', 'Dockerfile.3') + $client->copyFile('Dockerfile.3', 'Dockerfile.3', Argument::any()) ->shouldBeCalled(); $filesystem = $this->prophesize(Filesystem::class); $filesystem->exists('Dockerfile') diff --git a/tests/src/Unit/HttpFileManagerTest.php b/tests/src/Unit/HttpFileManagerTest.php index 7a8927e..27ec6a4 100644 --- a/tests/src/Unit/HttpFileManagerTest.php +++ b/tests/src/Unit/HttpFileManagerTest.php @@ -5,6 +5,7 @@ namespace Drupal\Tests\helfi_drupal_tools\Unit; use DrupalTools\HttpFileManager; +use DrupalTools\Update\UpdateOptions; use GuzzleHttp\Client; use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\HandlerStack; @@ -43,7 +44,7 @@ public function testCopyFile() : void { new Response(body: '123'), ]); $manager = new HttpFileManager($client); - $manager->copyFile('http://localhost/file', $filename); + $manager->copyFile('http://localhost/file', $filename, new UpdateOptions()); $content = file_get_contents($filename); $this->assertSame('123', $content); From 8a74364253ae13d13d985e27bceb44ffc6932a69 Mon Sep 17 00:00:00 2001 From: Santeri Hurnanen Date: Tue, 17 Dec 2024 07:43:09 +0200 Subject: [PATCH 5/6] Move VersionChecker from helfi_api_base to helfi_drupal_tools --- README.md | 2 +- .../Commands/PackageScannerDrushCommands.php | 30 ++++--- src/Package/ComposerOutdatedProcess.php | 30 +++++++ .../Exception/VersionCheckException.php | 11 +++ src/Package/Version.php | 29 +++++++ src/Package/VersionChecker.php | 74 +++++++++++++++++ tests/fixtures/composer.lock | 50 +++++++++++ .../CheckPackageVersionsCommandsTest.php | 23 ++--- tests/src/Unit/Package/VersionCheckerTest.php | 83 +++++++++++++++++++ 9 files changed, 299 insertions(+), 33 deletions(-) create mode 100644 src/Package/ComposerOutdatedProcess.php create mode 100644 src/Package/Exception/VersionCheckException.php create mode 100644 src/Package/Version.php create mode 100644 src/Package/VersionChecker.php create mode 100644 tests/fixtures/composer.lock create mode 100644 tests/src/Unit/Package/VersionCheckerTest.php diff --git a/README.md b/README.md index bf8de3d..7f7b2af 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,7 @@ $fileManager->updateFiles($options, [ ``` ## Checking composer package versions -Checks ackages are up-to-date. This is used as a part of our [Automatic update workflow](https://github.com/City-of-Helsinki/drupal-helfi-platform/blob/main/documentation/automatic-updates.md) to warn if composer was unable to update any of our dependencies. +Checks that packages are up-to-date. This is used as a part of our [Automatic update workflow](https://github.com/City-of-Helsinki/drupal-helfi-platform/blob/main/documentation/automatic-updates.md) to warn if composer was unable to update any of our dependencies. Run `drush helfi:tools:check-composer-versions /path/to/composer.lock`. diff --git a/src/Drush/Commands/PackageScannerDrushCommands.php b/src/Drush/Commands/PackageScannerDrushCommands.php index c694c8b..20dca32 100644 --- a/src/Drush/Commands/PackageScannerDrushCommands.php +++ b/src/Drush/Commands/PackageScannerDrushCommands.php @@ -6,14 +6,15 @@ use Consolidation\AnnotatedCommand\CommandResult; use Consolidation\OutputFormatters\StructuredData\RowsOfFields; -use Drupal\Component\DependencyInjection\ContainerInterface; -use Drupal\helfi_api_base\Package\VersionChecker; use DrupalTools\OutputFormatters\MarkdownTableFormatter; +use DrupalTools\Package\ComposerOutdatedProcess; +use DrupalTools\Package\VersionChecker; use Drush\Attributes\Argument; +use Drush\Attributes\Bootstrap; use Drush\Attributes\Command; use Drush\Attributes\FieldLabels; +use Drush\Boot\DrupalBootLevels; use Drush\Commands\DrushCommands; -use Drush\Drush; use Psr\Container\ContainerInterface as DrushContainer; /** @@ -24,7 +25,7 @@ final class PackageScannerDrushCommands extends DrushCommands { /** * Constructs a new instance. * - * @param \Drupal\helfi_api_base\Package\VersionChecker $versionChecker + * @param \DrupalTools\Package\VersionChecker $versionChecker * The version checker service. */ public function __construct( @@ -36,10 +37,7 @@ public function __construct( /** * {@inheritdoc} */ - public static function create(ContainerInterface $container, ?DrushContainer $drush = NULL): self { - if (!$drush && Drush::hasContainer()) { - $drush = Drush::getContainer(); - } + public static function create(DrushContainer $drush): self { /** @var \Drush\Formatters\DrushFormatterManager $formatterManager */ $formatterManager = $drush->get('formatterManager'); @@ -47,9 +45,11 @@ public static function create(ContainerInterface $container, ?DrushContainer $dr if (!$formatterManager->hasFormatter('markdown_table')) { $formatterManager->addFormatter('markdown_table', new MarkdownTableFormatter()); } - return new self( - $container->get('helfi_api_base.package_version_checker'), - ); + + $process = new ComposerOutdatedProcess(); + $versionChecker = new VersionChecker($process); + + return new self($versionChecker); } /** @@ -66,6 +66,7 @@ public static function create(ContainerInterface $container, ?DrushContainer $dr * The result. */ #[Command(name: 'helfi:tools:check-composer-versions')] + #[Bootstrap(level: DrupalBootLevels::NONE)] #[Argument(name: 'file', description: 'Path to composer.lock file')] #[FieldLabels(labels: [ 'name' => 'Name', @@ -75,11 +76,8 @@ public static function create(ContainerInterface $container, ?DrushContainer $dr public function checkVersions(?string $file = NULL, array $options = ['format' => 'table']) : CommandResult { $rows = []; foreach ($this->versionChecker->getOutdated($file) as $version) { - /** @var \Drupal\helfi_api_base\Package\Version $version */ - - // Skip dev versions since we can't easily verify the latest - // version. - if ($version->isLatest || str_starts_with($version->version, 'dev-')) { + // Skip dev versions since we can't easily verify the latest version. + if (str_starts_with($version->version, 'dev-')) { continue; } diff --git a/src/Package/ComposerOutdatedProcess.php b/src/Package/ComposerOutdatedProcess.php new file mode 100644 index 0000000..60de629 --- /dev/null +++ b/src/Package/ComposerOutdatedProcess.php @@ -0,0 +1,30 @@ +mustRun(); + return json_decode($process->getOutput(), TRUE); + } + +} diff --git a/src/Package/Exception/VersionCheckException.php b/src/Package/Exception/VersionCheckException.php new file mode 100644 index 0000000..39e5f3f --- /dev/null +++ b/src/Package/Exception/VersionCheckException.php @@ -0,0 +1,11 @@ +getPackages($composerLockFile); + $versions = []; + + foreach ($packages as $packageName => $package) { + $versions[] = new Version($packageName, $package['latest'], $package['version']); + } + + return $versions; + } + + /** + * Get outdated packages. + * + * @throws \DrupalTools\Package\Exception\VersionCheckException + */ + private function getPackages(string $composerLockFile): array { + if (!$composerLockFile = realpath($composerLockFile)) { + throw new VersionCheckException('Composer lock file not found'); + } + + $workingDir = dirname($composerLockFile); + try { + $packages = $this->process->run($workingDir); + $packages = $packages['installed'] ?? []; + } + catch (ProcessFailedException) { + throw new VersionCheckException("Composer process failed"); + } + + $result = []; + + // Key with package name. + foreach ($packages as $package) { + $result[$package['name']] = $package; + } + + return $result; + } + +} diff --git a/tests/fixtures/composer.lock b/tests/fixtures/composer.lock new file mode 100644 index 0000000..ba9dcba --- /dev/null +++ b/tests/fixtures/composer.lock @@ -0,0 +1,50 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "packages": [ + { + "name": "drupal/helfi_api_base", + "version": "1.0.18", + "source": { + "type": "git", + "url": "https://github.com/City-of-Helsinki/drupal-module-helfi-api-base.git", + "reference": "e22755ca1b95a9ee142f2edeadc4a1d99a98cb17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/City-of-Helsinki/drupal-module-helfi-api-base/zipball/e22755ca1b95a9ee142f2edeadc4a1d99a98cb17", + "reference": "e22755ca1b95a9ee142f2edeadc4a1d99a98cb17", + "shasum": "" + }, + "require": { + "drupal/api_tools": "~1.0.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", + "drupal/coder": "^8.3" + }, + "type": "drupal-custom-module", + "license": [ + "GPL-2.0-or-later" + ], + "description": "Helfi - API Base", + "support": { + "source": "https://github.com/City-of-Helsinki/drupal-module-helfi-api-base/tree/1.0.18", + "issues": "https://github.com/City-of-Helsinki/drupal-module-helfi-api-base/issues" + }, + "time": "2021-04-29T07:34:21+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.6.0" +} diff --git a/tests/src/Kernel/CheckPackageVersionsCommandsTest.php b/tests/src/Kernel/CheckPackageVersionsCommandsTest.php index 4019a17..57861ce 100644 --- a/tests/src/Kernel/CheckPackageVersionsCommandsTest.php +++ b/tests/src/Kernel/CheckPackageVersionsCommandsTest.php @@ -5,11 +5,12 @@ namespace Drupal\Tests\helfi_drupal_tools\Kernel; use Consolidation\OutputFormatters\StructuredData\RowsOfFields; -use Drupal\helfi_api_base\Package\ComposerOutdatedProcess; use Drupal\KernelTests\KernelTestBase; use Drupal\Tests\helfi_api_base\Traits\ApiTestTrait; use DrupalTools\Drush\Commands\PackageScannerDrushCommands; use DrupalTools\OutputFormatters\MarkdownTableFormatter; +use DrupalTools\Package\ComposerOutdatedProcess; +use DrupalTools\Package\VersionChecker; use Drush\Commands\DrushCommands; use Drush\Formatters\DrushFormatterManager; use League\Container\Container; @@ -49,19 +50,10 @@ private function getDrushContainer() : ContainerInterface { */ public function testMarkdownTableFormatter() : void { $container = $this->getDrushContainer(); - PackageScannerDrushCommands::create($this->container, $container); + PackageScannerDrushCommands::create($container); $this->assertInstanceOf(MarkdownTableFormatter::class, $container->get('formatterManager')->getFormatter('markdown_table')); } - /** - * Tests version check with invalid composer.json file. - */ - public function testInvalidComposerFileException() : void { - $sut = PackageScannerDrushCommands::create($this->container, $this->getDrushContainer()); - $this->expectException(\RuntimeException::class); - $sut->checkVersions('nonexistent.lock'); - } - /** * Tests version check. */ @@ -79,16 +71,15 @@ public function testVersionCheck() : void { ], ], ]); - $this->container->set(ComposerOutdatedProcess::class, $process->reveal()); - - $sut = PackageScannerDrushCommands::create($this->container, $this->getDrushContainer()); + $versionChecker = new VersionChecker($process->reveal()); + $sut = new PackageScannerDrushCommands($versionChecker); // Test with up-to-date version and make sure we exit with success. - $return = $sut->checkVersions(); + $return = $sut->checkVersions(__DIR__ . '/../../fixtures/composer.lock'); $this->assertEquals(DrushCommands::EXIT_SUCCESS, $return->getExitCode()); // Test with old version and make sure we exit with failure. - $return = $sut->checkVersions(); + $return = $sut->checkVersions(__DIR__ . '/../../fixtures/composer.lock'); $this->assertEquals(DrushCommands::EXIT_FAILURE_WITH_CLARITY, $return->getExitCode()); $rows = $return->getOutputData(); $this->assertInstanceOf(RowsOfFields::class, $rows); diff --git a/tests/src/Unit/Package/VersionCheckerTest.php b/tests/src/Unit/Package/VersionCheckerTest.php new file mode 100644 index 0000000..87b65fa --- /dev/null +++ b/tests/src/Unit/Package/VersionCheckerTest.php @@ -0,0 +1,83 @@ +prophesize(ComposerOutdatedProcess::class); + $process->run(Argument::any())->willReturn([]); + $sut = new VersionChecker($process->reveal()); + + $this->expectException(VersionCheckException::class); + $sut->getOutdated('nonexistent.lock'); + } + + /** + * Tests composer command failure. + */ + public function testProcessFailure(): void { + $process = $this->prophesize(ComposerOutdatedProcess::class); + $process + ->run(Argument::any()) + ->shouldBeCalled() + ->willThrow($this->prophesize(ProcessFailedException::class)->reveal()); + + $sut = new VersionChecker($process->reveal()); + + $this->expectException(VersionCheckException::class); + $sut->getOutdated(__DIR__ . '/../../../fixtures/composer.lock'); + } + + /** + * Tests getOutdated(). + */ + public function testGetOutdated(): void { + $process = $this->prophesize(ComposerOutdatedProcess::class); + $process + ->run(Argument::any()) + ->shouldBeCalled() + ->willReturn([ + 'installed' => [ + [ + 'name' => 'drupal/helfi_api_base', + 'version' => '1.0.18', + 'latest' => '1.1.0', + ], + ], + ]); + + $sut = new VersionChecker($process->reveal()); + + $outdated = $sut->getOutdated(__DIR__ . '/../../../fixtures/composer.lock'); + + $this->assertNotEmpty($outdated); + $outdated = reset($outdated); + $this->assertInstanceOf(Version::class, $outdated); + $this->assertEquals('drupal/helfi_api_base', $outdated->name); + $this->assertEquals('1.0.18', $outdated->version); + $this->assertEquals('1.1.0', $outdated->latestVersion); + } + +} From 244e6135ef0a7ed7e084077f1b88fec1107acdd3 Mon Sep 17 00:00:00 2001 From: Santeri Hurnanen Date: Tue, 17 Dec 2024 09:42:31 +0200 Subject: [PATCH 6/6] Require file argument --- src/Drush/Commands/PackageScannerDrushCommands.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Drush/Commands/PackageScannerDrushCommands.php b/src/Drush/Commands/PackageScannerDrushCommands.php index 20dca32..c08b76c 100644 --- a/src/Drush/Commands/PackageScannerDrushCommands.php +++ b/src/Drush/Commands/PackageScannerDrushCommands.php @@ -73,7 +73,7 @@ public static function create(DrushContainer $drush): self { 'version' => 'Current version', 'latest' => 'Latest version', ])] - public function checkVersions(?string $file = NULL, array $options = ['format' => 'table']) : CommandResult { + public function checkVersions(string $file, array $options = ['format' => 'table']) : CommandResult { $rows = []; foreach ($this->versionChecker->getOutdated($file) as $version) { // Skip dev versions since we can't easily verify the latest version.