From 299277060c2ab5f0fe088710728b0c2a98e7e489 Mon Sep 17 00:00:00 2001
From: phenaproxima <phenaproxima@205645.no-reply.drupal.org>
Date: Wed, 19 Oct 2022 00:35:34 +0000
Subject: [PATCH] Issue #3314787 by kunal.sachdev, phenaproxima, tedbow:
 StagedDBUpdateValidator will throw an exception during status check if the
 stage exists, but is unclaimed

---
 .../src/Validator/StagedDBUpdateValidator.php | 28 +++++++++----------
 .../src/StagedDatabaseUpdateValidator.php     |  5 ++--
 .../Kernel/StagedDBUpdateValidatorTest.php    | 11 ++++++++
 .../StagedDatabaseUpdateValidator.php         |  2 +-
 4 files changed, 28 insertions(+), 18 deletions(-)

diff --git a/package_manager/src/Validator/StagedDBUpdateValidator.php b/package_manager/src/Validator/StagedDBUpdateValidator.php
index da53bc1f75..5bba97de81 100644
--- a/package_manager/src/Validator/StagedDBUpdateValidator.php
+++ b/package_manager/src/Validator/StagedDBUpdateValidator.php
@@ -8,7 +8,6 @@ use Drupal\Core\Extension\ThemeExtensionList;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\package_manager\Event\StatusCheckEvent;
 use Drupal\package_manager\PathLocator;
