diff --git a/package_manager/src/Event/StatusCheckEvent.php b/package_manager/src/Event/StatusCheckEvent.php index 8ad76416485386f05dcaa7c8292add8fe484c529..876b43a4d348df8f0105030b11f3447a2c3ee891 100644 --- a/package_manager/src/Event/StatusCheckEvent.php +++ b/package_manager/src/Event/StatusCheckEvent.php @@ -7,6 +7,7 @@ namespace Drupal\package_manager\Event; use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\package_manager\Stage; use Drupal\package_manager\ValidationResult; +use Drupal\system\SystemManager; /** * Event fired to check the status of the system to use Package Manager. @@ -16,19 +17,37 @@ use Drupal\package_manager\ValidationResult; */ class StatusCheckEvent extends PreOperationStageEvent { - use ExcludedPathsTrait; + /** + * Returns paths to exclude or NULL if a base requirement is not fulfilled. + * + * @return string[]|null + * The paths to exclude, or NULL if a base requirement is not fulfilled. + * + * @throws \LogicException + * Thrown if the excluded paths are NULL and no errors have been added to + * this event. + */ + public function getExcludedPaths(): ?array { + if (isset($this->excludedPaths)) { + return array_unique($this->excludedPaths); + } + + if (empty($this->getResults(SystemManager::REQUIREMENT_ERROR))) { + throw new \LogicException('$ignored_paths should only be NULL if the error that caused the paths to not be collected was added to the status check event.'); + } + return NULL; + } /** * Constructs a StatusCheckEvent object. * * @param \Drupal\package_manager\Stage $stage * The stage which fired this event. - * @param string[] $ignored_paths - * The list of ignored paths. + * @param string[]|null $excludedPaths + * The list of ignored paths, or NULL if they could not be collected. */ - public function __construct(Stage $stage, array $ignored_paths) { + public function __construct(Stage $stage, private ?array $excludedPaths) { parent::__construct($stage); - $this->excludedPaths = $ignored_paths; } /** diff --git a/package_manager/src/PathExcluder/GitExcluder.php b/package_manager/src/PathExcluder/GitExcluder.php index f1ac80ebed10c668e4d23983e6b96a283fd89ce5..08e6f50644ad02b1176ece4704e4481f37655ac9 100644 --- a/package_manager/src/PathExcluder/GitExcluder.php +++ b/package_manager/src/PathExcluder/GitExcluder.php @@ -4,7 +4,7 @@ declare(strict_types = 1); namespace Drupal\package_manager\PathExcluder; -use Drupal\Core\File\FileSystemInterface; +use Drupal\package_manager\ComposerInspector; use Drupal\package_manager\Event\CollectIgnoredPathsEvent; use Drupal\package_manager\PathLocator; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -26,10 +26,10 @@ final class GitExcluder implements EventSubscriberInterface { * * @param \Drupal\package_manager\PathLocator $path_locator * The path locator service. - * @param \Drupal\Core\File\FileSystemInterface $fileSystem - * The file system service. + * @param \Drupal\package_manager\ComposerInspector $composerInspector + * The Composer inspector service. */ - public function __construct(PathLocator $path_locator, protected FileSystemInterface $fileSystem) { + public function __construct(PathLocator $path_locator, private ComposerInspector $composerInspector) { $this->pathLocator = $path_locator; } @@ -53,10 +53,11 @@ final class GitExcluder implements EventSubscriberInterface { $installed_paths = []; // Collect the paths of every installed package. - $installed_packages = $event->stage->getActiveComposer()->getInstalledPackagesData(); - foreach ($installed_packages as $package_data) { - if (array_key_exists('install_path', $package_data) && !empty($package_data['install_path'])) { - $installed_paths[] = $this->fileSystem->realpath($package_data['install_path']); + $project_root = $this->pathLocator->getProjectRoot(); + $installed_packages = $this->composerInspector->getInstalledPackagesList($project_root); + foreach ($installed_packages as $package) { + if (!empty($package->path)) { + $installed_paths[] = $package->path; } } $paths = $this->scanForDirectoriesByName('.git'); diff --git a/package_manager/src/StatusCheckTrait.php b/package_manager/src/StatusCheckTrait.php index e11dddabb2e9fef482ab9be1d09037ef64913476..5c1da0eac23efaf6a857d9bae3730bbaa15a6237 100644 --- a/package_manager/src/StatusCheckTrait.php +++ b/package_manager/src/StatusCheckTrait.php @@ -33,15 +33,22 @@ trait StatusCheckTrait { protected function runStatusCheck(Stage $stage, EventDispatcherInterface $event_dispatcher = NULL): array { $event_dispatcher ??= \Drupal::service('event_dispatcher'); try { - $ignored_paths = new CollectIgnoredPathsEvent($stage); - $event_dispatcher->dispatch($ignored_paths); + $ignored_paths_event = new CollectIgnoredPathsEvent($stage); + $event_dispatcher->dispatch($ignored_paths_event); + $event = new StatusCheckEvent($stage, $ignored_paths_event->getAll()); } catch (\Throwable $throwable) { - // We can't dispatch the status check event without the ignored paths. - return [ValidationResult::createErrorFromThrowable($throwable, t("Unable to collect ignored paths, therefore can't perform status checks."))]; + // We can dispatch the status check event without the ignored paths, but + // it must be set explicitly to NULL, to allow those status checks to run + // that do not need the ignored paths. + $event = new StatusCheckEvent($stage, NULL); + // Add the error that was encountered so that regardless of any other + // validation errors BaseRequirementsFulfilledValidator will stop the + // event propagation after the base requirement validators have run. + // @see \Drupal\package_manager\Validator\BaseRequirementsFulfilledValidator + $event->addErrorFromThrowable($throwable, t('Unable to collect the ignored paths.')); } - $event = new StatusCheckEvent($stage, $ignored_paths->getAll()); $event_dispatcher->dispatch($event); return $event->getResults(); } diff --git a/package_manager/src/Validator/SymlinkValidator.php b/package_manager/src/Validator/SymlinkValidator.php index 0766fdcac2e0fe434a389dde386aecf05ff9e537..4f56da42b63e044887f89cd1f6add1f183ce1f97 100644 --- a/package_manager/src/Validator/SymlinkValidator.php +++ b/package_manager/src/Validator/SymlinkValidator.php @@ -69,8 +69,16 @@ class SymlinkValidator implements EventSubscriberInterface { } $stage_dir = $this->pathFactory->create($stage_dir); + $ignored_paths = $event->getExcludedPaths(); + // Return early if no ignored paths were collected because this validator + // is dependent on knowing which paths to ignore when searching for + // symlinks. + // @see \Drupal\package_manager\StatusCheckTrait::runStatusCheck() + if ($ignored_paths === NULL) { + return; + } + try { - $ignored_paths = $event->getExcludedPaths(); $this->precondition->assertIsFulfilled($active_dir, $stage_dir, new PathList($ignored_paths)); } catch (PreconditionException $e) { diff --git a/package_manager/tests/src/Kernel/PathExcluder/GitExcluderTest.php b/package_manager/tests/src/Kernel/PathExcluder/GitExcluderTest.php index af74830be7e3c937fb728054073cacb4ab404412..4c3f902eadc60e521d6caa453b0784a6d4be704e 100644 --- a/package_manager/tests/src/Kernel/PathExcluder/GitExcluderTest.php +++ b/package_manager/tests/src/Kernel/PathExcluder/GitExcluderTest.php @@ -4,8 +4,6 @@ declare(strict_types = 1); namespace Drupal\Tests\package_manager\Kernel\PathExcluder; -use Drupal\Core\DependencyInjection\ContainerBuilder; -use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Serialization\Yaml; use Drupal\fixture_manipulator\ActiveFixtureManipulator; use Drupal\Tests\package_manager\Kernel\PackageManagerKernelTestBase; @@ -18,13 +16,6 @@ use Symfony\Component\Filesystem\Filesystem; */ class GitExcluderTest extends PackageManagerKernelTestBase { - /** - * The mocked file system service. - * - * @var \Drupal\Core\File\FileSystemInterface|\Prophecy\Prophecy\ObjectProphecy - */ - private $fileSystem; - /** * {@inheritdoc} */ @@ -43,18 +34,6 @@ class GitExcluderTest extends PackageManagerKernelTestBase { ->commitChanges(); } - /** - * {@inheritdoc} - */ - public function register(ContainerBuilder $container) { - parent::register($container); - - $this->fileSystem = $this->prophesize(FileSystemInterface::class); - - $container->getDefinition('package_manager.git_excluder') - ->setArgument('$fileSystem', $this->fileSystem->reveal()); - } - /** * Tests that Git directories are excluded from stage during PreCreate. */ diff --git a/package_manager/tests/src/Kernel/StageTest.php b/package_manager/tests/src/Kernel/StageTest.php index 9a31448a9a361a4c505b9c72d4c9f70bbca8e638..7a748232a4a202740b0a9d4b214b20e0a982caa1 100644 --- a/package_manager/tests/src/Kernel/StageTest.php +++ b/package_manager/tests/src/Kernel/StageTest.php @@ -616,9 +616,8 @@ class StageTest extends PackageManagerKernelTestBase { ->getProjectRoot(); unlink($project_root . '/composer.json'); $this->expectException(StageException::class); - $this->expectExceptionMessage("Composer could not find the config file: $project_root/composer.json\n"); - $stage = $this->createStage(); - $stage->create(); + $this->expectExceptionMessage("composer.json not found."); + $this->createStage()->create(); } /** diff --git a/package_manager/tests/src/Kernel/StatusCheckTraitTest.php b/package_manager/tests/src/Kernel/StatusCheckTraitTest.php index 9e9e70130e5fd3961ed4bd8ed9285c864eaae53c..8825f3b5c08cbf009fae3112fd47399898218f82 100644 --- a/package_manager/tests/src/Kernel/StatusCheckTraitTest.php +++ b/package_manager/tests/src/Kernel/StatusCheckTraitTest.php @@ -36,21 +36,17 @@ class StatusCheckTraitTest extends PackageManagerKernelTestBase { } /** - * Tests StatusCheckTrait returns an error when unable to get ignored paths. + * Tests that any error will be added to the status check event. */ - public function testErrorIgnoredPathsCollected(): void { - $exception = new \Exception("Not a chance, friend."); - - $expected_result = ValidationResult::createErrorFromThrowable( - $exception, - t("Unable to collect ignored paths, therefore can't perform status checks.") - ); - - $this->addEventTestListener(function () use ($exception): void { - throw $exception; + public function testNoErrorIfIgnoredPathsCannotBeCollected(): void { + $this->addEventTestListener(function (): void { + throw new \Exception('Not a chance, friend.'); }, CollectIgnoredPathsEvent::class); - - $this->assertStatusCheckResults([$expected_result]); + $result = ValidationResult::createError( + [t('Not a chance, friend.')], + t('Unable to collect the ignored paths.'), + ); + $this->assertStatusCheckResults([$result]); } } diff --git a/package_manager/tests/src/Unit/StatusCheckEventTest.php b/package_manager/tests/src/Unit/StatusCheckEventTest.php new file mode 100644 index 0000000000000000000000000000000000000000..3f16d4425868786e20f3085a33cf5640cd651e94 --- /dev/null +++ b/package_manager/tests/src/Unit/StatusCheckEventTest.php @@ -0,0 +1,30 @@ +<?php + +declare(strict_types = 1); + +namespace Drupal\Tests\package_manager\Unit; + +use Drupal\package_manager\Event\StatusCheckEvent; +use Drupal\package_manager\Stage; +use Drupal\Tests\UnitTestCase; + +/** + * @coversDefaultClass \Drupal\package_manager\Event\StatusCheckEvent + * @group package_manager + */ +class StatusCheckEventTest extends UnitTestCase { + + /** + * @covers ::getExcludedPaths + */ + public function testNoPathsNoErrorException(): void { + $event = new StatusCheckEvent( + $this->prophesize(Stage::class)->reveal(), + NULL + ); + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('$ignored_paths should only be NULL if the error that caused the paths to not be collected was added to the status check event.'); + $event->getExcludedPaths(); + } + +}