diff --git a/automatic_updates.services.yml b/automatic_updates.services.yml index 35026ad3af65794180301b3cf470890b9e9b03bd..f135d322464ecbb8d9d87ea92e9068a310bad5e5 100644 --- a/automatic_updates.services.yml +++ b/automatic_updates.services.yml @@ -129,9 +129,7 @@ services: automatic_updates.validator.staged_database_updates: class: Drupal\automatic_updates\Validator\StagedDatabaseUpdateValidator arguments: - - '@package_manager.path_locator' - - '@extension.list.module' - - '@extension.list.theme' + - '@package_manager.validator.staged_database_updates' - '@string_translation' tags: - { name: event_subscriber } diff --git a/automatic_updates_extensions/src/Form/UpdateReady.php b/automatic_updates_extensions/src/Form/UpdateReady.php index b92452ffea0aa32eedc593d2e0c009b8f2052eea..88ed08ab588214f0405a3aae38807f7b2beab308 100644 --- a/automatic_updates_extensions/src/Form/UpdateReady.php +++ b/automatic_updates_extensions/src/Form/UpdateReady.php @@ -4,7 +4,7 @@ namespace Drupal\automatic_updates_extensions\Form; use Drupal\package_manager\Exception\ApplyFailedException; use Drupal\package_manager\ProjectInfo; -use Drupal\automatic_updates\Validator\StagedDatabaseUpdateValidator; +use Drupal\package_manager\Validator\StagedDBUpdateValidator; use Drupal\automatic_updates_extensions\BatchProcessor; use Drupal\automatic_updates\BatchProcessor as AutoUpdatesBatchProcessor; use Drupal\automatic_updates_extensions\ExtensionUpdater; @@ -52,7 +52,7 @@ final class UpdateReady extends FormBase { /** * The staged database update validator service. * - * @var \Drupal\automatic_updates\Validator\StagedDatabaseUpdateValidator + * @var \Drupal\package_manager\Validator\StagedDBUpdateValidator */ protected $stagedDatabaseUpdateValidator; @@ -74,12 +74,12 @@ final class UpdateReady extends FormBase { * The state service. * @param \Drupal\Core\Extension\ModuleExtensionList $module_list * The module list service. - * @param \Drupal\automatic_updates\Validator\StagedDatabaseUpdateValidator $staged_database_update_validator + * @param \Drupal\package_manager\Validator\StagedDBUpdateValidator $staged_database_update_validator * The staged database update validator service. * @param \Drupal\Core\Render\RendererInterface $renderer * The renderer service. */ - public function __construct(ExtensionUpdater $updater, MessengerInterface $messenger, StateInterface $state, ModuleExtensionList $module_list, StagedDatabaseUpdateValidator $staged_database_update_validator, RendererInterface $renderer) { + public function __construct(ExtensionUpdater $updater, MessengerInterface $messenger, StateInterface $state, ModuleExtensionList $module_list, StagedDBUpdateValidator $staged_database_update_validator, RendererInterface $renderer) { $this->updater = $updater; $this->setMessenger($messenger); $this->state = $state; @@ -104,7 +104,7 @@ final class UpdateReady extends FormBase { $container->get('messenger'), $container->get('state'), $container->get('extension.list.module'), - $container->get('automatic_updates.validator.staged_database_updates'), + $container->get('package_manager.validator.staged_database_updates'), $container->get('renderer') ); } diff --git a/package_manager/package_manager.services.yml b/package_manager/package_manager.services.yml index ad96bab6dcbef6a2ec8fec22753d0af26309b831..3db8249aacceea0fe0b3b78adeb599004eff6c7e 100644 --- a/package_manager/package_manager.services.yml +++ b/package_manager/package_manager.services.yml @@ -123,6 +123,14 @@ services: - '@package_manager.path_locator' tags: - { name: event_subscriber } + package_manager.validator.staged_database_updates: + class: Drupal\package_manager\Validator\StagedDBUpdateValidator + arguments: + - '@package_manager.path_locator' + - '@extension.list.module' + - '@extension.list.theme' + tags: + - { name: event_subscriber } package_manager.test_site_excluder: class: Drupal\package_manager\PathExcluder\TestSiteExcluder arguments: diff --git a/package_manager/src/Validator/StagedDBUpdateValidator.php b/package_manager/src/Validator/StagedDBUpdateValidator.php new file mode 100644 index 0000000000000000000000000000000000000000..d131f834bfe11df4e1752006c0ed6b9efe9e7cd1 --- /dev/null +++ b/package_manager/src/Validator/StagedDBUpdateValidator.php @@ -0,0 +1,185 @@ +<?php + +namespace Drupal\package_manager\Validator; + +use Drupal\Core\Extension\Extension; +use Drupal\Core\Extension\ModuleExtensionList; +use Drupal\Core\Extension\ThemeExtensionList; +use Drupal\Core\StringTranslation\StringTranslationTrait; +use Drupal\package_manager\Event\StatusCheckEvent; +use Drupal\package_manager\PathLocator; +use Drupal\package_manager\Stage; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Flags a warning if there are database updates in a staged update. + * + * @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. + */ +class StagedDBUpdateValidator implements EventSubscriberInterface { + + use StringTranslationTrait; + + /** + * The path locator service. + * + * @var \Drupal\package_manager\PathLocator + */ + protected $pathLocator; + + /** + * The module list service. + * + * @var \Drupal\Core\Extension\ModuleExtensionList + */ + protected $moduleList; + + /** + * The theme list service. + * + * @var \Drupal\Core\Extension\ThemeExtensionList + */ + protected $themeList; + + /** + * Constructs a StagedDBUpdateValidator object. + * + * @param \Drupal\package_manager\PathLocator $path_locator + * The path locator service. + * @param \Drupal\Core\Extension\ModuleExtensionList $module_list + * The module list service. + * @param \Drupal\Core\Extension\ThemeExtensionList $theme_list + * The theme list service. + */ + public function __construct(PathLocator $path_locator, ModuleExtensionList $module_list, ThemeExtensionList $theme_list) { + $this->pathLocator = $path_locator; + $this->moduleList = $module_list; + $this->themeList = $theme_list; + } + + /** + * Checks that the staged update does not have changes to its install files. + * + * @param \Drupal\package_manager\Event\StatusCheckEvent $event + * The event object. + */ + public function checkForStagedDatabaseUpdates(StatusCheckEvent $event): void { + $stage = $event->getStage(); + if ($stage->isAvailable()) { + // No staged updates exist, therefore we don't need to run this check. + return; + } + + $extensions_with_updates = $this->getExtensionsWithDatabaseUpdates($stage); + if ($extensions_with_updates) { + $event->addWarning($extensions_with_updates, $this->t('Possible database updates have been detected in the following extensions.')); + } + } + + /** + * Determines if a staged extension has changed update functions. + * + * @param \Drupal\package_manager\Stage $stage + * The updater which is controlling the update process. + * @param \Drupal\Core\Extension\Extension $extension + * The extension to check. + * + * @return bool + * TRUE if the staged copy of the extension has changed update functions + * compared to the active copy, FALSE otherwise. + * + * @todo Use a more sophisticated method to detect changes in the staged + * extension. Right now, we just compare hashes of the .install and + * .post_update.php files in both copies of the given extension, but this + * will cause false positives for changes to comments, whitespace, or + * runtime code like requirements checks. It would be preferable to use a + * static analyzer to detect new or changed functions that are actually + * executed during an update. No matter what, this method must NEVER cause + * false negatives, since that could result in code which is incompatible + * with the current database schema being copied to the active directory. + * + * @see https://www.drupal.org/project/automatic_updates/issues/3253828 + */ + public function hasStagedUpdates(Stage $stage, Extension $extension): bool { + $active_dir = $this->pathLocator->getProjectRoot(); + $stage_dir = $stage->getStageDirectory(); + + $web_root = $this->pathLocator->getWebRoot(); + if ($web_root) { + $active_dir .= DIRECTORY_SEPARATOR . $web_root; + $stage_dir .= DIRECTORY_SEPARATOR . $web_root; + } + + $active_hashes = $this->getHashes($active_dir, $extension); + $staged_hashes = $this->getHashes($stage_dir, $extension); + + return $active_hashes !== $staged_hashes; + } + + /** + * Returns hashes of the .install and .post-update.php files for a module. + * + * @param string $root_dir + * The root directory of the Drupal code base. + * @param \Drupal\Core\Extension\Extension $extension + * The module to check. + * + * @return string[] + * The hashes of the module's .install and .post_update.php files, in that + * order, if they exist. The array will be keyed by file extension. + */ + protected function getHashes(string $root_dir, Extension $extension): array { + $path = implode(DIRECTORY_SEPARATOR, [ + $root_dir, + $extension->getPath(), + $extension->getName(), + ]); + $hashes = []; + + foreach (['.install', '.post_update.php'] as $suffix) { + $file = $path . $suffix; + + if (file_exists($file)) { + $hashes[$suffix] = hash_file('sha256', $file); + } + } + return $hashes; + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() { + return [ + StatusCheckEvent::class => 'checkForStagedDatabaseUpdates', + ]; + } + + /** + * Gets extensions that have database updates. + * + * @param \Drupal\package_manager\Stage $stage + * The stage. + * + * @return string[] + * The names of the extensions that have possible database updates. + */ + public function getExtensionsWithDatabaseUpdates(Stage $stage): array { + $extensions_with_updates = []; + // Check all installed extensions for database updates. + $lists = [$this->moduleList, $this->themeList]; + foreach ($lists as $list) { + foreach ($list->getAllInstalledInfo() as $name => $info) { + if ($this->hasStagedUpdates($stage, $list->get($name))) { + $extensions_with_updates[] = $info['name']; + } + } + } + + return $extensions_with_updates; + } + +} diff --git a/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php b/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php index d6d06a90e510f03642388c2697ba12985a0d2687..a46ba08a35eef8f2257fe208f411fd36118b5b0b 100644 --- a/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php +++ b/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php @@ -177,9 +177,12 @@ abstract class PackageManagerKernelTestBase extends KernelTestBase { * * @param \Drupal\package_manager\ValidationResult[] $expected_results * The expected validation results. + * @param \Drupal\package_manager\Stage|null $stage + * (optional) The stage to use to create the status check event. If none is + * provided a new stage will be created. */ - protected function assertStatusCheckResults(array $expected_results): void { - $event = new StatusCheckEvent($this->createStage()); + protected function assertStatusCheckResults(array $expected_results, Stage $stage = NULL): void { + $event = new StatusCheckEvent($stage ?? $this->createStage()); $this->container->get('event_dispatcher')->dispatch($event); $this->assertValidationResultsEqual($expected_results, $event->getResults()); } diff --git a/package_manager/tests/src/Kernel/ReadinessValidation/StagedDBUpdateValidatorTest.php b/package_manager/tests/src/Kernel/ReadinessValidation/StagedDBUpdateValidatorTest.php new file mode 100644 index 0000000000000000000000000000000000000000..77a67f54ae816ae9176367122ab3aeb2228a7d67 --- /dev/null +++ b/package_manager/tests/src/Kernel/ReadinessValidation/StagedDBUpdateValidatorTest.php @@ -0,0 +1,166 @@ +<?php + +namespace Drupal\Tests\package_manager\Kernel\ReadinessValidation; + +use Drupal\package_manager\ValidationResult; +use Drupal\Tests\package_manager\Kernel\PackageManagerKernelTestBase; + +/** + * @covers \Drupal\package_manager\Validator\StagedDBUpdateValidator + * + * @group package_manager + */ +class StagedDBUpdateValidatorTest extends PackageManagerKernelTestBase { + + /** + * The suffixes of the files that can contain database updates. + * + * @var string[] + */ + private const SUFFIXES = ['install', 'post_update.php']; + + /** + * {@inheritdoc} + */ + protected function createVirtualProject(?string $source_dir = NULL): void { + parent::createVirtualProject($source_dir); + + $drupal_root = $this->getDrupalRoot(); + $virtual_active_dir = $this->container->get('package_manager.path_locator') + ->getProjectRoot(); + + // Copy the .install and .post_update.php files from all extensions used in + // this test class, in the *actual* Drupal code base that is running this + // test, into the virtual project (i.e., the active directory). + $module_list = $this->container->get('extension.list.module'); + $extensions = []; + $extensions['system'] = $module_list->get('system'); + $extensions['views'] = $module_list->get('views'); + $extensions['package_manager_bypass'] = $module_list->get('package_manager_bypass'); + $theme_list = $this->container->get('extension.list.theme'); + // Theme with updates. + $extensions['olivero'] = $theme_list->get('olivero'); + // Theme without updates. + $extensions['stark'] = $theme_list->get('stark'); + foreach ($extensions as $name => $extension) { + $path = $extension->getPath(); + @mkdir("$virtual_active_dir/$path", 0777, TRUE); + + foreach (static::SUFFIXES as $suffix) { + if ($name === 'olivero') { + @touch("$virtual_active_dir/$path/$name.$suffix"); + continue; + } + // If the source file doesn't exist, silence the warning it raises. + @copy("$drupal_root/$path/$name.$suffix", "$virtual_active_dir/$path/$name.$suffix"); + } + } + } + + /** + * Data provider for testFileChanged(). + * + * @return mixed[] + * The test cases. + */ + public function providerFileChanged(): array { + $scenarios = []; + foreach (static::SUFFIXES as $suffix) { + $scenarios["$suffix kept"] = [$suffix, FALSE]; + $scenarios["$suffix deleted"] = [$suffix, TRUE]; + } + return $scenarios; + } + + /** + * Tests that an error is raised if install or post-update files are changed. + * + * @param string $suffix + * The suffix of the file to change. Can be either 'install' or + * 'post_update.php'. + * @param bool $delete + * Whether or not to delete the file. + * + * @dataProvider providerFileChanged + */ + public function testFileChanged(string $suffix, bool $delete): void { + $stage = $this->createStage(); + $stage->create(); + $dir = $stage->getStageDirectory(); + $this->container->get('theme_installer')->install(['olivero']); + $theme = $this->container->get('theme_handler') + ->getTheme('olivero'); + $module_file = "$dir/core/modules/system/system.$suffix"; + $theme_file = "$dir/{$theme->getPath()}/{$theme->getName()}.$suffix"; + if ($delete) { + unlink($module_file); + unlink($theme_file); + } + else { + file_put_contents($module_file, $this->randomString()); + file_put_contents($theme_file, $this->randomString()); + } + $error = ValidationResult::createWarning(['System', 'Olivero'], t('Possible database updates have been detected in the following extensions.')); + $this->assertStatusCheckResults([$error], $stage); + + } + + /** + * Tests that no errors are raised if staged files have no DB updates. + */ + public function testNoUpdates(): void { + // Since we're testing with a modified version of 'views' and + // 'olivero', these should not be installed. + $this->assertFalse($this->container->get('module_handler')->moduleExists('views')); + $this->assertFalse($this->container->get('theme_handler')->themeExists('olivero')); + + // Create bogus staged versions of Views' and + // Package Manager Theme with Updates .install and .post_update.php + // files. Since these extensions are not installed, the changes should not + // raise any validation errors. + $stage = $this->createStage(); + $stage->create(); + $dir = $stage->getStageDirectory(); + $module_list = $this->container->get('extension.list.module')->getList(); + $theme_list = $this->container->get('extension.list.theme')->getList(); + $module_dir = $dir . '/' . $module_list['views']->getPath(); + $theme_dir = $dir . '/' . $theme_list['olivero']->getPath(); + foreach (static::SUFFIXES as $suffix) { + file_put_contents("$module_dir/views.$suffix", $this->randomString()); + file_put_contents("$theme_dir/olivero.$suffix", $this->randomString()); + } + // There should not have been any errors. + $this->assertStatusCheckResults([], $stage); + } + + /** + * Tests that an error is raised if install or post-update files are added. + */ + public function testUpdatesAddedInStage(): void { + $module = $this->container->get('module_handler') + ->getModule('package_manager_bypass'); + $theme_installer = $this->container->get('theme_installer'); + $theme_installer->install(['stark']); + $theme = $this->container->get('theme_handler') + ->getTheme('stark'); + + $stage = $this->createStage(); + $stage->create(); + $dir = $stage->getStageDirectory(); + foreach (static::SUFFIXES as $suffix) { + $module_file = sprintf('%s/%s/%s.%s', $dir, $module->getPath(), $module->getName(), $suffix); + $theme_file = sprintf('%s/%s/%s.%s', $dir, $theme->getPath(), $theme->getName(), $suffix); + // The files we're creating shouldn't already exist in the staging area + // unless it's a file we actually ship, which is a scenario covered by + // ::testFileChanged(). + $this->assertFileDoesNotExist($module_file); + $this->assertFileDoesNotExist($theme_file); + file_put_contents($module_file, $this->randomString()); + file_put_contents($theme_file, $this->randomString()); + } + $error = ValidationResult::createWarning(['Package Manager Bypass', 'Stark'], t('Possible database updates have been detected in the following extensions.')); + + $this->assertStatusCheckResults([$error], $stage); + } + +} diff --git a/src/Form/UpdateReady.php b/src/Form/UpdateReady.php index f77b2ece8bdf574380cd4c731455aa79eb180113..0f33929e617b515e5f50bd43a011b8fde784b36a 100644 --- a/src/Form/UpdateReady.php +++ b/src/Form/UpdateReady.php @@ -5,7 +5,7 @@ namespace Drupal\automatic_updates\Form; use Drupal\automatic_updates\BatchProcessor; use Drupal\automatic_updates\Updater; use Drupal\automatic_updates\Validation\ReadinessTrait; -use Drupal\automatic_updates\Validator\StagedDatabaseUpdateValidator; +use Drupal\package_manager\Validator\StagedDBUpdateValidator; use Drupal\Core\Batch\BatchBuilder; use Drupal\Core\Extension\ModuleExtensionList; use Drupal\Core\Form\FormBase; @@ -55,7 +55,7 @@ final class UpdateReady extends FormBase { /** * The staged database update validator service. * - * @var \Drupal\automatic_updates\Validator\StagedDatabaseUpdateValidator + * @var \Drupal\package_manager\Validator\StagedDBUpdateValidator */ protected $stagedDatabaseUpdateValidator; @@ -84,14 +84,14 @@ final class UpdateReady extends FormBase { * The state service. * @param \Drupal\Core\Extension\ModuleExtensionList $module_list * The module list service. - * @param \Drupal\automatic_updates\Validator\StagedDatabaseUpdateValidator $staged_database_update_validator + * @param \Drupal\package_manager\Validator\StagedDBUpdateValidator $staged_database_update_validator * The staged database update validator service. * @param \Drupal\Core\Render\RendererInterface $renderer * The renderer service. * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher * Event dispatcher service. */ - public function __construct(Updater $updater, MessengerInterface $messenger, StateInterface $state, ModuleExtensionList $module_list, StagedDatabaseUpdateValidator $staged_database_update_validator, RendererInterface $renderer, EventDispatcherInterface $event_dispatcher) { + public function __construct(Updater $updater, MessengerInterface $messenger, StateInterface $state, ModuleExtensionList $module_list, StagedDBUpdateValidator $staged_database_update_validator, RendererInterface $renderer, EventDispatcherInterface $event_dispatcher) { $this->updater = $updater; $this->setMessenger($messenger); $this->state = $state; @@ -117,7 +117,7 @@ final class UpdateReady extends FormBase { $container->get('messenger'), $container->get('state'), $container->get('extension.list.module'), - $container->get('automatic_updates.validator.staged_database_updates'), + $container->get('package_manager.validator.staged_database_updates'), $container->get('renderer'), $container->get('event_dispatcher') ); diff --git a/src/Validator/StagedDatabaseUpdateValidator.php b/src/Validator/StagedDatabaseUpdateValidator.php index fb386c6b4d698b5a5374694c468d9cb20e6bd150..70949123473aec21c2729f8836bd7c0b1e07d8b8 100644 --- a/src/Validator/StagedDatabaseUpdateValidator.php +++ b/src/Validator/StagedDatabaseUpdateValidator.php @@ -3,14 +3,10 @@ namespace Drupal\automatic_updates\Validator; use Drupal\automatic_updates\CronUpdater; -use Drupal\Core\Extension\Extension; -use Drupal\Core\Extension\ModuleExtensionList; -use Drupal\Core\Extension\ThemeExtensionList; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\StringTranslation\TranslationInterface; use Drupal\package_manager\Event\PreApplyEvent; -use Drupal\package_manager\PathLocator; -use Drupal\package_manager\Stage; +use Drupal\package_manager\Validator\StagedDBUpdateValidator; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** @@ -26,42 +22,22 @@ class StagedDatabaseUpdateValidator implements EventSubscriberInterface { use StringTranslationTrait; /** - * The path locator service. + * The Staged DB Update Validator service. * - * @var \Drupal\package_manager\PathLocator + * @var \Drupal\package_manager\Validator\StagedDBUpdateValidator */ - protected $pathLocator; - - /** - * The module list service. - * - * @var \Drupal\Core\Extension\ModuleExtensionList - */ - protected $moduleList; - - /** - * The theme list service. - * - * @var \Drupal\Core\Extension\ThemeExtensionList - */ - protected $themeList; + protected $stagedDBUpdateValidator; /** * Constructs a StagedDatabaseUpdateValidator object. * - * @param \Drupal\package_manager\PathLocator $path_locator - * The path locator service. - * @param \Drupal\Core\Extension\ModuleExtensionList $module_list - * The module list service. - * @param \Drupal\Core\Extension\ThemeExtensionList $theme_list - * The theme list service. + * @param \Drupal\package_manager\Validator\StagedDBUpdateValidator $staged_db_update_update_validator + * The Staged DB Update Validator service. * @param \Drupal\Core\StringTranslation\TranslationInterface $translation * The string translation service. */ - public function __construct(PathLocator $path_locator, ModuleExtensionList $module_list, ThemeExtensionList $theme_list, TranslationInterface $translation) { - $this->pathLocator = $path_locator; - $this->moduleList = $module_list; - $this->themeList = $theme_list; + public function __construct(StagedDBUpdateValidator $staged_db_update_update_validator, TranslationInterface $translation) { + $this->stagedDBUpdateValidator = $staged_db_update_update_validator; $this->setStringTranslation($translation); } @@ -77,82 +53,12 @@ class StagedDatabaseUpdateValidator implements EventSubscriberInterface { return; } - $invalid_extensions = $this->getExtensionsWithDatabaseUpdates($stage); + $invalid_extensions = $this->stagedDBUpdateValidator->getExtensionsWithDatabaseUpdates($stage); if ($invalid_extensions) { $event->addError($invalid_extensions, $this->t('The update cannot proceed because possible database updates have been detected in the following extensions.')); } } - /** - * Determines if a staged extension has changed update functions. - * - * @param \Drupal\package_manager\Stage $stage - * The updater which is controlling the update process. - * @param \Drupal\Core\Extension\Extension $extension - * The extension to check. - * - * @return bool - * TRUE if the staged copy of the extension has changed update functions - * compared to the active copy, FALSE otherwise. - * - * @todo Use a more sophisticated method to detect changes in the staged - * extension. Right now, we just compare hashes of the .install and - * .post_update.php files in both copies of the given extension, but this - * will cause false positives for changes to comments, whitespace, or - * runtime code like requirements checks. It would be preferable to use a - * static analyzer to detect new or changed functions that are actually - * executed during an update. No matter what, this method must NEVER cause - * false negatives, since that could result in code which is incompatible - * with the current database schema being copied to the active directory. - * - * @see https://www.drupal.org/project/automatic_updates/issues/3253828 - */ - public function hasStagedUpdates(Stage $stage, Extension $extension): bool { - $active_dir = $this->pathLocator->getProjectRoot(); - $stage_dir = $stage->getStageDirectory(); - - $web_root = $this->pathLocator->getWebRoot(); - if ($web_root) { - $active_dir .= DIRECTORY_SEPARATOR . $web_root; - $stage_dir .= DIRECTORY_SEPARATOR . $web_root; - } - - $active_hashes = $this->getHashes($active_dir, $extension); - $staged_hashes = $this->getHashes($stage_dir, $extension); - - return $active_hashes !== $staged_hashes; - } - - /** - * Returns hashes of the .install and .post-update.php files for a module. - * - * @param string $root_dir - * The root directory of the Drupal code base. - * @param \Drupal\Core\Extension\Extension $extension - * The module to check. - * - * @return string[] - * The hashes of the module's .install and .post_update.php files, in that - * order, if they exist. The array will be keyed by file extension. - */ - protected function getHashes(string $root_dir, Extension $extension): array { - $path = implode(DIRECTORY_SEPARATOR, [ - $root_dir, - $extension->getPath(), - $extension->getName(), - ]); - $hashes = []; - - foreach (['.install', '.post_update.php'] as $suffix) { - $file = $path . $suffix; - - if (file_exists($file)) { - $hashes[$suffix] = hash_file('sha256', $file); - } - } - return $hashes; - } - /** * {@inheritdoc} */ @@ -162,31 +68,4 @@ class StagedDatabaseUpdateValidator implements EventSubscriberInterface { ]; } - /** - * Get extensions that have database updates. - * - * @param \Drupal\package_manager\Stage $stage - * The stage. - * - * @return string[] - * The names of the extensions that have possible database updates. - */ - public function getExtensionsWithDatabaseUpdates(Stage $stage): array { - $invalid_extensions = []; - // Although \Drupal\automatic_updates\Validator\StagedProjectsValidator - // should prevent non-core modules from being added, updated, or removed in - // the staging area, we check all installed extensions so as not to rely on - // the presence of StagedProjectsValidator. - $lists = [$this->moduleList, $this->themeList]; - foreach ($lists as $list) { - foreach ($list->getAllInstalledInfo() as $name => $info) { - if ($this->hasStagedUpdates($stage, $list->get($name))) { - $invalid_extensions[] = $info['name']; - } - } - } - - return $invalid_extensions; - } - } diff --git a/tests/modules/automatic_updates_test/src/AutomaticUpdatesTestServiceProvider.php b/tests/modules/automatic_updates_test/src/AutomaticUpdatesTestServiceProvider.php index 3dfd3dd79d0d04e0b5abfcd182ceb929de5a6ae7..0d2691a94fcb312b363ab6250e00aef0a1c547ca 100644 --- a/tests/modules/automatic_updates_test/src/AutomaticUpdatesTestServiceProvider.php +++ b/tests/modules/automatic_updates_test/src/AutomaticUpdatesTestServiceProvider.php @@ -17,7 +17,7 @@ class AutomaticUpdatesTestServiceProvider extends ServiceProviderBase { public function alter(ContainerBuilder $container) { parent::alter($container); - $service_id = 'automatic_updates.validator.staged_database_updates'; + $service_id = 'package_manager.validator.staged_database_updates'; if ($container->hasDefinition($service_id)) { $container->getDefinition($service_id) ->setClass(StagedDatabaseUpdateValidator::class) diff --git a/tests/modules/automatic_updates_test/src/StagedDatabaseUpdateValidator.php b/tests/modules/automatic_updates_test/src/StagedDatabaseUpdateValidator.php index e2abcea63e7a3b1936c448d5e59617e417bb56ee..d4725297070246438f055051a50ba02d375c077f 100644 --- a/tests/modules/automatic_updates_test/src/StagedDatabaseUpdateValidator.php +++ b/tests/modules/automatic_updates_test/src/StagedDatabaseUpdateValidator.php @@ -2,7 +2,7 @@ namespace Drupal\automatic_updates_test; -use Drupal\automatic_updates\Validator\StagedDatabaseUpdateValidator as BaseValidator; +use Drupal\package_manager\Validator\StagedDBUpdateValidator as BaseValidator; use Drupal\Core\Extension\Extension; use Drupal\Core\State\StateInterface; use Drupal\package_manager\Stage;