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); + } + +}