diff --git a/core/modules/auto_updates/auto_updates.info.yml b/core/modules/auto_updates/auto_updates.info.yml
index 1914ec34e7..bb73ad023b 100644
--- a/core/modules/auto_updates/auto_updates.info.yml
+++ b/core/modules/auto_updates/auto_updates.info.yml
@@ -4,4 +4,3 @@ description: 'Experimental module to develop automatic updates. Currently the mo
configure: auto_updates.settings
package: Core (Experimental)
version: VERSION
-hidden: true
diff --git a/core/modules/auto_updates/auto_updates.install b/core/modules/auto_updates/auto_updates.install
index 1c9a1306fe..300d535483 100644
--- a/core/modules/auto_updates/auto_updates.install
+++ b/core/modules/auto_updates/auto_updates.install
@@ -7,7 +7,7 @@
use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
use Drupal\Core\Url;
-use Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManagerInterface;
+use Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManager;
/**
* Implements hook_requirements().
@@ -17,35 +17,35 @@ function auto_updates_requirements($phase) {
return [];
}
- /** @var \Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManagerInterface $checker_manager */
+ /** @var \Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManager $checker_manager */
$checker_manager = \Drupal::service('auto_updates.readiness_checker_manager');
if (!$checker_manager->isEnabled()) {
return [];
}
$requirements['auto_updates_readiness']['title'] = t('Update readiness checks');
- $readiness_check = Url::fromRoute('auto_updates.update_readiness');
- $last_check_timestamp = $checker_manager->getTimestamp();
+ $readiness_check_url = Url::fromRoute('auto_updates.update_readiness');
+ $last_check_timestamp = $checker_manager->getMostRecentRunTime();
if ($last_check_timestamp === NULL) {
- $requirements['auto_updates_readiness']['severity'] = REQUIREMENT_ERROR;
+ $requirements['auto_updates_readiness']['severity'] = REQUIREMENT_WARNING;
// @todo Link "automatic updates" to documentation in
// https://www.drupal.org/node/3168405.
$requirements['auto_updates_readiness']['value'] = t('Your site has never checked if it is ready to apply automatic updates.');
- if ($readiness_check->access()) {
+ if ($readiness_check_url->access()) {
$requirements['auto_updates_readiness']['description'] = t('Run readiness checks manually.', [
- ':link' => $readiness_check->toString(),
+ ':link' => $readiness_check_url->toString(),
]);
}
}
elseif (!$checker_manager->hasRunRecently()) {
- $requirements['auto_updates_readiness']['severity'] = REQUIREMENT_ERROR;
+ $requirements['auto_updates_readiness']['severity'] = REQUIREMENT_WARNING;
$time_ago = \Drupal::service('date.formatter')->formatTimeDiffSince($last_check_timestamp);
// @todo Link "automatic updates" to documentation in
// https://www.drupal.org/node/3168405.
$requirements['auto_updates_readiness']['value'] = t('Your site has not recently checked if it is ready to apply automatic updates.');
- if ($readiness_check->access()) {
- $requirements['auto_updates_readiness']['description'] = t('Readiness checks were last run @time ago. Run readiness checks manually.', [
+ if ($readiness_check_url->access()) {
+ $requirements['auto_updates_readiness']['description'] = t('Readiness checks were last run @time ago. Run readiness checks now.', [
'@time' => $time_ago,
- ':link' => $readiness_check->toString(),
+ ':url' => $readiness_check_url->toString(),
]);
}
else {
@@ -53,8 +53,8 @@ function auto_updates_requirements($phase) {
}
}
else {
- $error_results = $checker_manager->getResults(ReadinessCheckerManagerInterface::ERROR);
- $warning_results = $checker_manager->getResults(ReadinessCheckerManagerInterface::WARNING);
+ $error_results = $checker_manager->getResults(ReadinessCheckerManager::ERROR);
+ $warning_results = $checker_manager->getResults(ReadinessCheckerManager::WARNING);
$checker_results = array_merge($error_results, $warning_results);
if (!empty($checker_results)) {
$requirements['auto_updates_readiness']['severity'] = $error_results ? REQUIREMENT_ERROR : REQUIREMENT_WARNING;
@@ -79,8 +79,8 @@ function auto_updates_requirements($phase) {
/**
* Implements hook_install().
*/
-function auto_updates_install($is_syncing) {
- /** @var \Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManagerInterface $checker_manager */
+function auto_updates_install() {
+ /** @var \Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManager $checker_manager */
$checker_manager = \Drupal::service('auto_updates.readiness_checker_manager');
$checker_manager->run();
}
diff --git a/core/modules/auto_updates/auto_updates.module b/core/modules/auto_updates/auto_updates.module
index 3bb1cad5a5..69d060891b 100644
--- a/core/modules/auto_updates/auto_updates.module
+++ b/core/modules/auto_updates/auto_updates.module
@@ -6,7 +6,7 @@
*/
use Drupal\Core\Url;
-use Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManagerInterface;
+use Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManager;
/**
* Implements hook_page_top().
@@ -29,10 +29,10 @@ function auto_updates_page_top(array &$page_top) {
'update.confirmation_page',
];
// These routes don't need additional nagging.
- if (in_array(\Drupal::routeMatch()->getRouteName(), $disabled_routes, TRUE)) {
+ if (in_array($route_match->getRouteName(), $disabled_routes, TRUE)) {
return;
}
- /** @var \Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManagerInterface $checker_manager */
+ /** @var \Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManager $checker_manager */
$checker_manager = \Drupal::service('auto_updates.readiness_checker_manager');
if (!$checker_manager->hasRunRecently()) {
$readiness_settings = Url::fromRoute('auto_updates.settings');
@@ -40,7 +40,7 @@ function auto_updates_page_top(array &$page_top) {
':url' => $readiness_settings->toString(),
]));
}
- $results = $checker_manager->getResults(ReadinessCheckerManagerInterface::ERROR);
+ $results = $checker_manager->getResults(ReadinessCheckerManager::ERROR);
if ($results) {
// @todo Link "automatic updates" to documentation in
// https://www.drupal.org/node/3168405.
@@ -49,7 +49,7 @@ function auto_updates_page_top(array &$page_top) {
\Drupal::messenger()->addError($message);
}
}
- $results = $checker_manager->getResults(ReadinessCheckerManagerInterface::WARNING);
+ $results = $checker_manager->getResults(ReadinessCheckerManager::WARNING);
if ($results) {
// @todo Link "automatic updates" to documentation in
// https://www.drupal.org/node/3168405.
@@ -66,9 +66,9 @@ function auto_updates_page_top(array &$page_top) {
*/
function auto_updates_cron() {
$request_time = \Drupal::time()->getRequestTime();
- /** @var \Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManagerInterface $checker_manager */
+ /** @var \Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManager $checker_manager */
$checker_manager = \Drupal::service('auto_updates.readiness_checker_manager');
- $last_check = $checker_manager->getTimestamp();
+ $last_check = $checker_manager->getMostRecentRunTime();
// Only allow cron to run once every hour.
if ($last_check && ($request_time - $last_check) < 3600) {
return;
@@ -79,8 +79,8 @@ function auto_updates_cron() {
/**
* Implements hook_modules_installed().
*/
-function auto_updates_modules_installed($modules) {
- /** @var \Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManagerInterface $checker_manager */
+function auto_updates_modules_installed() {
+ /** @var \Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManager $checker_manager */
$checker_manager = \Drupal::service('auto_updates.readiness_checker_manager');
if ($checker_manager->clearStaleResults()) {
$checker_manager->run();
diff --git a/core/modules/auto_updates/src/Controller/ReadinessCheckerController.php b/core/modules/auto_updates/src/Controller/ReadinessCheckerController.php
index bd3aac3fb8..56ee8dfb21 100644
--- a/core/modules/auto_updates/src/Controller/ReadinessCheckerController.php
+++ b/core/modules/auto_updates/src/Controller/ReadinessCheckerController.php
@@ -2,12 +2,13 @@
namespace Drupal\auto_updates\Controller;
-use Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManagerInterface;
+use Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManager;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Access\AccessResultInterface;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\StringTranslation\TranslationInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\RedirectResponse;
/**
* A controller for running Readiness Checkers.
@@ -20,7 +21,7 @@ class ReadinessCheckerController extends ControllerBase {
/**
* The readiness checker manager.
*
- * @var \Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManagerInterface
+ * @var \Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManager
*/
protected $checkerManager;
@@ -32,9 +33,9 @@ class ReadinessCheckerController extends ControllerBase {
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
* The string translation service.
*/
- public function __construct(ReadinessCheckerManagerInterface $checker_manager, TranslationInterface $string_translation) {
+ public function __construct(ReadinessCheckerManager $checker_manager, TranslationInterface $string_translation) {
$this->checkerManager = $checker_manager;
- $this->stringTranslation = $string_translation;
+ $this->setStringTranslation($string_translation);
}
/**
@@ -53,7 +54,7 @@ public static function create(ContainerInterface $container) {
* @return \Symfony\Component\HttpFoundation\RedirectResponse
* A redirect to the automatic updates settings page.
*/
- public function run() {
+ public function run(): RedirectResponse {
if (!array_filter($this->checkerManager->run())) {
// @todo Link "automatic updates" to documentation in
// https://www.drupal.org/node/3168405.
diff --git a/core/modules/auto_updates/src/Form/SettingsForm.php b/core/modules/auto_updates/src/Form/SettingsForm.php
index 96f634dfcb..07530515fd 100644
--- a/core/modules/auto_updates/src/Form/SettingsForm.php
+++ b/core/modules/auto_updates/src/Form/SettingsForm.php
@@ -9,13 +9,15 @@
/**
* Settings form for Automatic Updates.
+ *
+ * @internal
*/
class SettingsForm extends ConfigFormBase {
/**
* The readiness checker manager.
*
- * @var \Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManagerInterface
+ * @var \Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManager
*/
protected $checkerManager;
@@ -57,7 +59,7 @@ public function getFormId() {
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$config = $this->config('auto_updates.settings');
- $last_check_timestamp = $this->checkerManager->getTimestamp();
+ $last_check_timestamp = $this->checkerManager->getMostRecentRunTime();
$form['enable_readiness_checks'] = [
'#type' => 'checkbox',
'#title' => $this->t('Check the readiness of automatically updating the site.'),
diff --git a/core/modules/auto_updates/src/ReadinessChecker/DiskSpace.php b/core/modules/auto_updates/src/ReadinessChecker/DiskSpace.php
index 2cc5684d4a..81997d2305 100644
--- a/core/modules/auto_updates/src/ReadinessChecker/DiskSpace.php
+++ b/core/modules/auto_updates/src/ReadinessChecker/DiskSpace.php
@@ -13,12 +13,9 @@ class DiskSpace extends FileSystemBase {
* @todo Determine how much the minimum should be now that we will be using
* Composer in https://www.drupal.org/node/3166416.
*/
- const MINIMUM_DISK_SPACE = 1073741824;
+ protected const MINIMUM_DISK_SPACE = 1073741824;
- /**
- * Megabyte divisor.
- */
- const MEGABYTE_DIVISOR = 1000000;
+ protected const MEGABYTE_DIVISOR = 1000000;
/**
* {@inheritdoc}
diff --git a/core/modules/auto_updates/src/ReadinessChecker/FileSystemBase.php b/core/modules/auto_updates/src/ReadinessChecker/FileSystemBase.php
index 8c4da6a22b..61675f0333 100644
--- a/core/modules/auto_updates/src/ReadinessChecker/FileSystemBase.php
+++ b/core/modules/auto_updates/src/ReadinessChecker/FileSystemBase.php
@@ -86,11 +86,17 @@ protected function getVendorPath(): string {
*
* @return bool
* TRUE if they are on the same file system, FALSE otherwise.
+ *
+ * @throws \Exception
+ * Thrown if the an error is found trying get the directory information.
*/
protected function areSameLogicalDisk(string $root, string $vendor): bool {
$root_statistics = stat($root);
$vendor_statistics = stat($vendor);
- return $root_statistics && $vendor_statistics && $root_statistics['dev'] === $vendor_statistics['dev'];
+ if ($root_statistics === FALSE || $vendor_statistics === FALSE) {
+ throw new \Exception('Unable to determine if the root and vendor directories are on the same logic disk.');
+ }
+ return $root_statistics['dev'] === $vendor_statistics['dev'];
}
}
diff --git a/core/modules/auto_updates/src/ReadinessChecker/ReadinessCheckerManager.php b/core/modules/auto_updates/src/ReadinessChecker/ReadinessCheckerManager.php
index 2d855d3190..54b95a2db6 100644
--- a/core/modules/auto_updates/src/ReadinessChecker/ReadinessCheckerManager.php
+++ b/core/modules/auto_updates/src/ReadinessChecker/ReadinessCheckerManager.php
@@ -9,7 +9,18 @@
/**
* Defines a manager to run readiness checkers.
*/
-class ReadinessCheckerManager implements ReadinessCheckerManagerInterface {
+class ReadinessCheckerManager {
+
+
+ /**
+ * Error category.
+ */
+ const ERROR = 'error';
+
+ /**
+ * Warning category.
+ */
+ const WARNING = 'warning';
/**
* Time (in seconds) since the last check after which we generate a warning.
@@ -66,9 +77,20 @@ public function __construct(KeyValueFactoryInterface $key_value, ConfigFactoryIn
}
/**
- * {@inheritdoc}
+ * Appends a checker to the checker chain.
+ *
+ * @param \Drupal\auto_updates\ReadinessChecker\ReadinessCheckerInterface $checker
+ * The checker interface to be appended to the checker chain.
+ * @param string $category
+ * (optional) The category of check. Defaults to 'warning'.
+ * @param int $priority
+ * (optional) The priority of the checker being added. Defaults to 0.
+ * Readiness checkers with larger priorities will run first within a
+ * category.
+ *
+ * @return $this
*/
- public function addChecker(ReadinessCheckerInterface $checker, string $category = 'warning', $priority = 0): ReadinessCheckerManagerInterface {
+ public function addChecker(ReadinessCheckerInterface $checker, string $category = 'warning', $priority = 0): ReadinessCheckerManager {
if (!in_array($category, $this->getCategories(), TRUE)) {
throw new \InvalidArgumentException(sprintf('Readiness checker category "%s" is invalid. Use "%s" instead.', $category, implode('" or "', $this->getCategories())));
}
@@ -77,7 +99,12 @@ public function addChecker(ReadinessCheckerInterface $checker, string $category
}
/**
- * {@inheritdoc}
+ * Runs readiness checks.
+ *
+ * @return string[][]
+ * A nested array of readiness check messages. The top level array is keyed
+ * by category and the next level array is an array of translatable strings
+ * for the category.
*/
public function run(): array {
if (!$this->isEnabled()) {
@@ -104,7 +131,14 @@ public function run(): array {
}
/**
- * {@inheritdoc}
+ * Gets the results of the most recent run.
+ *
+ * @param string $category
+ * The category of check.
+ *
+ * @return array
+ * An array of translatable messages if any checks fail, otherwise an empty
+ * array.
*/
public function getResults(string $category): array {
if ($this->isEnabled()) {
@@ -116,7 +150,10 @@ public function getResults(string $category): array {
}
/**
- * {@inheritdoc}
+ * Clears readiness checker results if the available checkers have changed.
+ *
+ * @return bool
+ * TRUE if the results were cleared, otherwise FALSE.
*/
public function clearStaleResults(): bool {
$results = $this->keyValue->get('readiness_check_results');
@@ -128,21 +165,31 @@ public function clearStaleResults(): bool {
}
/**
- * {@inheritdoc}
+ * Gets the timestamp of the most recent run.
+ *
+ * @return int|null
+ * The timestamp of the most recently completed run, or NULL if no run has
+ * been completed.
*/
- public function getTimestamp(): int {
+ public function getMostRecentRunTime(): int {
return $this->keyValue->get('readiness_check_timestamp');
}
/**
- * {@inheritdoc}
+ * Determines if readiness checks are enabled.
+ *
+ * @return bool
+ * TRUE if enabled, otherwise FALSE.
*/
public function isEnabled(): bool {
return $this->configFactory->get('auto_updates.settings')->get('enable_readiness_checks');
}
/**
- * {@inheritdoc}
+ * Gets the checker categories.
+ *
+ * @return string[]
+ * The checkers categories.
*/
public function getCategories(): array {
return [self::ERROR, self::WARNING];
@@ -185,10 +232,13 @@ protected function getCurrentCheckerIds(): string {
}
/**
- * {@inheritdoc}
+ * Determines whether the readiness checkers have been run recently.
+ *
+ * @return bool
+ * TRUE if the checkers have been run recently, otherwise FALSE.
*/
public function hasRunRecently(): bool {
- return $this->time->getRequestTime() <= $this->getTimestamp() + self::LAST_CHECKED_WARNING;
+ return $this->time->getRequestTime() <= $this->getMostRecentRunTime() + self::LAST_CHECKED_WARNING;
}
}
diff --git a/core/modules/auto_updates/src/ReadinessChecker/ReadinessCheckerManagerInterface.php b/core/modules/auto_updates/src/ReadinessChecker/ReadinessCheckerManagerInterface.php
deleted file mode 100644
index 51923de26e..0000000000
--- a/core/modules/auto_updates/src/ReadinessChecker/ReadinessCheckerManagerInterface.php
+++ /dev/null
@@ -1,99 +0,0 @@
-assertSession();
$page = $this->getSession()->getPage();
@@ -80,7 +80,7 @@ public function testReadinessChecksStatusReport() {
$this->drupalLogin($this->checkerRunnerUser);
$this->drupalGet('admin/reports/status');
$this->assertReadinessReportMatches('Your site has not recently checked if it is ready to apply automatic updates.'
- . ' Readiness checks were last run %s ago. Run readiness checks manually.');
+ . ' Readiness checks were last run %s ago. Run readiness checks now.');
$this->container->get('state')->set(TestChecker::STATE_KEY, 'OMG 🚒. Your server is on 🔥!');
// Run the readiness checks.
@@ -139,7 +139,7 @@ public function testReadinessChecksStatusReport() {
/**
* Tests installing a module with a checker before installing auto_updates.
*/
- public function testReadinessCheckAfterInstall() {
+ public function testReadinessCheckAfterInstall(): void {
$assert = $this->assertSession();
$this->drupalLogin($this->checkerRunnerUser);
@@ -174,6 +174,23 @@ public function testReadinessCheckAfterInstall() {
$this->assertReadinessReportMatches('1 check failed: Security has been compromised. "pass123" was a bad password!');
}
+ /**
+ * Tests that checker message for an uninstalled module is not displayed.
+ */
+ public function testReadinessCheckerUninstall(): void {
+ $assert = $this->assertSession();
+ $this->drupalLogin($this->checkerRunnerUser);
+
+ $this->container->get('state')->set(TestChecker::STATE_KEY, '😲Your site is running on Commodore 64! Not powerful enough to do updates!');
+ $this->container->get('module_installer')->install(['auto_updates', 'auto_updates_test']);
+ $this->drupalGet('admin/reports/status');
+ $this->assertReadinessReportMatches('1 check failed: 😲Your site is running on Commodore 64! Not powerful enough to do updates!');
+
+ $this->container->get('module_installer')->uninstall(['auto_updates_test']);
+ $this->drupalGet('admin/reports/status');
+ $assert->pageTextNotContains('1 check failed: 😲Your site is running on Commodore 64! Not powerful enough to do updates!');
+ }
+
/**
* Tests that the readiness checks are run on cron.
*/
diff --git a/core/modules/auto_updates/tests/src/Kernel/ReadinessChecker/DiskSpaceTest.php b/core/modules/auto_updates/tests/src/Kernel/ReadinessChecker/DiskSpaceTest.php
index e1c7dd0f3f..d128c46c5f 100644
--- a/core/modules/auto_updates/tests/src/Kernel/ReadinessChecker/DiskSpaceTest.php
+++ b/core/modules/auto_updates/tests/src/Kernel/ReadinessChecker/DiskSpaceTest.php
@@ -64,7 +64,7 @@ class TestDiskSpace extends DiskSpace {
}
/**
- * Class TestDiskSpaceNonSameDisk.
+ * A test checker that overrides TestDiskSpace to fake different logical disks.
*/
class TestDiskSpaceNonSameDisk extends TestDiskSpace {