From 111c3cd8d0af1866a36aea9946fa6bb018f7fa30 Mon Sep 17 00:00:00 2001
From: lucashedding <lucashedding@1463982.no-reply.drupal.org>
Date: Tue, 28 May 2019 10:19:44 -0500
Subject: [PATCH] Issue #3054005 by heddn, catch: Checker: warn for projects
 that don't have packing info added

---
 automatic_updates.module                      |   2 +-
 automatic_updates.services.yml                |   9 ++
 src/Controller/ReadinessCheckerController.php |   2 +-
 src/ReadinessChecker/MissingProjectInfo.php   | 107 ++++++++++++++++++
 tests/src/Functional/AutomaticUpdatesTest.php |   2 +-
 .../MissingProjectInfoTest.php                |  69 +++++++++++
 6 files changed, 188 insertions(+), 3 deletions(-)
 create mode 100644 src/ReadinessChecker/MissingProjectInfo.php
 create mode 100644 tests/src/Kernel/ReadinessChecker/MissingProjectInfoTest.php

diff --git a/automatic_updates.module b/automatic_updates.module
index cb65f4a4b0..9f389b04f7 100644
--- a/automatic_updates.module
+++ b/automatic_updates.module
@@ -55,7 +55,7 @@ function automatic_updates_page_top(array &$page_top) {
     }
     $results = $checker->getResults('warning');
     if ($results) {
-      \Drupal::messenger()->addWarning(t('Your site does not pass some readiness checks for automatic updates. It might not be eligible for <a href="@readiness_checks">automatic updates</a>.', ['@readiness_checks' => 'https://www.drupal.org/docs/8/update/automatic-updates#readiness-checks']));
+      \Drupal::messenger()->addWarning(t('Your site does not pass some readiness checks for automatic updates. It might not be completely eligible for <a href="@readiness_checks">automatic updates</a>.', ['@readiness_checks' => 'https://www.drupal.org/docs/8/update/automatic-updates#readiness-checks']));
       foreach ($results as $message) {
         \Drupal::messenger()->addWarning($message);
       }
diff --git a/automatic_updates.services.yml b/automatic_updates.services.yml
index 5dc14c723a..ba47c91629 100644
--- a/automatic_updates.services.yml
+++ b/automatic_updates.services.yml
@@ -73,3 +73,12 @@ services:
     class: Drupal\automatic_updates\ReadinessChecker\PendingDbUpdates
     tags:
       - { name: readiness_checker, category: error}
+  automatic_updates.missing_project_info:
+    class: Drupal\automatic_updates\ReadinessChecker\MissingProjectInfo
+    arguments:
+      - '@automatic_updates.drupal_finder'
+      - '@extension.list.module'
+      - '@extension.list.profile'
+      - '@extension.list.theme'
+    tags:
+      - { name: readiness_checker, category: warning}
diff --git a/src/Controller/ReadinessCheckerController.php b/src/Controller/ReadinessCheckerController.php
index b5872cf747..29f253d0c4 100644
--- a/src/Controller/ReadinessCheckerController.php
+++ b/src/Controller/ReadinessCheckerController.php
@@ -54,7 +54,7 @@ class ReadinessCheckerController extends ControllerBase {
       $messages = array_merge($this->checker->run($category), $messages);
     }
     if (empty($messages)) {
-      $this->messenger()->addStatus($this->t('No issues found. Your site is ready to for <a href="@readiness_checks">automatic updates</a>.', ['@readiness_checks' => 'https://www.drupal.org/docs/8/update/automatic-updates#readiness-checks']));
+      $this->messenger()->addStatus($this->t('No issues found. Your site is completely ready for <a href="@readiness_checks">automatic updates</a>.', ['@readiness_checks' => 'https://www.drupal.org/docs/8/update/automatic-updates#readiness-checks']));
     }
     return $this->redirect('automatic_updates.settings');
   }