-use Drupal\package_manager\Stage;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
 /**
@@ -67,13 +66,15 @@ class StagedDBUpdateValidator implements EventSubscriberInterface {
    *   The event object.
    */
   public function checkForStagedDatabaseUpdates(StatusCheckEvent $event): void {
-    $stage = $event->getStage();
-    if ($stage->isAvailable()) {
-      // No staged updates exist, therefore we don't need to run this check.
+    try {
+      $stage_dir = $event->getStage()->getStageDirectory();
+    }
+    catch (\LogicException $e) {
+      // Stage directory can't be determined, so there's nothing to validate.
       return;
     }
 
-    $extensions_with_updates = $this->getExtensionsWithDatabaseUpdates($stage);
+    $extensions_with_updates = $this->getExtensionsWithDatabaseUpdates($stage_dir);
     if ($extensions_with_updates) {
       $event->addWarning($extensions_with_updates, $this->t('Possible database updates have been detected in the following extensions.'));
     }
@@ -82,8 +83,8 @@ class StagedDBUpdateValidator implements EventSubscriberInterface {
   /**
    * Determines if a staged extension has changed update functions.
    *
-   * @param \Drupal\package_manager\Stage $stage
-   *   The updater which is controlling the update process.
+   * @param string $stage_dir
+   *   The path of the staging area.
    * @param \Drupal\Core\Extension\Extension $extension
    *   The extension to check.
    *
@@ -103,9 +104,8 @@ class StagedDBUpdateValidator implements EventSubscriberInterface {
    *
    * @see https://www.drupal.org/project/automatic_updates/issues/3253828
    */
-  public function hasStagedUpdates(Stage $stage, Extension $extension): bool {
+  public function hasStagedUpdates(string $stage_dir, Extension $extension): bool {
     $active_dir = $this->pathLocator->getProjectRoot();
-    $stage_dir = $stage->getStageDirectory();
 
     $web_root = $this->pathLocator->getWebRoot();
     if ($web_root) {
@@ -159,21 +159,21 @@ class StagedDBUpdateValidator implements EventSubscriberInterface {
   }
 
   /**
-   * Gets extensions that have database updates.
+   * Gets extensions that have database updates in the staging area.
    *
-   * @param \Drupal\package_manager\Stage $stage
-   *   The stage.
+   * @param string $stage_dir
+   *   The path of the staging area.
    *
    * @return string[]
    *   The names of the extensions that have possible database updates.
    */
-  public function getExtensionsWithDatabaseUpdates(Stage $stage): array {
+  public function getExtensionsWithDatabaseUpdates(string $stage_dir): array {
     $extensions_with_updates = [];
     // Check all installed extensions for database updates.
     $lists = [$this->moduleList, $this->themeList];
     foreach ($lists as $list) {
       foreach ($list->getAllInstalledInfo() as $name => $info) {
-        if ($this->hasStagedUpdates($stage, $list->get($name))) {
+        if ($this->hasStagedUpdates($stage_dir, $list->get($name))) {
           $extensions_with_updates[] = $info['name'];
         }
       }
diff --git a/package_manager/tests/modules/package_manager_test_validation/src/StagedDatabaseUpdateValidator.php b/package_manager/tests/modules/package_manager_test_validation/src/StagedDatabaseUpdateValidator.php
index 8ee4bc71b6..c0503696a4 100644
--- a/package_manager/tests/modules/package_manager_test_validation/src/StagedDatabaseUpdateValidator.php
+++ b/package_manager/tests/modules/package_manager_test_validation/src/StagedDatabaseUpdateValidator.php
@@ -5,7 +5,6 @@ namespace Drupal\package_manager_test_validation;
 use Drupal\package_manager\Validator\StagedDBUpdateValidator as BaseValidator;
 use Drupal\Core\Extension\Extension;
 use Drupal\Core\State\StateInterface;
-use Drupal\package_manager\Stage;
 
 /**
  * Allows tests to dictate which extensions have staged database updates.
@@ -43,12 +42,12 @@ class StagedDatabaseUpdateValidator extends BaseValidator {
   /**
    * {@inheritdoc}
    */
-  public function hasStagedUpdates(Stage $stage, Extension $extension): bool {
+  public function hasStagedUpdates(string $stage_dir, Extension $extension): bool {
     $extensions = $this->state->get(static::class);
     if (isset($extensions)) {
       return in_array($extension->getName(), $extensions, TRUE);
     }
-    return parent::hasStagedUpdates($stage, $extension);
+    return parent::hasStagedUpdates($stage_dir, $extension);
   }
 
 }
diff --git a/package_manager/tests/src/Kernel/StagedDBUpdateValidatorTest.php b/package_manager/tests/src/Kernel/StagedDBUpdateValidatorTest.php
index eba232a27e..ff17b6c579 100644
--- a/package_manager/tests/src/Kernel/StagedDBUpdateValidatorTest.php
+++ b/package_manager/tests/src/Kernel/StagedDBUpdateValidatorTest.php
@@ -141,4 +141,15 @@ class StagedDBUpdateValidatorTest extends PackageManagerKernelTestBase {
     $this->assertStatusCheckResults([$result], $stage);
   }
 
+  /**
+   * Tests that the validator disregards unclaimed stages.
+   */
+  public function testUnclaimedStage(): void {
+    $stage = $this->createStage();
+    $stage->create();
+    $this->assertStatusCheckResults([], $stage);
+    // A new, unclaimed stage should be ignored by the validator.
+    $this->assertStatusCheckResults([], $this->createStage());
+  }
+
 }
diff --git a/src/Validator/StagedDatabaseUpdateValidator.php b/src/Validator/StagedDatabaseUpdateValidator.php
index 8bc6181d2f..1f64735a2c 100644
--- a/src/Validator/StagedDatabaseUpdateValidator.php
+++ b/src/Validator/StagedDatabaseUpdateValidator.php
@@ -53,7 +53,7 @@ class StagedDatabaseUpdateValidator implements EventSubscriberInterface {
       return;
     }
 
-    $invalid_extensions = $this->stagedDBUpdateValidator->getExtensionsWithDatabaseUpdates($stage);
+    $invalid_extensions = $this->stagedDBUpdateValidator->getExtensionsWithDatabaseUpdates($stage->getStageDirectory());
     if ($invalid_extensions) {
       $event->addError($invalid_extensions, $this->t('The update cannot proceed because possible database updates have been detected in the following extensions.'));
     }
-- 
GitLab