From c086051a5bfd6586a14fcc62e520e35f1f502200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Fri, 31 Jan 2025 11:26:59 -0500 Subject: [PATCH 1/5] Add an event subscriber for config export --- .../package_manager.services.yml | 1 + .../src/EventSubscriber/ConfigSubscriber.php | 47 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 core/modules/package_manager/src/EventSubscriber/ConfigSubscriber.php diff --git a/core/modules/package_manager/package_manager.services.yml b/core/modules/package_manager/package_manager.services.yml index 88a837631012..2d1cc482ad89 100644 --- a/core/modules/package_manager/package_manager.services.yml +++ b/core/modules/package_manager/package_manager.services.yml @@ -43,6 +43,7 @@ services: arguments: $appRoot: '%app.root%' Drupal\package_manager\FailureMarker: {} + Drupal\package_manager\EventSubscriber\ConfigSubscriber: {} Drupal\package_manager\EventSubscriber\UpdateDataSubscriber: {} Drupal\package_manager\EventSubscriber\ChangeLogger: calls: diff --git a/core/modules/package_manager/src/EventSubscriber/ConfigSubscriber.php b/core/modules/package_manager/src/EventSubscriber/ConfigSubscriber.php new file mode 100644 index 000000000000..e4392cef331b --- /dev/null +++ b/core/modules/package_manager/src/EventSubscriber/ConfigSubscriber.php @@ -0,0 +1,47 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\package_manager\EventSubscriber; + +use Drupal\Core\Config\ConfigEvents; +use Drupal\Core\Config\StorageTransformEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Event subscriber to remove executable paths from exported config. + * + * @internal + * This is an internal part of Package Manager and may be changed or removed + * at any time without warning. External code should not interact with this + * class. + */ +final class ConfigSubscriber implements EventSubscriberInterface { + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents(): array { + return [ + ConfigEvents::STORAGE_TRANSFORM_EXPORT => 'onExport', + ]; + } + + /** + * Removes executable paths from exported config. + * + * @param \Drupal\Core\Config\StorageTransformEvent $event + * The event object. + */ + public function onExport(StorageTransformEvent $event): void { + $storage = $event->getStorage(); + + $settings = $storage->read('package_manager.settings'); + $storage['executables'] = [ + 'composer' => NULL, + 'rsync' => NULL, + ]; + $storage->write('package_manager.settings', $settings); + } + +} -- GitLab From cf60f88f73ad883de1e939045038e21e52c764f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Fri, 31 Jan 2025 11:32:55 -0500 Subject: [PATCH 2/5] Add install hook --- .../package_manager/package_manager.install | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/core/modules/package_manager/package_manager.install b/core/modules/package_manager/package_manager.install index 22d42a346ead..840eaf5b0c2d 100644 --- a/core/modules/package_manager/package_manager.install +++ b/core/modules/package_manager/package_manager.install @@ -12,8 +12,27 @@ use Drupal\package_manager\Exception\StageFailureMarkerException; use Drupal\package_manager\FailureMarker; use PhpTuf\ComposerStager\API\Exception\ExceptionInterface; +use PhpTuf\ComposerStager\API\Exception\LogicException; use PhpTuf\ComposerStager\API\Finder\Service\ExecutableFinderInterface; +/** + * Implements hook_install(). + */ +function package_manager_install(): void { + $executable_finder = \Drupal::service(ExecutableFinderInterface::class); + + $config = \Drupal::configFactory()->getEditable('package_manager.settings'); + foreach (['composer', 'rsync'] as $executable_name) { + try { + $config->set("executables.$executable_name", $executable_finder->find($executable_name)); + } + catch (LogicException) { + // Couldn't find the executable; don't change the config. + } + } + $config->save(); +} + /** * Implements hook_requirements(). */ -- GitLab From df0b089b8b9e154bd05f038d2f9d1378d48385cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Fri, 31 Jan 2025 11:51:46 -0500 Subject: [PATCH 3/5] Fix DefaultConfigTest --- core/tests/Drupal/KernelTests/Config/DefaultConfigTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/core/tests/Drupal/KernelTests/Config/DefaultConfigTest.php b/core/tests/Drupal/KernelTests/Config/DefaultConfigTest.php index fcdf72d1b8c3..13946e62a624 100644 --- a/core/tests/Drupal/KernelTests/Config/DefaultConfigTest.php +++ b/core/tests/Drupal/KernelTests/Config/DefaultConfigTest.php @@ -43,6 +43,7 @@ class DefaultConfigTest extends KernelTestBase { */ public static $skippedConfig = [ 'locale.settings' => ['path: '], + 'package_manager.settings' => ['composer: ', 'rsync: '], 'syslog.settings' => ['facility: '], ]; -- GitLab From bd04ab4a1bb6e9ddb4b30d8d51ae232c24820e12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Fri, 31 Jan 2025 12:03:44 -0500 Subject: [PATCH 4/5] Add a settings form --- .../package_manager.links.menu.yml | 5 ++ .../package_manager.routing.yml | 6 ++ .../package_manager/src/Form/SettingsForm.php | 64 +++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 core/modules/package_manager/package_manager.links.menu.yml create mode 100644 core/modules/package_manager/package_manager.routing.yml create mode 100644 core/modules/package_manager/src/Form/SettingsForm.php diff --git a/core/modules/package_manager/package_manager.links.menu.yml b/core/modules/package_manager/package_manager.links.menu.yml new file mode 100644 index 000000000000..65fe5e04146e --- /dev/null +++ b/core/modules/package_manager/package_manager.links.menu.yml @@ -0,0 +1,5 @@ +package_manager.settings: + title: 'Package Manager' + parent: system.admin_config_development + description: 'Configure Package Manager settings.' + route_name: package_manager.settings diff --git a/core/modules/package_manager/package_manager.routing.yml b/core/modules/package_manager/package_manager.routing.yml new file mode 100644 index 000000000000..e0e347c23b35 --- /dev/null +++ b/core/modules/package_manager/package_manager.routing.yml @@ -0,0 +1,6 @@ +package_manager.settings: + path: '/admin/config/development/package-manager' + defaults: + _form: 'Drupal\package_manager\Form\SettingsForm' + requirements: + _permission: 'administer software updates' diff --git a/core/modules/package_manager/src/Form/SettingsForm.php b/core/modules/package_manager/src/Form/SettingsForm.php new file mode 100644 index 000000000000..0b690c70cd95 --- /dev/null +++ b/core/modules/package_manager/src/Form/SettingsForm.php @@ -0,0 +1,64 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\package_manager\Form; + +use Drupal\Core\Form\ConfigFormBase; +use Drupal\Core\Form\ConfigTarget; +use Drupal\Core\Form\FormStateInterface; + +/** + * Defines a form to configure Package Manager settings. + * + * @internal + * This is an internal part of Package Manager and may be changed or removed + * at any time without warning. External code should not interact with this + * class. + */ +final class SettingsForm extends ConfigFormBase { + + /** + * {@inheritdoc} + */ + public function getFormId(): string { + return 'package_manager_settings_form'; + } + + /** + * {@inheritdoc} + */ + protected function getEditableConfigNames(): array { + return ['package_manager.settings']; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state): array { + $value_or_null = fn (string $value): ?string => trim($value) ?: NULL; + + $form['executables']['composer'] = [ + '#type' => 'textfield', + '#title' => $this->t('Path to Composer'), + '#description' => $this->t('The full path to the Composer executable (usually named <code>composer</code> or <code>composer.phar</code>. Leave blank to auto-detect.'), + '#config_target' => new ConfigTarget( + 'package_manager.settings', + 'executables.composer', + toConfig: $value_or_null, + ), + ]; + $form['executables']['rsync'] = [ + '#type' => 'textfield', + '#title' => $this->t('Path to rsync'), + '#description' => $this->t('The full path to the <code>rsync</code> executable. Leave blank to auto-detect.'), + '#config_target' => new ConfigTarget( + 'package_manager.settings', + 'executables.rsync', + toConfig: $value_or_null, + ), + ]; + return parent::buildForm($form, $form_state); + } + +} -- GitLab From 9da807205e14a4d29a618ac2164d8e0dea4a0875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=C3=A9na=20Proxima?= <adam@phenaproxima.net> Date: Fri, 31 Jan 2025 13:57:46 -0500 Subject: [PATCH 5/5] Add test of install hook and config export --- .../src/EventSubscriber/ConfigSubscriber.php | 2 +- .../tests/src/Kernel/ConfigTest.php | 59 +++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 core/modules/package_manager/tests/src/Kernel/ConfigTest.php diff --git a/core/modules/package_manager/src/EventSubscriber/ConfigSubscriber.php b/core/modules/package_manager/src/EventSubscriber/ConfigSubscriber.php index e4392cef331b..0155ded18883 100644 --- a/core/modules/package_manager/src/EventSubscriber/ConfigSubscriber.php +++ b/core/modules/package_manager/src/EventSubscriber/ConfigSubscriber.php @@ -37,7 +37,7 @@ public function onExport(StorageTransformEvent $event): void { $storage = $event->getStorage(); $settings = $storage->read('package_manager.settings'); - $storage['executables'] = [ + $settings['executables'] = [ 'composer' => NULL, 'rsync' => NULL, ]; diff --git a/core/modules/package_manager/tests/src/Kernel/ConfigTest.php b/core/modules/package_manager/tests/src/Kernel/ConfigTest.php new file mode 100644 index 000000000000..6e7b97c2bfad --- /dev/null +++ b/core/modules/package_manager/tests/src/Kernel/ConfigTest.php @@ -0,0 +1,59 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Tests\package_manager\Kernel; + +use Drupal\Core\Config\StorageManagerInterface; +use Drupal\Core\Extension\ModuleHandlerInterface; +use PhpTuf\ComposerStager\API\Finder\Service\ExecutableFinderInterface; + +/** + * @group package_manager + * @internal + */ +class ConfigTest extends PackageManagerKernelTestBase { + + /** + * Tests that Package Manager auto-detects executable paths on install. + */ + public function testExecutablePathsAutoDetection(): void { + $this->container->set(ExecutableFinderInterface::class, new class () implements ExecutableFinderInterface { + + /** + * {@inheritdoc} + */ + public function find(string $name): string { + return match ($name) { + 'composer' => '/fake/path/to/composer', + 'rsync' => '/fake/path/to/rsync', + }; + } + + }); + + $executables = $this->config('package_manager.settings') + ->get('executables'); + $this->assertNull($executables['composer']); + $this->assertNull($executables['rsync']); + + $this->container->get(ModuleHandlerInterface::class) + ->loadInclude('package_manager', 'install'); + package_manager_install(); + + $executables = $this->config('package_manager.settings') + ->get('executables'); + $this->assertSame('/fake/path/to/composer', $executables['composer']); + $this->assertSame('/fake/path/to/rsync', $executables['rsync']); + + // The executable paths should be removed from exported config. + /** @var \Drupal\Core\Config\StorageInterface $export */ + $export = $this->container->get(StorageManagerInterface::class) + ->getStorage(); + $exported_settings = $export->read('package_manager.settings'); + $this->assertIsArray($exported_settings); + $this->assertNull($exported_settings['executables']['composer']); + $this->assertNull($exported_settings['executables']['rsync']); + } + +} -- GitLab