diff --git a/src/ReadinessChecker/MissingProjectInfo.php b/src/ReadinessChecker/MissingProjectInfo.php
new file mode 100644
index 0000000000..9b202732ba
--- /dev/null
+++ b/src/ReadinessChecker/MissingProjectInfo.php
@@ -0,0 +1,107 @@
+<?php
+
+namespace Drupal\automatic_updates\ReadinessChecker;
+
+use Drupal\Core\Extension\ExtensionList;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use DrupalFinder\DrupalFinder;
+
+/**
+ * Missing project info checker.
+ */
+class MissingProjectInfo extends Filesystem {
+  use StringTranslationTrait;
+
+  /**
+   * The module extension list.
+   *
+   * @var \Drupal\Core\Extension\ExtensionList
+   */
+  protected $modules;
+
+  /**
+   * The profile extension list.
+   *
+   * @var \Drupal\Core\Extension\ExtensionList
+   */
+  protected $profiles;
+
+  /**
+   * The theme extension list.
+   *
+   * @var \Drupal\Core\Extension\ExtensionList
+   */
+  protected $themes;
+
+  /**
+   * MissingProjectInfo constructor.
+   *
+   * @param \DrupalFinder\DrupalFinder $drupal_finder
+   *   The Drupal finder.
+   * @param \Drupal\Core\Extension\ExtensionList $modules
+   *   The module extension list.
+   * @param \Drupal\Core\Extension\ExtensionList $profiles
+   *   The profile extension list.
+   * @param \Drupal\Core\Extension\ExtensionList $themes
+   *   The theme extension list.
+   */
+  public function __construct(DrupalFinder $drupal_finder, ExtensionList $modules, ExtensionList $profiles, ExtensionList $themes) {
+    $this->drupalFinder = $drupal_finder;
+    $this->modules = $modules;
+    $this->profiles = $profiles;
+    $this->themes = $themes;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doCheck() {
+    return $this->missingProjectInfoCheck();
+  }
+
+  /**
+   * Check for projects missing project info.
+   *
+   * @return array
+   *   An array of translatable strings if any checks fail.
+   */
+  protected function missingProjectInfoCheck() {
+    $messages = [];
+    foreach ($this->getExtensionsTypes() as $extension_type) {
+      foreach ($this->getInfos($extension_type) as $extension_name => $info) {
+        if (empty($info['version'])) {
+          $messages[] = $this->t('The project "@extension" will not be updated because it is missing the "version" key in the @extension.info.yml file.', ['@extension' => $extension_name]);
+        }
+        if (empty($info['project'])) {
+          $messages[] = $this->t('The project "@extension" will not be updated because it is missing the "project" key in the @extension.info.yml file.', ['@extension' => $extension_name]);
+        }
+      }
+    }
+    return $messages;
+  }
+
+  /**
+   * Get the extension types.
+   *
+   * @return array
+   *   The extension types.
+   */
+  protected function getExtensionsTypes() {
+    return ['modules', 'profiles', 'themes'];
+  }
+
+  /**
+   * Returns an array of info files information of available extensions.
+   *
+   * @param string $extension_type
+   *   The extension type.
+   *
+   * @return array
+   *   An associative array of extension information arrays, keyed by extension
+   *   name.
+   */
+  protected function getInfos($extension_type) {
+    return $this->{$extension_type}->getAllAvailableInfo();
+  }
+
+}
diff --git a/tests/src/Functional/AutomaticUpdatesTest.php b/tests/src/Functional/AutomaticUpdatesTest.php
index a416154170..1ce69e985b 100644
--- a/tests/src/Functional/AutomaticUpdatesTest.php
+++ b/tests/src/Functional/AutomaticUpdatesTest.php
@@ -94,7 +94,7 @@ class AutomaticUpdatesTest extends BrowserTestBase {
     // Test manually running readiness checks.
     $this->drupalGet(Url::fromRoute('automatic_updates.settings'));
     $this->clickLink('run the readiness checks');
-    $this->assertSession()->pageTextContains('No issues found. Your site is ready to for automatic updates.');
+    $this->assertSession()->pageTextContains('Your site does not pass some readiness checks for automatic updates. It might not be completely eligible for automatic updates.');
   }
 
 }
diff --git a/tests/src/Kernel/ReadinessChecker/MissingProjectInfoTest.php b/tests/src/Kernel/ReadinessChecker/MissingProjectInfoTest.php
new file mode 100644
index 0000000000..e5ac3b3f1c
--- /dev/null
+++ b/tests/src/Kernel/ReadinessChecker/MissingProjectInfoTest.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace Drupal\Tests\automatic_updates\Kernel\ReadinessChecker;
+
+use Drupal\automatic_updates\ReadinessChecker\MissingProjectInfo;
+use DrupalFinder\DrupalFinder;
+use Drupal\Core\Extension\ExtensionList;
+use Drupal\KernelTests\KernelTestBase;
+
+/**
+ * Tests missing project info readiness checking.
+ *
+ * @group automatic_updates
+ */
+class MissingProjectInfoTest extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'automatic_updates',
+  ];
+
+  /**
+   * Tests pending db updates readiness checks.
+   */
+  public function testMissingProjectInfo() {
+    // The checker should always have messages on the testbot, since project
+    // info is added by the packager.
+    $messages = $this->container->get('automatic_updates.missing_project_info')->run();
+    $this->assertNotEmpty($messages);
+
+    // Now test with a some dummy info data that won't cause any issues.
+    $drupal_finder = new DrupalFinder();
+    $extension_list = $this->createMock(ExtensionList::class);
+    $messages = (new TestMissingProjectInfo($drupal_finder, $extension_list, $extension_list, $extension_list))->run();
+    $this->assertEmpty($messages);
+  }
+
+}
+
+/**
+ * Class TestMissingProjectInfo.
+ */
+class TestMissingProjectInfo extends MissingProjectInfo {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getInfos($extension_type) {
+    $infos = [];
+    if ($extension_type === 'modules') {
+      $infos['system'] = [
+        'name' => 'System',
+        'type' => 'module',
+        'description' => 'Handles general site configuration for administrators.',
+        'package' => 'Core',
+        'version' => 'VERSION',
+        'project' => 'drupal',
+        'core' => '8.x',
+        'required' => 'true',
+        'configure' => 'system.admin_config_system',
+        'dependencies' => [],
+      ];
+    }
+    return $infos;
+  }
+
+}
-- 
GitLab