diff --git a/package_manager/src/Validator/WritableFileSystemValidator.php b/package_manager/src/Validator/WritableFileSystemValidator.php index 4b4ce66add001525f5c0c3d800a9895fd0bf4b07..b8c656d7c65f7937737193e2985e3acba822b1d2 100644 --- a/package_manager/src/Validator/WritableFileSystemValidator.php +++ b/package_manager/src/Validator/WritableFileSystemValidator.php @@ -67,6 +67,23 @@ class WritableFileSystemValidator implements PreOperationStageValidatorInterface $messages[] = $this->t('The vendor directory "@dir" is not writable.', ['@dir' => $dir]); } + // Ensure the staging root is writable. If it doesn't exist, ensure we will + // be able to create it. + $dir = $this->pathLocator->getStagingRoot(); + if (!file_exists($dir)) { + $dir = dirname($dir); + if (!is_writable($dir)) { + $messages[] = $this->t('The staging root directory will not able to be created at "@dir".', [ + '@dir' => $dir, + ]); + } + } + elseif (!is_writable($dir)) { + $messages[] = $this->t('The staging root directory "@dir" is not writable.', [ + '@dir' => $dir, + ]); + } + if ($messages) { $event->addError($messages, $this->t('The file system is not writable.')); } diff --git a/package_manager/tests/src/Kernel/StageTest.php b/package_manager/tests/src/Kernel/StageTest.php index 65fd2edd505e490dd0ec4020572adb92f8e4ab56..f2eea4f7f2a99c15ae1d28dbba8b1e1237b04d77 100644 --- a/package_manager/tests/src/Kernel/StageTest.php +++ b/package_manager/tests/src/Kernel/StageTest.php @@ -44,6 +44,12 @@ class StageTest extends PackageManagerKernelTestBase { * @covers ::getStageDirectory */ public function testGetStageDirectory(): void { + // In this test, we're working with paths that (probably) don't exist in + // the file system at all, so we don't want to validate that the file system + // is writable when creating stages. + $validator = $this->container->get('package_manager.validator.file_system'); + $this->container->get('event_dispatcher')->removeSubscriber($validator); + // Don't mirror the active directory from the virtual project into the // real file system. Beginner::setFixturePath(NULL); diff --git a/package_manager/tests/src/Kernel/WritableFileSystemValidatorTest.php b/package_manager/tests/src/Kernel/WritableFileSystemValidatorTest.php index 988e339cb82dba1302a59ff61bc057979199d258..d9f25fcb76f179475a3d6ab172095a6f02eb9294 100644 --- a/package_manager/tests/src/Kernel/WritableFileSystemValidatorTest.php +++ b/package_manager/tests/src/Kernel/WritableFileSystemValidatorTest.php @@ -4,6 +4,7 @@ namespace Drupal\Tests\package_manager\Kernel; use Drupal\package_manager\Event\PreCreateEvent; use Drupal\package_manager\ValidationResult; +use Symfony\Component\Filesystem\Filesystem; /** * Unit tests the file system permissions validator. @@ -86,4 +87,63 @@ class WritableFileSystemValidatorTest extends PackageManagerKernelTestBase { $this->assertResults($expected_results, PreCreateEvent::class); } + /** + * Data provider for ::testStagingRootPermissions(). + * + * @return mixed[][] + * The test cases. + */ + public function providerStagingRootPermissions(): array { + $writable_permission = 0777; + $non_writable_permission = 0444; + $summary = t('The file system is not writable.'); + return [ + 'writable staging root exists' => [ + $writable_permission, + [], + FALSE, + ], + 'write-protected staging root exists' => [ + $non_writable_permission, + [ + ValidationResult::createError(['The staging root directory "vfs://root/stage" is not writable.'], $summary), + ], + FALSE, + ], + 'staging root does not exist, parent directory not writable' => [ + $non_writable_permission, + [ + ValidationResult::createError(['The staging root directory will not able to be created at "vfs://root".'], $summary), + ], + TRUE, + ], + ]; + } + + /** + * Tests that the staging root's permissions are validated. + * + * @param int $permissions + * The file permissions to apply to the staging root, or its parent + * directory, depending on the value of $delete_staging_root. + * @param array $expected_results + * The expected validation results. + * @param bool $delete_staging_root + * Whether the staging root directory will exist at all. + * + * @dataProvider providerStagingRootPermissions + */ + public function testStagingRootPermissions(int $permissions, array $expected_results, bool $delete_staging_root): void { + $dir = $this->container->get('package_manager.path_locator') + ->getStagingRoot(); + + if ($delete_staging_root) { + $fs = new Filesystem(); + $fs->remove($dir); + $dir = dirname($dir); + } + $this->assertTrue(chmod($dir, $permissions)); + $this->assertResults($expected_results, PreCreateEvent::class); + } + }