From de0c72c82fe828f54f9f2931bac2b193834cdb13 Mon Sep 17 00:00:00 2001 From: Adam G-H <32250-phenaproxima@users.noreply.drupalcode.org> Date: Sun, 4 Jun 2023 21:06:15 +0000 Subject: [PATCH] Issue #3363938 by phenaproxima, tedbow: Package Manager should ignore default.settings.php and default.services.yml --- automatic_updates.services.yml | 5 - .../SiteConfigurationExcluder.php | 99 ++++- .../ScaffoldFilePermissionsValidator.php | 134 ------- tests/src/Build/CoreUpdateTest.php | 23 +- .../ScaffoldFilePermissionsValidatorTest.php | 355 ------------------ .../Kernel/StatusCheck/StatusCheckerTest.php | 16 +- 6 files changed, 110 insertions(+), 522 deletions(-) delete mode 100644 src/Validator/ScaffoldFilePermissionsValidator.php delete mode 100644 tests/src/Kernel/StatusCheck/ScaffoldFilePermissionsValidatorTest.php diff --git a/automatic_updates.services.yml b/automatic_updates.services.yml index 8e0a83cc13..9b29fe9166 100644 --- a/automatic_updates.services.yml +++ b/automatic_updates.services.yml @@ -79,11 +79,6 @@ services: tags: - { name: event_subscriber } Drupal\automatic_updates\Validator\VersionPolicyValidator: '@automatic_updates.validator.version_policy' - automatic_updates.validator.scaffold_file_permissions: - class: Drupal\automatic_updates\Validator\ScaffoldFilePermissionsValidator - tags: - - { name: event_subscriber } - Drupal\automatic_updates\Validator\ScaffoldFilePermissionsValidator: '@automatic_updates.validator.scaffold_file_permissions' automatic_updates.validator.cron_server: class: Drupal\automatic_updates\Validator\CronServerValidator tags: diff --git a/package_manager/src/PathExcluder/SiteConfigurationExcluder.php b/package_manager/src/PathExcluder/SiteConfigurationExcluder.php index 7a4df3b2d0..1745d92f4e 100644 --- a/package_manager/src/PathExcluder/SiteConfigurationExcluder.php +++ b/package_manager/src/PathExcluder/SiteConfigurationExcluder.php @@ -4,7 +4,12 @@ declare(strict_types = 1); namespace Drupal\package_manager\PathExcluder; +use Drupal\Core\File\Exception\FileException; +use Drupal\Core\File\FileSystemInterface; use Drupal\package_manager\Event\CollectPathsToExcludeEvent; +use Drupal\package_manager\Event\PostCreateEvent; +use Drupal\package_manager\Event\PreApplyEvent; +use Drupal\package_manager\PathLocator; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** @@ -22,8 +27,16 @@ class SiteConfigurationExcluder implements EventSubscriberInterface { * * @param string $sitePath * The current site path, relative to the Drupal root. + * @param \Drupal\package_manager\PathLocator $pathLocator + * The path locator service. + * @param \Drupal\Core\File\FileSystemInterface $fileSystem + * The file system service. */ - public function __construct(protected string $sitePath) {} + public function __construct( + protected string $sitePath, + private readonly PathLocator $pathLocator, + private readonly FileSystemInterface $fileSystem + ) {} /** * Excludes site configuration files from stage operations. @@ -32,8 +45,11 @@ class SiteConfigurationExcluder implements EventSubscriberInterface { * The event object. */ public function excludeSiteConfiguration(CollectPathsToExcludeEvent $event): void { - // Site configuration files are always excluded relative to the web root. - $paths = []; + // These two files are never relevant to existing sites. + $paths = [ + 'sites/default/default.settings.php', + 'sites/default/default.services.yml', + ]; // Exclude site-specific settings files, which are always in the web root. // By default, Drupal core will always try to write-protect these files. @@ -46,15 +62,92 @@ class SiteConfigurationExcluder implements EventSubscriberInterface { $paths[] = $this->sitePath . '/' . $settings_file; $paths[] = 'sites/default/' . $settings_file; } + // Site configuration files are always excluded relative to the web root. $event->addPathsRelativeToWebRoot($paths); } + /** + * Makes the staged `sites/default` directory world-writable. + * + * This is done to allow the core scaffold plugin to make changes in + * `sites/default`, if necessary, without breaking if `sites/default` is not + * writable (this can happen because rsync preserves directory permissions, + * and Drupal will try to harden the site directory's permissions as much as + * possible). We specifically exclude the `default.settings.php` and + * `default.services.yml` files from Package Manager operations, so we want to + * allow the scaffold plugin to make whatever changes it wants to those files + * in the stage directory. + * + * @param \Drupal\package_manager\Event\PostCreateEvent $event + * The event being handled. + * + * @see ::excludeSiteConfiguration() + */ + public function makeDefaultSiteDirectoryWritable(PostCreateEvent $event): void { + $dir = $this->getDefaultSiteDirectoryPath($event->stage->getStageDirectory()); + // If the directory doesn't even exist, there's nothing to do here. + if (!is_dir($dir)) { + return; + } + if (!$this->fileSystem->chmod($dir, 0777)) { + throw new FileException("Could not change permissions on '$dir'."); + } + } + + /** + * Makes `sites/default` permissions the same in live and stage directories. + * + * @param \Drupal\package_manager\Event\PreApplyEvent $event + * The event being handled. + * + * @throws \Drupal\Core\File\Exception\FileException + * If the permissions of the live `sites/default` cannot be determined, or + * cannot be changed on the staged `sites/default`. + */ + public function syncDefaultSiteDirectoryPermissions(PreApplyEvent $event): void { + $staged_dir = $this->getDefaultSiteDirectoryPath($event->stage->getStageDirectory()); + // If the directory doesn't even exist, there's nothing to do here. + if (!is_dir($staged_dir)) { + return; + } + $live_dir = $this->getDefaultSiteDirectoryPath($this->pathLocator->getProjectRoot()); + + $permissions = fileperms($live_dir); + if ($permissions === FALSE) { + throw new FileException("Could not determine permissions for '$live_dir'."); + } + + if (!$this->fileSystem->chmod($staged_dir, $permissions)) { + throw new FileException("Could not change permissions on '$staged_dir'."); + } + } + + /** + * Returns the full path to `sites/default`, relative to a root directory. + * + * @param string $root_dir + * The root directory. + * + * @return string + * The full path to `sites/default` within the given root directory. + */ + private function getDefaultSiteDirectoryPath(string $root_dir): string { + $dir = [$root_dir]; + $web_root = $this->pathLocator->getWebRoot(); + if ($web_root) { + $dir[] = $web_root; + } + return implode(DIRECTORY_SEPARATOR, [...$dir, 'sites', 'default']); + } + /** * {@inheritdoc} */ public static function getSubscribedEvents(): array { return [ CollectPathsToExcludeEvent::class => 'excludeSiteConfiguration', + PostCreateEvent::class => 'makeDefaultSiteDirectoryWritable', + PreApplyEvent::class => 'syncDefaultSiteDirectoryPermissions', ]; } diff --git a/src/Validator/ScaffoldFilePermissionsValidator.php b/src/Validator/ScaffoldFilePermissionsValidator.php deleted file mode 100644 index e8d3b8639b..0000000000 --- a/src/Validator/ScaffoldFilePermissionsValidator.php +++ /dev/null @@ -1,134 +0,0 @@ -<?php - -declare(strict_types = 1); - -namespace Drupal\automatic_updates\Validator; - -use Drupal\automatic_updates\UpdateStage; -use Drupal\Core\StringTranslation\StringTranslationTrait; -use Drupal\package_manager\ComposerInspector; -use Drupal\package_manager\Event\PreApplyEvent; -use Drupal\package_manager\Event\PreCreateEvent; -use Drupal\package_manager\Event\PreOperationStageEvent; -use Drupal\package_manager\Event\StatusCheckEvent; -use Drupal\package_manager\PathLocator; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; - -/** - * Validates that scaffold files have appropriate permissions. - * - * @internal - * This is an internal part of Automatic Updates and may be changed or removed - * at any time without warning. External code should not interact with this - * class. - */ -final class ScaffoldFilePermissionsValidator implements EventSubscriberInterface { - - use StringTranslationTrait; - - /** - * Constructs a ScaffoldFilePermissionsValidator object. - * - * @param \Drupal\package_manager\ComposerInspector $composerInspector - * The Composer inspector service. - * @param \Drupal\package_manager\PathLocator $pathLocator - * The path locator service. - */ - public function __construct( - private readonly ComposerInspector $composerInspector, - private readonly PathLocator $pathLocator, - ) {} - - /** - * Validates that scaffold files have the appropriate permissions. - */ - public function validate(PreOperationStageEvent $event): void { - // We only want to do this check if the stage belongs to Automatic Updates. - if (!$event->stage instanceof UpdateStage) { - return; - } - $paths = []; - - // Figure out the absolute path of `sites/default`. - $site_dir = $this->pathLocator->getProjectRoot(); - $web_root = $this->pathLocator->getWebRoot(); - if ($web_root) { - $site_dir .= '/' . $web_root; - } - $site_dir .= '/sites/default'; - - $active_scaffold_files = $this->getDefaultSiteFilesFromScaffold($this->pathLocator->getProjectRoot()); - - // If the active directory and stage directory have different files - // scaffolded into `sites/default` (i.e., files were added, renamed, or - // deleted), the site directory itself must be writable for the changes to - // be applied. - if ($event instanceof PreApplyEvent) { - $staged_scaffold_files = $this->getDefaultSiteFilesFromScaffold($event->stage->getStageDirectory()); - - if ($active_scaffold_files !== $staged_scaffold_files) { - $paths[] = $site_dir; - } - } - // The scaffolded files themselves must be writable, so that any changes to - // them in the stage directory can be synced back to the active directory. - foreach ($active_scaffold_files as $scaffold_file) { - $paths[] = $site_dir . '/' . $scaffold_file; - } - - // Flag messages about anything in $paths which exists, but isn't writable. - $non_writable_files = array_filter($paths, function (string $path): bool { - return file_exists($path) && !is_writable($path); - }); - if ($non_writable_files) { - // Re-key the messages in order to prevent false negative comparisons in - // tests. - $non_writable_files = array_map($this->t(...), array_values($non_writable_files)); - $event->addError($non_writable_files, $this->t('The following paths must be writable in order to update default site configuration files.')); - } - } - - /** - * Returns the list of file names scaffolded into `sites/default`. - * - * @param string $working_dir - * The directory in which to run Composer. - * - * @return string[] - * The names of files that are scaffolded into `sites/default`, stripped - * of the preceding path. For example, - * `[web-root]/sites/default/default.settings.php` will be - * `default.settings.php`. Will be sorted alphabetically. If the target - * directory doesn't have the `drupal/core` package installed, the returned - * array will be empty. - */ - protected function getDefaultSiteFilesFromScaffold(string $working_dir): array { - $installed = $this->composerInspector->getInstalledPackagesList($working_dir); - - if (isset($installed['drupal/core'])) { - // We expect Drupal core to provide a list of scaffold files. - $files = (array) json_decode($this->composerInspector->getConfig('extra.drupal-scaffold.file-mapping', $installed['drupal/core']->path . '/composer.json')); - } - else { - $files = []; - } - $files = array_keys($files); - $files = preg_grep('/sites\/default\//', $files); - $files = array_map('basename', $files); - sort($files); - - return $files; - } - - /** - * {@inheritdoc} - */ - public static function getSubscribedEvents(): array { - return [ - PreCreateEvent::class => 'validate', - PreApplyEvent::class => 'validate', - StatusCheckEvent::class => 'validate', - ]; - } - -} diff --git a/tests/src/Build/CoreUpdateTest.php b/tests/src/Build/CoreUpdateTest.php index 76073cbf0d..8b3c3d4877 100644 --- a/tests/src/Build/CoreUpdateTest.php +++ b/tests/src/Build/CoreUpdateTest.php @@ -89,9 +89,6 @@ class CoreUpdateTest extends UpdateTestBase { // Ensure that Drupal has write-protected the site directory. $this->assertDirectoryIsNotWritable($this->getWebRoot() . '/sites/default'); - // @todo Remove this when default.settings.php and default.services.yml are - // ignored by Package Manager in in https://drupal.org/i/3363938. - $this->assertTrue(chmod($this->getWebRoot() . '/sites/default', 0777)); } /** @@ -185,9 +182,6 @@ class CoreUpdateTest extends UpdateTestBase { $this->installModules(['dblog']); $this->visit('/admin/reports/status'); - // @todo Remove this line when default.settings.php and default.services.yml - // are ignored by Package Manager in in https://drupal.org/i/3363938. - $this->assertTrue(chmod($this->getWebRoot() . '/sites/default', 0777)); $mink = $this->getMink(); $page = $mink->getSession()->getPage(); $assert_session = $mink->assertSession(); @@ -239,9 +233,6 @@ class CoreUpdateTest extends UpdateTestBase { $assert_session = $mink->assertSession(); $this->coreUpdateTillUpdateReady($page); $this->visit('/admin/reports/status'); - // @todo Remove this line when default.settings.php and default.services.yml - // are ignored by Package Manager in https://drupal.org/i/3363938. - $this->assertTrue(chmod($this->getWebRoot() . '/sites/default', 0777)); $assert_session->pageTextContains('Your site is ready for automatic updates.'); $page->clickLink('Run cron'); $this->assertUpdateSuccessful('9.8.1'); @@ -309,12 +300,16 @@ class CoreUpdateTest extends UpdateTestBase { foreach (['default.settings.php', 'default.services.yml'] as $file) { $file = $web_root . '/sites/default/' . $file; $this->assertFileIsReadable($file); - $this->assertStringContainsString("# This is part of Drupal $expected_version.", file_get_contents($file)); + // The `default.settings.php` and `default.services.yml` files are + // explicitly excluded from Package Manager operations, since they are not + // relevant to existing sites. Therefore, ensure that the changes we made + // to the original (scaffold) versions of the files are not present in + // the updated site. + // @see \Drupal\package_manager\PathExcluder\SiteConfigurationExcluder() + // @see \Drupal\Tests\package_manager\Build\TemplateProjectTestBase::setUpstreamCoreVersion() + $this->assertStringNotContainsString("# This is part of Drupal $expected_version.", file_get_contents($file)); } - // @todo Restore this line when default.settings.php and - // default.services.yml are ignored by Package Manager in - // https://drupal.org/i/3363938. - // $this->assertDirectoryIsNotWritable("$web_root/sites/default"); + $this->assertDirectoryIsNotWritable("$web_root/sites/default"); $info = $this->runComposer('composer info --self --format json', 'project', TRUE); diff --git a/tests/src/Kernel/StatusCheck/ScaffoldFilePermissionsValidatorTest.php b/tests/src/Kernel/StatusCheck/ScaffoldFilePermissionsValidatorTest.php deleted file mode 100644 index 9236e3eca5..0000000000 --- a/tests/src/Kernel/StatusCheck/ScaffoldFilePermissionsValidatorTest.php +++ /dev/null @@ -1,355 +0,0 @@ -<?php - -declare(strict_types = 1); - -namespace Drupal\Tests\automatic_updates\Kernel\StatusCheck; - -use Drupal\automatic_updates\UpdateStage; -use Drupal\fixture_manipulator\ActiveFixtureManipulator; -use Drupal\package_manager\Exception\ApplyFailedException; -use Drupal\package_manager\Exception\StageEventException; -use Drupal\package_manager\PathLocator; -use Drupal\package_manager\ValidationResult; -use Drupal\Tests\automatic_updates\Kernel\AutomaticUpdatesKernelTestBase; - -/** - * @covers \Drupal\automatic_updates\Validator\ScaffoldFilePermissionsValidator - * @group automatic_updates - * @internal - */ -class ScaffoldFilePermissionsValidatorTest extends AutomaticUpdatesKernelTestBase { - - /** - * {@inheritdoc} - */ - protected static $modules = ['automatic_updates']; - - /** - * The active directory of the test project. - * - * @var string - */ - private $activeDir; - - /** - * {@inheritdoc} - */ - protected function setUp(): void { - parent::setUp(); - - $this->activeDir = $this->container->get(PathLocator::class) - ->getProjectRoot(); - } - - /** - * {@inheritdoc} - */ - protected function assertValidationResultsEqual(array $expected_results, array $actual_results, ?PathLocator $path_locator = NULL, ?string $stage_dir = NULL): void { - $map = function (string $path): string { - return $this->activeDir . '/' . $path; - }; - foreach ($expected_results as $i => $result) { - // Prepend the active directory to every path listed in the error result, - // and add the expected summary. - $messages = array_map($map, $result->messages); - $messages = array_map(t(...), $messages); - $expected_results[$i] = ValidationResult::createError($messages, t('The following paths must be writable in order to update default site configuration files.')); - } - parent::assertValidationResultsEqual($expected_results, $actual_results, $path_locator); - } - - /** - * Write-protects a set of paths in the active directory. - * - * @param string[] $paths - * The paths to write-protect, relative to the active directory. - */ - private function writeProtect(array $paths): void { - foreach ($paths as $path) { - $path = $this->activeDir . '/' . $path; - chmod($path, 0500); - $this->assertFileIsNotWritable($path, "Failed to write-protect $path."); - } - } - - /** - * Data provider for testPermissionsBeforeStart(). - * - * @return mixed[][] - * The test cases. - */ - public function providerPermissionsBeforeStart(): array { - return [ - 'write-protected scaffold file, writable site directory' => [ - ['sites/default/default.settings.php'], - [ - ValidationResult::createError([t('sites/default/default.settings.php')]), - ], - ], - // Whether the site directory is write-protected only matters during - // pre-apply, because it only presents a problem if scaffold files have - // been added or removed in the stage directory. Which is a condition we - // can only detect during pre-apply. - 'write-protected scaffold file and site directory' => [ - [ - 'sites/default/default.settings.php', - 'sites/default', - ], - [ - ValidationResult::createError([t('sites/default/default.settings.php')]), - ], - ], - 'write-protected site directory' => [ - ['sites/default'], - [], - ], - ]; - } - - /** - * Tests that scaffold file permissions are checked before an update begins. - * - * @param string[] $write_protected_paths - * A list of paths, relative to the project root, which should be write - * protected before staged changes are applied. - * @param \Drupal\package_manager\ValidationResult[] $expected_results - * The expected validation results, if any. - * - * @dataProvider providerPermissionsBeforeStart - */ - public function testPermissionsBeforeStart(array $write_protected_paths, array $expected_results): void { - $this->writeProtect($write_protected_paths); - $this->assertCheckerResultsFromManager($expected_results, TRUE); - - try { - $this->container->get(UpdateStage::class) - ->begin(['drupal' => '9.8.1']); - - // If no exception was thrown, ensure that we weren't expecting an error. - $this->assertEmpty($expected_results); - } - catch (StageEventException $e) { - $this->assertExpectedResultsFromException($expected_results, $e); - } - } - - /** - * Data provider for testScaffoldFilesChanged(). - * - * @return mixed[][] - * The test cases. - */ - public function providerScaffoldFilesChanged(): array { - return [ - // If no scaffold files are changed, it doesn't matter if the site - // directory is writable. - 'no scaffold changes, site directory not writable' => [ - ['sites/default'], - [], - [], - [], - ], - 'no scaffold changes, site directory writable' => [ - [], - [], - [], - [], - ], - // If scaffold files are added or deleted in the site directory, the site - // directory must be writable. - 'new scaffold file added to non-writable site directory' => [ - ['sites/default'], - [], - [ - '[web-root]/sites/default/new.txt' => '', - ], - [ - ValidationResult::createError([t('sites/default')]), - ], - ], - 'new scaffold file added to writable site directory' => [ - [], - [], - [ - '[web-root]/sites/default/new.txt' => '', - ], - [], - ], - 'writable scaffold file removed from non-writable site directory' => [ - ['sites/default'], - [ - '[web-root]/sites/default/deleted.txt' => '', - ], - [], - [ - ValidationResult::createError([t('sites/default')]), - ], - ], - 'writable scaffold file removed from writable site directory' => [ - [], - [ - '[web-root]/sites/default/deleted.txt' => '', - ], - [], - [], - ], - 'non-writable scaffold file removed from non-writable site directory' => [ - [ - // The file must be made write-protected before the site directory is, - // or the permissions change will fail. - 'sites/default/deleted.txt', - 'sites/default', - ], - [ - '[web-root]/sites/default/deleted.txt' => '', - ], - [], - [ - ValidationResult::createError([ - t('sites/default'), - t('sites/default/deleted.txt'), - ], t('I summarize thee!')), - ], - ], - 'non-writable scaffold file removed from writable site directory' => [ - ['sites/default/deleted.txt'], - [ - '[web-root]/sites/default/deleted.txt' => '', - ], - [], - [ - ValidationResult::createError([t('sites/default/deleted.txt')]), - ], - ], - // If only scaffold files outside the site directory changed, the - // validator doesn't care if the site directory is writable. - 'new scaffold file added outside non-writable site directory' => [ - ['sites/default'], - [], - [ - '[web-root]/foo.html' => '', - ], - [], - ], - 'new scaffold file added outside writable site directory' => [ - [], - [], - [ - '[web-root]/foo.html' => '', - ], - [], - ], - 'writable scaffold file removed outside non-writable site directory' => [ - ['sites/default'], - [ - '[web-root]/foo.txt' => '', - ], - [], - [], - ], - 'writable scaffold file removed outside writable site directory' => [ - [], - [ - '[web-root]/foo.txt' => '', - ], - [], - [], - ], - 'non-writable scaffold file removed outside non-writable site directory' => [ - [ - 'sites/default', - 'foo.txt', - ], - [ - '[web-root]/foo.txt' => '', - ], - [], - [], - ], - 'non-writable scaffold file removed outside writable site directory' => [ - ['foo.txt'], - [ - '[web-root]/foo.txt' => '', - ], - [], - [], - ], - ]; - } - - /** - * Tests site directory permissions are checked before changes are applied. - * - * @param string[] $write_protected_paths - * A list of paths, relative to the project root, which should be write - * protected before staged changes are applied. - * @param string[] $active_scaffold_files - * An array simulating the extra.drupal-scaffold.file-mapping section of the - * active drupal/core package. - * @param string[] $staged_scaffold_files - * An array simulating the extra.drupal-scaffold.file-mapping section of the - * staged drupal/core package. - * @param \Drupal\package_manager\ValidationResult[] $expected_results - * The expected validation results, if any. - * - * @dataProvider providerScaffoldFilesChanged - */ - public function testScaffoldFilesChanged(array $write_protected_paths, array $active_scaffold_files, array $staged_scaffold_files, array $expected_results): void { - // Rewrite the active and staged composer.json files, inserting the given - // lists of scaffold files. - if ($active_scaffold_files) { - (new ActiveFixtureManipulator()) - ->modifyPackageConfig('drupal/core', '9.8.0', [ - 'extra' => [ - 'drupal-scaffold' => [ - 'file-mapping' => $active_scaffold_files, - ], - ], - ]) - ->commitChanges(); - } - $stage_manipulator = $this->getStageFixtureManipulator(); - $stage_manipulator->setVersion('drupal/core-recommended', '9.8.1'); - $stage_manipulator->setVersion('drupal/core-dev', '9.8.1'); - if ($staged_scaffold_files) { - $stage_manipulator->modifyPackageConfig('drupal/core', '9.8.1', [ - 'extra' => [ - 'drupal-scaffold' => [ - 'file-mapping' => $staged_scaffold_files, - ], - ], - ]); - } - else { - $stage_manipulator->setVersion('drupal/core', '9.8.1'); - } - - // Create fake scaffold files so we can test scenarios in which a scaffold - // file that exists in the active directory is deleted in the stage - // directory. - touch($this->activeDir . '/sites/default/deleted.txt'); - touch($this->activeDir . '/foo.txt'); - - $stage = $this->container->get(UpdateStage::class); - $stage->begin(['drupal' => '9.8.1']); - $stage->stage(); - - $this->writeProtect($write_protected_paths); - - try { - $stage->apply(); - - // If no exception was thrown, ensure that we weren't expecting an error. - $this->assertSame([], $expected_results); - } - // If we try to overwrite any write-protected paths, even if they're not - // scaffold files, we'll get an ApplyFailedException. - catch (ApplyFailedException $e) { - $this->assertStringStartsWith("Automatic updates failed to apply, and the site is in an indeterminate state. Consider restoring the code and database from a backup.", $e->getMessage()); - } - catch (StageEventException $e) { - $this->assertExpectedResultsFromException($expected_results, $e); - } - } - -} diff --git a/tests/src/Kernel/StatusCheck/StatusCheckerTest.php b/tests/src/Kernel/StatusCheck/StatusCheckerTest.php index 8ebddb72ae..ba2f79a055 100644 --- a/tests/src/Kernel/StatusCheck/StatusCheckerTest.php +++ b/tests/src/Kernel/StatusCheck/StatusCheckerTest.php @@ -7,7 +7,6 @@ namespace Drupal\Tests\automatic_updates\Kernel\StatusCheck; use Drupal\automatic_updates\CronUpdateStage; use Drupal\automatic_updates\UpdateStage; use Drupal\automatic_updates\Validation\StatusChecker; -use Drupal\automatic_updates\Validator\ScaffoldFilePermissionsValidator; use Drupal\automatic_updates\Validator\StagedProjectsValidator; use Drupal\automatic_updates_test\EventSubscriber\TestSubscriber1; use Drupal\automatic_updates_test_status_checker\EventSubscriber\TestSubscriber2; @@ -243,16 +242,11 @@ class StatusCheckerTest extends AutomaticUpdatesKernelTestBase { // results should be stored. $this->assertValidationResultsEqual($results, $manager->getResults()); - // Don't validate staged projects or scaffold file permissions because - // actual stage operations are bypassed by package_manager_bypass, which - // will make these validators complain that there is no actual Composer data - // for them to inspect. - $validators = array_map([$this->container, 'get'], [ - StagedProjectsValidator::class, - ScaffoldFilePermissionsValidator::class, - ]); - $event_dispatcher = $this->container->get('event_dispatcher'); - array_walk($validators, [$event_dispatcher, 'removeSubscriber']); + // Don't validate staged projects because actual stage operations are + // bypassed by package_manager_bypass, which will make this validator + // complain that there is no actual Composer data for it to inspect. + $validator = $this->container->get(StagedProjectsValidator::class); + $this->container->get('event_dispatcher')->removeSubscriber($validator); $stage = $this->container->get(UpdateStage::class); $stage->begin(['drupal' => '9.8.1']); -- GitLab