From 15b871ae20dc49f20fa74feee5161df809d385db Mon Sep 17 00:00:00 2001 From: "kunal.sachdev" <kunal.sachdev@3685163.no-reply.drupal.org> Date: Tue, 5 Apr 2022 18:16:44 +0000 Subject: [PATCH] Issue #3253858 by kunal.sachdev, phenaproxima, tedbow: In ComposerExecutableValidator provide in explanation about how you can set the path to the Composer executable --- package_manager/package_manager.module | 2 +- package_manager/package_manager.services.yml | 1 + .../Validator/ComposerExecutableValidator.php | 54 +++++++++++++++---- .../ComposerExecutableValidatorTest.php | 35 ++++++++++++ 4 files changed, 80 insertions(+), 12 deletions(-) diff --git a/package_manager/package_manager.module b/package_manager/package_manager.module index c54269edfc..338a3ce447 100644 --- a/package_manager/package_manager.module +++ b/package_manager/package_manager.module @@ -17,7 +17,7 @@ function package_manager_help($route_name, RouteMatchInterface $route_match) { $output = '<h3>' . t('About') . '</h3>'; $output .= '<p>' . t('Package Manager is a framework for updating Drupal core and installing contributed modules and themes via Composer. It has no user interface, but it provides an API for creating a temporary copy of the current site, making changes to the copy, and then syncing those changes back into the live site.') . '</p>'; $output .= '<p>' . t('Package Manager dispatches events before and after various operations, and external code can integrate with it by subscribing to those events. For more information, see <code>package_manager.api.php</code>.') . '</p>'; - $output .= '<h3>' . t('Requirements') . '</h3>'; + $output .= '<h3 id="package-manager-requirements">' . t('Requirements') . '</h3>'; $output .= '<p>' . t('Package Manager requires Composer @version or later available as an executable, and PHP must have permission to run it. The path to the executable may be set in the <code>package_manager.settings:executables.composer</code> config setting, or it will be automatically detected.', ['@version' => ComposerExecutableValidator::MINIMUM_COMPOSER_VERSION]) . '</p>'; $output .= '<h3>' . t('Limitations') . '</h3>'; $output .= '<p>' . t("Because Package Manager modifies the current site's code base, it is intentionally limited in certain ways to prevent unexpected changes from being made to the live site:") . '</p>'; diff --git a/package_manager/package_manager.services.yml b/package_manager/package_manager.services.yml index 75ae35d2ec..3fa0bfa707 100644 --- a/package_manager/package_manager.services.yml +++ b/package_manager/package_manager.services.yml @@ -80,6 +80,7 @@ services: class: Drupal\package_manager\Validator\ComposerExecutableValidator arguments: - '@package_manager.composer_runner' + - '@module_handler' - '@string_translation' tags: - { name: event_subscriber } diff --git a/package_manager/src/Validator/ComposerExecutableValidator.php b/package_manager/src/Validator/ComposerExecutableValidator.php index f2e4e46157..06c7104e22 100644 --- a/package_manager/src/Validator/ComposerExecutableValidator.php +++ b/package_manager/src/Validator/ComposerExecutableValidator.php @@ -3,6 +3,8 @@ namespace Drupal\package_manager\Validator; use Composer\Semver\Comparator; +use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\Url; use Drupal\package_manager\Event\PreCreateEvent; use Drupal\package_manager\Event\PreOperationStageEvent; use Drupal\Core\StringTranslation\StringTranslationTrait; @@ -32,6 +34,13 @@ class ComposerExecutableValidator implements PreOperationStageValidatorInterface */ protected $composer; + /** + * The module handler service. + * + * @var \Drupal\Core\Extension\ModuleHandlerInterface + */ + protected $moduleHandler; + /** * The detected version of Composer. * @@ -44,11 +53,14 @@ class ComposerExecutableValidator implements PreOperationStageValidatorInterface * * @param \PhpTuf\ComposerStager\Domain\Process\Runner\ComposerRunnerInterface $composer * The Composer runner. + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler service. * @param \Drupal\Core\StringTranslation\TranslationInterface $translation * The translation service. */ - public function __construct(ComposerRunnerInterface $composer, TranslationInterface $translation) { + public function __construct(ComposerRunnerInterface $composer, ModuleHandlerInterface $module_handler, TranslationInterface $translation) { $this->composer = $composer; + $this->moduleHandler = $module_handler; $this->setStringTranslation($translation); } @@ -60,27 +72,47 @@ class ComposerExecutableValidator implements PreOperationStageValidatorInterface $this->composer->run(['--version'], $this); } catch (ExceptionInterface $e) { - $event->addError([ - $e->getMessage(), - ]); + $this->setError($e->getMessage(), $event); return; } if ($this->version) { if (Comparator::lessThan($this->version, static::MINIMUM_COMPOSER_VERSION)) { - $event->addError([ - $this->t('Composer @minimum_version or later is required, but version @detected_version was detected.', [ - '@minimum_version' => static::MINIMUM_COMPOSER_VERSION, - '@detected_version' => $this->version, - ]), + $message = $this->t('Composer @minimum_version or later is required, but version @detected_version was detected.', [ + '@minimum_version' => static::MINIMUM_COMPOSER_VERSION, + '@detected_version' => $this->version, ]); + $this->setError($message, $event); } } else { - $event->addError([ - $this->t('The Composer version could not be detected.'), + $this->setError($this->t('The Composer version could not be detected.'), $event); + } + } + + /** + * Flags a validation error. + * + * @param string $message + * The error message. If the Help module is enabled, a link to Package + * Manager's online documentation will be appended. + * @param \Drupal\package_manager\Event\PreOperationStageEvent $event + * The event object. + * + * @see package_manager_help() + */ + protected function setError(string $message, PreOperationStageEvent $event): void { + if ($this->moduleHandler->moduleExists('help')) { + $url = Url::fromRoute('help.page', ['name' => 'package_manager']) + ->setOption('fragment', 'package-manager-requirements') + ->toString(); + + $message .= ' '; + $message .= $this->t('See <a href=":package-manager-help">the help page</a> for information on how to configure the path to Composer.', [ + ':package-manager-help' => $url, ]); } + $event->addError([$message]); } /** diff --git a/package_manager/tests/src/Kernel/ComposerExecutableValidatorTest.php b/package_manager/tests/src/Kernel/ComposerExecutableValidatorTest.php index 9b8236a786..004e37e5b0 100644 --- a/package_manager/tests/src/Kernel/ComposerExecutableValidatorTest.php +++ b/package_manager/tests/src/Kernel/ComposerExecutableValidatorTest.php @@ -2,6 +2,7 @@ namespace Drupal\Tests\package_manager\Kernel; +use Drupal\Core\Url; use Drupal\package_manager\Event\PreCreateEvent; use Drupal\package_manager\Validator\ComposerExecutableValidator; use Drupal\package_manager\ValidationResult; @@ -35,6 +36,10 @@ class ComposerExecutableValidatorTest extends PackageManagerKernelTestBase { $exception->getMessage(), ]); $this->assertResults([$error], PreCreateEvent::class); + + $this->enableModules(['help']); + $this->container->set('package_manager.executable_finder', $exec_finder->reveal()); + $this->assertResultsWithHelp([$error], PreCreateEvent::class); } /** @@ -137,6 +142,36 @@ class ComposerExecutableValidatorTest extends PackageManagerKernelTestBase { // If the validator can't find a recognized, supported version of Composer, // it should produce errors. $this->assertResults($expected_results, PreCreateEvent::class); + + $this->enableModules(['help']); + $this->container->set('package_manager.composer_runner', $runner->reveal()); + $this->assertResultsWithHelp($expected_results, PreCreateEvent::class); + } + + /** + * Asserts that a set of validation results link to the Package Manager help. + * + * @param \Drupal\package_manager\ValidationResult[] $expected_results + * The expected validation results. + * @param string|null $event_class + * (optional) The class of the event which should return the results. Must + * be passed if $expected_results is not empty. + */ + private function assertResultsWithHelp(array $expected_results, string $event_class = NULL): void { + $url = Url::fromRoute('help.page', ['name' => 'package_manager']) + ->setOption('fragment', 'package-manager-requirements') + ->toString(); + + // Reformat the provided results so that they all have the link to the + // online documentation appended to them. + $map = function (string $message) use ($url): string { + return $message . ' See <a href="' . $url . '">the help page</a> for information on how to configure the path to Composer.'; + }; + foreach ($expected_results as $index => $result) { + $messages = array_map($map, $result->getMessages()); + $expected_results[$index] = ValidationResult::createError($messages); + } + $this->assertResults($expected_results, $event_class); } } -- GitLab