From 34ce371803b87a98a86ebad239e6facc61e0a535 Mon Sep 17 00:00:00 2001 From: "kunal.sachdev" <kunal.sachdev@3685163.no-reply.drupal.org> Date: Fri, 10 Jun 2022 02:19:03 +0000 Subject: [PATCH] Issue #3280168 by kunal.sachdev: StagedDatabaseUpdateValidator should check themes also --- automatic_updates.services.yml | 1 + .../StagedDatabaseUpdateValidator.php | 39 +++++++--- .../StagedDatabaseUpdateValidatorTest.php | 75 ++++++++++++------- .../automatic_updates_theme.info.yml | 5 ++ ...omatic_updates_theme_with_updates.info.yml | 5 ++ ...tomatic_updates_theme_with_updates.install | 8 ++ ...updates_theme_with_updates.post_update.php | 8 ++ 7 files changed, 104 insertions(+), 37 deletions(-) create mode 100644 tests/themes/automatic_updates_theme/automatic_updates_theme.info.yml create mode 100644 tests/themes/automatic_updates_theme_with_updates/automatic_updates_theme_with_updates.info.yml create mode 100644 tests/themes/automatic_updates_theme_with_updates/automatic_updates_theme_with_updates.install create mode 100644 tests/themes/automatic_updates_theme_with_updates/automatic_updates_theme_with_updates.post_update.php diff --git a/automatic_updates.services.yml b/automatic_updates.services.yml index 8ef9946555..cb01c1f435 100644 --- a/automatic_updates.services.yml +++ b/automatic_updates.services.yml @@ -108,6 +108,7 @@ services: arguments: - '@package_manager.path_locator' - '@extension.list.module' + - '@extension.list.theme' - '@string_translation' tags: - { name: event_subscriber } diff --git a/src/Validator/StagedDatabaseUpdateValidator.php b/src/Validator/StagedDatabaseUpdateValidator.php index 3c1cedd9d8..a585490d3d 100644 --- a/src/Validator/StagedDatabaseUpdateValidator.php +++ b/src/Validator/StagedDatabaseUpdateValidator.php @@ -6,6 +6,7 @@ use Drupal\automatic_updates\CronUpdater; use Drupal\automatic_updates\Updater; 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; @@ -37,6 +38,13 @@ class StagedDatabaseUpdateValidator implements EventSubscriberInterface { */ protected $moduleList; + /** + * The theme list service. + * + * @var \Drupal\Core\Extension\ThemeExtensionList + */ + protected $themeList; + /** * Constructs a StagedDatabaseUpdateValidator object. * @@ -44,12 +52,15 @@ class StagedDatabaseUpdateValidator implements EventSubscriberInterface { * 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\Core\StringTranslation\TranslationInterface $translation * The string translation service. */ - public function __construct(PathLocator $path_locator, ModuleExtensionList $module_list, TranslationInterface $translation) { + 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; $this->setStringTranslation($translation); } @@ -65,19 +76,23 @@ class StagedDatabaseUpdateValidator implements EventSubscriberInterface { return; } - $invalid_modules = []; + $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 modules so as not to rely on the // presence of StagedProjectsValidator. - foreach ($this->moduleList->getAllInstalledInfo() as $name => $info) { - if ($this->hasStagedUpdates($stage, $this->moduleList->get($name))) { - $invalid_modules[] = $info['name']; + foreach ($this->moduleList->getAllInstalledInfo() as $module_name => $module_info) { + if ($this->hasStagedUpdates($stage, $this->moduleList->get($module_name))) { + $invalid_extensions[] = $module_info['name']; } } - - if ($invalid_modules) { - $event->addError($invalid_modules, $this->t('The update cannot proceed because possible database updates have been detected in the following modules.')); + foreach ($this->themeList->getAllInstalledInfo() as $theme_name => $theme_info) { + if ($this->hasStagedUpdates($stage, $this->themeList->get($theme_name))) { + $invalid_extensions[] = $theme_info['name']; + } + } + if ($invalid_extensions) { + $event->addError($invalid_extensions, $this->t('The update cannot proceed because possible database updates have been detected in the following extensions.')); } } @@ -126,18 +141,18 @@ class StagedDatabaseUpdateValidator implements EventSubscriberInterface { * * @param string $root_dir * The root directory of the Drupal code base. - * @param \Drupal\Core\Extension\Extension $module + * @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 $module): array { + protected function getHashes(string $root_dir, Extension $extension): array { $path = implode(DIRECTORY_SEPARATOR, [ $root_dir, - $module->getPath(), - $module->getName(), + $extension->getPath(), + $extension->getName(), ]); $hashes = []; diff --git a/tests/src/Kernel/ReadinessValidation/StagedDatabaseUpdateValidatorTest.php b/tests/src/Kernel/ReadinessValidation/StagedDatabaseUpdateValidatorTest.php index aaf909d17c..8b294d01ae 100644 --- a/tests/src/Kernel/ReadinessValidation/StagedDatabaseUpdateValidatorTest.php +++ b/tests/src/Kernel/ReadinessValidation/StagedDatabaseUpdateValidatorTest.php @@ -49,12 +49,19 @@ class StagedDatabaseUpdateValidatorTest extends AutomaticUpdatesKernelTestBase { $virtual_active_dir = $this->container->get('package_manager.path_locator') ->getProjectRoot(); - // Copy the .install and .post_update.php files from every installed module, - // 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('module_handler')->getModuleList(); - foreach ($module_list as $name => $module) { - $path = $module->getPath(); + // 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'); + $extensions['automatic_updates_theme'] = $theme_list->get('automatic_updates_theme'); + $extensions['automatic_updates_theme_with_updates'] = $theme_list->get('automatic_updates_theme_with_updates'); + foreach ($extensions as $name => $extension) { + $path = $extension->getPath(); @mkdir("$virtual_active_dir/$path", 0777, TRUE); foreach (static::SUFFIXES as $suffix) { @@ -68,18 +75,23 @@ class StagedDatabaseUpdateValidatorTest extends AutomaticUpdatesKernelTestBase { * 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, it should not be - // installed. + // Since we're testing with a modified version of 'views' and + // 'automatic_updates_theme_with_updates', these should not be installed. $this->assertFalse($this->container->get('module_handler')->moduleExists('views')); + $this->assertFalse($this->container->get('theme_handler')->themeExists('automatic_updates_theme_with_updates')); - // Create bogus staged versions of Views' .install and .post_update.php - // files. Since it's not installed, the changes should not raise any - // validation errors. + // Create bogus staged versions of Views' and + // Automatic Updates Theme with Updates .install and .post_update.php files. + // Since these extensions are not installed, the changes should not raise + // any validation errors. $updater = $this->container->get('automatic_updates.cron_updater'); - $module_dir = $updater->getStageDirectory() . '/core/modules/views'; - mkdir($module_dir, 0777, TRUE); + $module_list = $this->container->get('extension.list.module')->getList(); + $theme_list = $this->container->get('extension.list.theme')->getList(); + $module_dir = $updater->getStageDirectory() . '/' . $module_list['views']->getPath(); + $theme_dir = $updater->getStageDirectory() . '/' . $theme_list['automatic_updates_theme_with_updates']->getPath(); foreach (static::SUFFIXES as $suffix) { file_put_contents("$module_dir/views.$suffix", $this->randomString()); + file_put_contents("$theme_dir/automatic_updates_theme_with_updates.$suffix", $this->randomString()); } $updater->apply(); @@ -112,19 +124,25 @@ class StagedDatabaseUpdateValidatorTest extends AutomaticUpdatesKernelTestBase { * @dataProvider providerFileChanged */ public function testFileChanged(string $suffix, bool $delete): void { - /** @var \Drupal\Tests\automatic_updates\Kernel\ReadinessValidation\TestCronUpdater $updater */ + /** @var \Drupal\automatic_updates\CronUpdater $updater */ $updater = $this->container->get('automatic_updates.cron_updater'); - - $file = $updater->getStageDirectory() . "/core/modules/system/system.$suffix"; + $theme_installer = $this->container->get('theme_installer'); + $theme_installer->install(['automatic_updates_theme_with_updates']); + $theme = $this->container->get('theme_handler') + ->getTheme('automatic_updates_theme_with_updates'); + $module_file = $updater->getStageDirectory() . "/core/modules/system/system.$suffix"; + $theme_file = $updater->getStageDirectory() . "/{$theme->getPath()}/automatic_updates_theme_with_updates.$suffix"; if ($delete) { - unlink($file); + unlink($module_file); + unlink($theme_file); } else { - file_put_contents($file, $this->randomString()); + file_put_contents($module_file, $this->randomString()); + file_put_contents($theme_file, $this->randomString()); } $expected_results = [ - ValidationResult::createError(['System'], t('The update cannot proceed because possible database updates have been detected in the following modules.')), + ValidationResult::createError(['System', 'Automatic Updates Theme With Updates'], t('The update cannot proceed because possible database updates have been detected in the following extensions.')), ]; try { @@ -142,21 +160,28 @@ class StagedDatabaseUpdateValidatorTest extends AutomaticUpdatesKernelTestBase { public function testUpdatesAddedInStage(): void { $module = $this->container->get('module_handler') ->getModule('package_manager_bypass'); + $theme_installer = $this->container->get('theme_installer'); + $theme_installer->install(['automatic_updates_theme']); + $theme = $this->container->get('theme_handler') + ->getTheme('automatic_updates_theme'); - /** @var \Drupal\Tests\automatic_updates\Kernel\ReadinessValidation\TestCronUpdater $updater */ + /** @var \Drupal\automatic_updates\CronUpdater $updater */ $updater = $this->container->get('automatic_updates.cron_updater'); foreach (static::SUFFIXES as $suffix) { - $file = sprintf('%s/%s/%s.%s', $updater->getStageDirectory(), $module->getPath(), $module->getName(), $suffix); - // The file we're creating shouldn't already exist in the staging area + $module_file = sprintf('%s/%s/%s.%s', $updater->getStageDirectory(), $module->getPath(), $module->getName(), $suffix); + $theme_file = sprintf('%s/%s/%s.%s', $updater->getStageDirectory(), $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($file); - file_put_contents($file, $this->randomString()); + $this->assertFileDoesNotExist($module_file); + $this->assertFileDoesNotExist($theme_file); + file_put_contents($module_file, $this->randomString()); + file_put_contents($theme_file, $this->randomString()); } $expected_results = [ - ValidationResult::createError(['Package Manager Bypass'], t('The update cannot proceed because possible database updates have been detected in the following modules.')), + ValidationResult::createError(['Package Manager Bypass', 'Automatic Updates Theme'], t('The update cannot proceed because possible database updates have been detected in the following extensions.')), ]; try { diff --git a/tests/themes/automatic_updates_theme/automatic_updates_theme.info.yml b/tests/themes/automatic_updates_theme/automatic_updates_theme.info.yml new file mode 100644 index 0000000000..3caac67b43 --- /dev/null +++ b/tests/themes/automatic_updates_theme/automatic_updates_theme.info.yml @@ -0,0 +1,5 @@ +name: Automatic Updates Theme +type: theme +description: 'Empty theme for tests.' +package: Testing +base theme: false diff --git a/tests/themes/automatic_updates_theme_with_updates/automatic_updates_theme_with_updates.info.yml b/tests/themes/automatic_updates_theme_with_updates/automatic_updates_theme_with_updates.info.yml new file mode 100644 index 0000000000..0f3e8b23f4 --- /dev/null +++ b/tests/themes/automatic_updates_theme_with_updates/automatic_updates_theme_with_updates.info.yml @@ -0,0 +1,5 @@ +name: Automatic Updates Theme With Updates +type: theme +description: 'Empty theme with updates for tests.' +package: Testing +base theme: false diff --git a/tests/themes/automatic_updates_theme_with_updates/automatic_updates_theme_with_updates.install b/tests/themes/automatic_updates_theme_with_updates/automatic_updates_theme_with_updates.install new file mode 100644 index 0000000000..c469c7bd16 --- /dev/null +++ b/tests/themes/automatic_updates_theme_with_updates/automatic_updates_theme_with_updates.install @@ -0,0 +1,8 @@ +<?php + +/** + * @file + * Blank .install file. + * + * @see \Drupal\Tests\automatic_updates\Kernel\ReadinessValidation\StagedDatabaseUpdateValidatorTest + */ diff --git a/tests/themes/automatic_updates_theme_with_updates/automatic_updates_theme_with_updates.post_update.php b/tests/themes/automatic_updates_theme_with_updates/automatic_updates_theme_with_updates.post_update.php new file mode 100644 index 0000000000..bb8046bdfc --- /dev/null +++ b/tests/themes/automatic_updates_theme_with_updates/automatic_updates_theme_with_updates.post_update.php @@ -0,0 +1,8 @@ +<?php + +/** + * @file + * Blank .post_update file. + * + * @see \Drupal\Tests\automatic_updates\Kernel\ReadinessValidation\StagedDatabaseUpdateValidatorTest + */ -- GitLab