ExtensionUpgrades - Skip trying to upgrade missing dependencies
authorColeman Watts <coleman@civicrm.org>
Tue, 25 Jan 2022 16:54:40 +0000 (11:54 -0500)
committerColeman Watts <coleman@civicrm.org>
Thu, 27 Jan 2022 02:33:46 +0000 (21:33 -0500)
Before: Missing dependencies would be added and then the extension upgrader would try and fail to upgrade them
After: They are skipped, as missing extension dependencies are handled elsewhere

This allows the extension upgrade to proceed without error, even if there are missing dependencies.
The user will be prompted to install the missing dependencies afterward.

CRM/Extension/Mapper.php
CRM/Extension/Upgrades.php

index e056a7b24d9874f440e293289502ce3e39eacec2..550091a84e615ed3d7aa6a79cd06d3a6fe380dbe 100644 (file)
@@ -441,7 +441,7 @@ class CRM_Extension_Mapper {
   }
 
   /**
-   * @return array
+   * @return CRM_Extension_Info[]
    *   Ex: $result['org.civicrm.foobar'] = new CRM_Extension_Info(...).
    * @throws \CRM_Extension_Exception
    * @throws \Exception
index adf3b1f49493501cda107cf59bf5654b2b772694..6d5991fa55b1a87cab6c6dd9ccee299b14f5bd31 100644 (file)
@@ -127,7 +127,10 @@ class CRM_Extension_Upgrades {
   }
 
   /**
+   * Sorts active extensions according to their dependencies
+   *
    * @param string[] $keys
+   *   Names of all active modules
    *
    * @return string[]
    * @throws \CRM_Extension_Exception
@@ -137,30 +140,25 @@ class CRM_Extension_Upgrades {
   protected static function sortKeys($keys) {
     $infos = CRM_Extension_System::singleton()->getMapper()->getAllInfos();
 
-    // Start with our inputs in a normalized form.
+    // Ensure a stable starting order.
     $todoKeys = array_unique($keys);
     sort($todoKeys);
 
-    // Goal: Add all active items to $sorter and flag $doneKeys['org.example.foobar']=1.
-    $doneKeys = [];
     $sorter = new \MJS\TopSort\Implementations\FixedArraySort();
 
-    while (!empty($todoKeys)) {
-      $key = array_shift($todoKeys);
-      if (isset($doneKeys[$key])) {
-        continue;
-      }
-      $doneKeys[$key] = 1;
-
+    foreach ($todoKeys as $key) {
       /** @var CRM_Extension_Info $info */
-      $info = @$infos[$key];
+      $info = $infos[$key] ?? NULL;
 
-      if ($info && $info->requires) {
-        $sorter->add($key, $info->requires);
-        $todoKeys = array_merge($todoKeys, $info->requires);
+      // Add dependencies
+      if ($info) {
+        // Filter out missing dependencies; missing modules cannot be upgraded
+        $requires = array_intersect($info->requires ?? [], $keys);
+        $sorter->add($key, $requires);
       }
+      // This shouldn't ever happen if this function is being passed a list of active extensions.
       else {
-        $sorter->add($key, []);
+        throw new CRM_Extension_Exception('Invalid extension key: "' . $key . '"');
       }
     }
     return $sorter->sort();