diff --git a/automatic_updates.services.yml b/automatic_updates.services.yml index 26d646a..0d335fc 100644 --- a/automatic_updates.services.yml +++ b/automatic_updates.services.yml @@ -2,6 +2,10 @@ services: logger.channel.automatic_updates: parent: logger.channel_base arguments: ['automatic_updates'] + automatic_updates.drupal_finder: + class: DrupalFinder\DrupalFinder + automatic_updates.filesystem_cache: + class: League\Flysystem\Cached\Storage\Memory automatic_updates.psa: class: Drupal\automatic_updates\Services\AutomaticUpdatesPsa arguments: @@ -13,3 +17,6 @@ services: - '@extension.list.profile' - '@extension.list.theme' - '@logger.channel.automatic_updates' + plugin.manager.preflight: + class: Drupal\automatic_updates\PreflightPluginManager + parent: default_plugin_manager diff --git a/composer.json b/composer.json index 0e18c77..e366d15 100644 --- a/composer.json +++ b/composer.json @@ -12,6 +12,9 @@ }, "require": { "ext-json": "*", - "composer/semver": "^1.0@dev" + "composer/semver": "^1.0", + "league/flysystem": "^1.1", + "league/flysystem-cached-adapter": "^1.0", + "webflo/drupal-finder": "^1.1" } } diff --git a/src/Annotation/Preflight.php b/src/Annotation/Preflight.php new file mode 100644 index 0000000..6efe017 --- /dev/null +++ b/src/Annotation/Preflight.php @@ -0,0 +1,39 @@ +logger = $logger; + $this->findDrupal($drupal_finder, $file_system_cache); + } + + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('automatic_updates.drupal_finder'), + $container->get('automatic_updates.filesystem_cache'), + $container->get('logger.channel.automatic_updates') + ); + } + + /** + * {@inheritdoc} + */ + public function runPreflight() { + $messages = []; + if (!$this->coreFileSystem || !$this->coreFileSystem->has('COPYRIGHT.txt')) { + $messages[] = $this->t('The drupal web public directory could not be located.'); + return $messages; + } + $this->readOnly($messages); + return $messages; + } + + /** + * Find where drupal is located. + * + * @param \DrupalFinder\DrupalFinder $drupal_finder + * The drupal finder service. + * @param \League\Flysystem\Cached\CacheInterface $file_system_cache + * The file system cache. + */ + protected function findDrupal(DrupalFinder $drupal_finder, CacheInterface $file_system_cache) { + if ($drupal_finder->locateRoot(getcwd())) { + $this->coreFileSystem = new Filesystem(new CachedAdapter(new Local($drupal_finder->getDrupalRoot() . '/core'), $file_system_cache)); + $this->vendorFileSystem = new Filesystem(new CachedAdapter(new Local($drupal_finder->getVendorDir()), $file_system_cache)); + } + } + + /** + * Check if the filesystem is read only. + * + * @param array $messages + * The messages array of translatable strings. + */ + protected function readOnly(array &$messages) { + try { + $this->doReadOnlyCheck($this->coreFileSystem, 'COPYRIGHT.txt', $messages, $this->t('Drupal core filesystem is read only.')); + $this->doReadOnlyCheck($this->vendorFileSystem, 'composer/LICENSE', $messages, $this->t('Vendor filesystem is read only.')); + } + catch (\LogicException $exception) { + $this->logger->error($exception->getMessage()); + $messages[] = $this->t('During read only check, a logic exception was encountered'); + } + } + + /** + * Do the read only check. + * + * @param \League\Flysystem\FilesystemInterface $filesystem + * The filesystem to test. + * @param string $file + * The file path. + * @param array $messages + * The messages array of translatable strings. + * @param \Drupal\Component\Render\MarkupInterface $message + * The error message to add if the file is read only. + */ + private function doReadOnlyCheck(FilesystemInterface $filesystem, $file, array &$messages, MarkupInterface $message) { + try { + if ($filesystem->rename($file, "$file.moved")) { + // Move it back after moving it. + $filesystem->rename("$file.moved", $file); + } + else { + $messages[] = $message; + } + } + catch (FileExistsException $exception) { + $this->logger->error($exception->getMessage()); + $messages[] = $this->t('File already exists at path: %file', ['%file' => $exception->getPath()]); + } + catch (FileNotFoundException $exception) { + $this->logger->error($exception->getMessage()); + $messages[] = $this->t('File not found at path: %file', ['%file' => $exception->getPath()]); + } + } +} diff --git a/src/PreflightPluginBase.php b/src/PreflightPluginBase.php new file mode 100644 index 0000000..db38fcd --- /dev/null +++ b/src/PreflightPluginBase.php @@ -0,0 +1,11 @@ +alterInfo('preflight_info'); + $this->setCacheBackend($cache_backend, 'preflight_plugins'); + } + +} diff --git a/tests/src/Kernel/AutomaticUpdatesPreflightTest.php b/tests/src/Kernel/AutomaticUpdatesPreflightTest.php new file mode 100644 index 0000000..9241d54 --- /dev/null +++ b/tests/src/Kernel/AutomaticUpdatesPreflightTest.php @@ -0,0 +1,30 @@ +container->get('plugin.manager.preflight'); + /** @var \Drupal\automatic_updates\Plugin\Preflight\FilesystemPreflight $plugin */ + $plugin = $manager->createInstance('filesystem_preflight'); + $this->assertEmpty($plugin->runPreflight()); + } + +}