diff --git a/automatic_updates.services.yml b/automatic_updates.services.yml index 834207fca3be224147cfcc76da2e63366a146994..d7b7982665a9822bfaa504146d4a48744bd4c55e 100644 --- a/automatic_updates.services.yml +++ b/automatic_updates.services.yml @@ -76,3 +76,8 @@ services: arguments: ['%app.root%', '%site.path%', '@file_system', '@stream_wrapper_manager'] tags: - { name: event_subscriber } + automatic_updates.composer_executable_validator: + class: Drupal\automatic_updates\Validation\ComposerExecutableValidator + arguments: ['@automatic_updates.exec_finder'] + tags: + - { name: event_subscriber } diff --git a/src/Validation/ComposerExecutableValidator.php b/src/Validation/ComposerExecutableValidator.php new file mode 100644 index 0000000000000000000000000000000000000000..6244e78fde7883a91d215500422e87199afb88f1 --- /dev/null +++ b/src/Validation/ComposerExecutableValidator.php @@ -0,0 +1,60 @@ +<?php + +namespace Drupal\automatic_updates\Validation; + +use Drupal\automatic_updates\AutomaticUpdatesEvents; +use Drupal\automatic_updates\Event\UpdateEvent; +use PhpTuf\ComposerStager\Exception\IOException; +use PhpTuf\ComposerStager\Infrastructure\Process\ExecutableFinderInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Validates that the Composer executable can be found. + */ +class ComposerExecutableValidator implements EventSubscriberInterface { + + /** + * The executable finder service. + * + * @var \PhpTuf\ComposerStager\Infrastructure\Process\ExecutableFinderInterface + */ + protected $executableFinder; + + /** + * Constructs a ComposerExecutableValidator object. + * + * @param \PhpTuf\ComposerStager\Infrastructure\Process\ExecutableFinderInterface $executable_finder + * The executable finder service. + */ + public function __construct(ExecutableFinderInterface $executable_finder) { + $this->executableFinder = $executable_finder; + } + + /** + * Validates that the Composer executable can be found. + * + * @param \Drupal\automatic_updates\Event\UpdateEvent $event + * The event object. + */ + public function checkForComposerExecutable(UpdateEvent $event): void { + try { + $this->executableFinder->find('composer'); + } + catch (IOException $e) { + $error = ValidationResult::createError([ + $e->getMessage(), + ]); + $event->addValidationResult($error); + } + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() { + return [ + AutomaticUpdatesEvents::READINESS_CHECK => 'checkForComposerExecutable', + ]; + } + +} diff --git a/tests/src/Kernel/ReadinessValidation/ComposerExecutableValidatorTest.php b/tests/src/Kernel/ReadinessValidation/ComposerExecutableValidatorTest.php new file mode 100644 index 0000000000000000000000000000000000000000..7426323fc4674b270458e6d78e73fb3992685082 --- /dev/null +++ b/tests/src/Kernel/ReadinessValidation/ComposerExecutableValidatorTest.php @@ -0,0 +1,49 @@ +<?php + +namespace Drupal\Tests\automatic_updates\Kernel\ReadinessValidation; + +use Drupal\automatic_updates\Validation\ValidationResult; +use Drupal\KernelTests\KernelTestBase; +use Drupal\Tests\automatic_updates\Traits\ValidationTestTrait; +use PhpTuf\ComposerStager\Exception\IOException; +use PhpTuf\ComposerStager\Infrastructure\Process\ExecutableFinderInterface; + +/** + * @covers \Drupal\automatic_updates\Validation\ComposerExecutableValidator + * + * @group automatic_updates + */ +class ComposerExecutableValidatorTest extends KernelTestBase { + + use ValidationTestTrait; + + /** + * {@inheritdoc} + */ + protected static $modules = ['automatic_updates']; + + /** + * Tests that an error is raised if the Composer executable isn't found. + */ + public function testErrorIfComposerNotFound(): void { + $exception = new IOException("This is your regularly scheduled error."); + + // The executable finder throws an exception if it can't find the requested + // executable. + $exec_finder = $this->prophesize(ExecutableFinderInterface::class); + $exec_finder->find('composer') + ->willThrow($exception) + ->shouldBeCalled(); + $this->container->set('automatic_updates.exec_finder', $exec_finder->reveal()); + + // The validator should translate that exception into an error. + $error = ValidationResult::createError([ + $exception->getMessage(), + ]); + $results = $this->container->get('automatic_updates.readiness_validation_manager') + ->run() + ->getResults(); + $this->assertValidationResultsEqual([$error], $results); + } + +}