From 7e576e98eb1e0758c0179b669d17f6c4b9b599b0 Mon Sep 17 00:00:00 2001
From: tedbow <tedbow@240860.no-reply.drupal.org>
Date: Tue, 11 Jan 2022 21:11:18 +0000
Subject: [PATCH] Issue #3257849 by tedbow: Various core quality updates

---
 automatic_updates.info.yml                    |  2 +-
 automatic_updates.install                     |  1 +
 automatic_updates.module                      | 10 +++++--
 automatic_updates.routing.yml                 |  2 +-
 package_manager/package_manager.info.yml      |  2 +-
 package_manager/src/ComposerUtility.php       |  8 +++--
 .../src/Event/PostDestroyEvent.php            |  5 ++++
 .../src/Event/PostRequireEvent.php            |  2 +-
 package_manager/src/Event/PreDestroyEvent.php |  5 ++++
 .../src/Event/PreOperationStageEvent.php      |  2 +-
 package_manager/src/Event/PreRequireEvent.php |  2 +-
 package_manager/src/FileSyncerFactory.php     |  2 +-
 package_manager/src/ProcessFactory.php        |  2 --
 package_manager/src/Stage.php                 | 29 ++++++++++++++-----
 .../Validator/ComposerExecutableValidator.php |  2 +-
 .../src/Validator/DiskSpaceValidator.php      |  5 ++--
 .../src/Validator/LockFileValidator.php       |  2 +-
 src/Event/ReadinessCheckEvent.php             |  2 +-
 18 files changed, 57 insertions(+), 28 deletions(-)

diff --git a/automatic_updates.info.yml b/automatic_updates.info.yml
index b1f27b670e..7e3651197b 100644
--- a/automatic_updates.info.yml
+++ b/automatic_updates.info.yml
@@ -1,6 +1,6 @@
 name: 'Automatic Updates'
 type: module
-description: 'Experimental module to develop automatic updates. Currently the module provides checks for update readiness but does not yet provide update functionality.'
+description: 'Automatically updates Drupal core.'
 core_version_requirement: ^9.2
 dependencies:
   - drupal:automatic_updates_9_3_shim
diff --git a/automatic_updates.install b/automatic_updates.install
index d181ae6ac8..c17fad0c4c 100644
--- a/automatic_updates.install
+++ b/automatic_updates.install
@@ -12,6 +12,7 @@ use Drupal\automatic_updates\Validation\ReadinessRequirements;
  */
 function automatic_updates_requirements($phase) {
   if ($phase === 'runtime') {
+    // Check that site is ready to perform automatic updates.
     /** @var \Drupal\automatic_updates\Validation\ReadinessRequirements $readiness_requirement */
     $readiness_requirement = \Drupal::classResolver(ReadinessRequirements::class);
     return $readiness_requirement->getRequirements();
diff --git a/automatic_updates.module b/automatic_updates.module
index 7ed25a0bd8..d463ed3ac9 100644
--- a/automatic_updates.module
+++ b/automatic_updates.module
@@ -47,6 +47,7 @@ function automatic_updates_page_top() {
     'automatic_updates.report_update',
     'automatic_updates.module_update',
   ];
+  // @see auto_updates_module_implements_alter()
   $route_name = \Drupal::routeMatch()->getRouteName();
   if (!in_array($route_name, $skip_routes, TRUE) && function_exists('update_page_top')) {
     update_page_top();
@@ -63,8 +64,7 @@ function automatic_updates_module_implements_alter(&$implementations, $hook) {
     // Remove hook_page_top() implementation from the Update module. This '
     // implementation displays error messages about security releases. We call
     // this implementation in our own automatic_updates_page_top() except on our
-    // own routes to avoid stale messages about the security releases after an
-    // update.
+    // own routes to avoid these messages while an update is in progress.
     unset($implementations['update']);
   }
 }
@@ -114,7 +114,9 @@ function automatic_updates_modules_uninstalled() {
  */
 function automatic_updates_form_update_manager_update_form_alter(&$form, FormStateInterface $form_state, $form_id) {
   // Remove current message that core updates are not supported with a link to
-  // use this modules form.
+  // use this module's form. The local task to 'update_manager_update_form' is
+  // replaced by our own from but this original form would still accessible via
+  // by its original URL.
   if (isset($form['manual_updates']['#rows']['drupal']['data']['title'])) {
     $current_route = \Drupal::routeMatch()->getRouteName();
     if ($current_route === 'update.module_update') {
@@ -141,6 +143,8 @@ function automatic_updates_form_update_settings_alter(array &$form, FormStateInt
   $drupal_project = $recommender->getProjectInfo();
   $version = ExtensionVersion::createFromVersionString($drupal_project['existing_version']);
   $current_minor = $version->getMajorVersion() . '.' . $version->getMinorVersion();
+  // @todo In https://www.drupal.org/node/2998285 use the update XML to
+  //   determine when the installed of core will become unsupported.
   $supported_until_version = $version->getMajorVersion() . '.'
       . ((int) $version->getMinorVersion() + ProjectSecurityData::CORE_MINORS_WITH_SECURITY_COVERAGE)
       . '.0';
diff --git a/automatic_updates.routing.yml b/automatic_updates.routing.yml
index 848b3f8ce5..a3a37dab4a 100644
--- a/automatic_updates.routing.yml
+++ b/automatic_updates.routing.yml
@@ -13,7 +13,7 @@ automatic_updates.confirmation_page:
   requirements:
     _permission: 'administer software updates'
     _access_update_manager: 'TRUE'
-# Links to our updater form appear in two different sets of local tasks. To ensure the breadcrumbs and paths are
+# Links to our updater form appear in three different sets of local tasks. To ensure the breadcrumbs and paths are
 # consistent with the other local tasks in each set, we need two separate routes to the same form.
 automatic_updates.report_update:
   path: '/admin/reports/updates/automatic-update'
diff --git a/package_manager/package_manager.info.yml b/package_manager/package_manager.info.yml
index dfb5b5ffe2..ee81e2d7ab 100644
--- a/package_manager/package_manager.info.yml
+++ b/package_manager/package_manager.info.yml
@@ -1,4 +1,4 @@
 name: 'Package Manager'
 type: module
-description: 'API module providing functionality for staging package updates.'
+description: 'API module providing functionality for staging package installs and updates with Composer.'
 core_version_requirement: ^9
diff --git a/package_manager/src/ComposerUtility.php b/package_manager/src/ComposerUtility.php
index 6a111b74a9..cfab9e4087 100644
--- a/package_manager/src/ComposerUtility.php
+++ b/package_manager/src/ComposerUtility.php
@@ -48,7 +48,7 @@ class ComposerUtility {
   }
 
   /**
-   * Creates a utility object using the files in a given directory.
+   * Creates an instance of this class using the files in a given directory.
    *
    * @param string $dir
    *   The directory that contains composer.json and composer.lock.
@@ -111,7 +111,8 @@ class ComposerUtility {
    * @return string[]
    *   The names of the required core packages.
    *
-   * @todo Make this return a keyed array of packages, not just names.
+   * @todo Make this return a keyed array of packages, not just names in
+   *   https://www.drupal.org/i/3258059.
    */
   public function getCorePackageNames(): array {
     $core_packages = array_intersect(
@@ -135,7 +136,8 @@ class ComposerUtility {
    * @return string[]
    *   The names of the core packages in the dev requirements.
    *
-   * @todo Make this return a keyed array of packages, not just names.
+   * @todo Make this return a keyed array of packages, not just names in
+   *   https://www.drupal.org/i/3258059.
    */
   public function getCoreDevPackageNames(): array {
     $dev_packages = $this->composer->getPackage()->getDevRequires();
diff --git a/package_manager/src/Event/PostDestroyEvent.php b/package_manager/src/Event/PostDestroyEvent.php
index 3388a988e1..283bf69e34 100644
--- a/package_manager/src/Event/PostDestroyEvent.php
+++ b/package_manager/src/Event/PostDestroyEvent.php
@@ -4,6 +4,11 @@ namespace Drupal\package_manager\Event;
 
 /**
  * Event fired after the staging area is destroyed.
+ *
+ * If the stage is being force destroyed, ::getStage() may return an object of a
+ * different class than the one that originally created the staging area.
+ *
+ * @see \Drupal\package_manager\Stage::destroy()
  */
 class PostDestroyEvent extends StageEvent {
 }
diff --git a/package_manager/src/Event/PostRequireEvent.php b/package_manager/src/Event/PostRequireEvent.php
index 55b4184dd3..c35076f5cf 100644
--- a/package_manager/src/Event/PostRequireEvent.php
+++ b/package_manager/src/Event/PostRequireEvent.php
@@ -3,7 +3,7 @@
 namespace Drupal\package_manager\Event;
 
 /**
- * Event fired after packages are added to the staging area.
+ * Event fired after packages are updated to the staging area.
  */
 class PostRequireEvent extends StageEvent {
 }
diff --git a/package_manager/src/Event/PreDestroyEvent.php b/package_manager/src/Event/PreDestroyEvent.php
index d4918f0b8b..3a876312a2 100644
--- a/package_manager/src/Event/PreDestroyEvent.php
+++ b/package_manager/src/Event/PreDestroyEvent.php
@@ -4,6 +4,11 @@ namespace Drupal\package_manager\Event;
 
 /**
  * Event fired before the staging area is destroyed.
+ *
+ * If the stage is being force destroyed, ::getStage() may return an object of a
+ * different class than the one that originally created the staging area.
+ *
+ * @see \Drupal\package_manager\Stage::destroy()
  */
 class PreDestroyEvent extends PreOperationStageEvent {
 }
diff --git a/package_manager/src/Event/PreOperationStageEvent.php b/package_manager/src/Event/PreOperationStageEvent.php
index ee826f3ac3..832677a82e 100644
--- a/package_manager/src/Event/PreOperationStageEvent.php
+++ b/package_manager/src/Event/PreOperationStageEvent.php
@@ -11,7 +11,7 @@ use Drupal\package_manager\ValidationResult;
 abstract class PreOperationStageEvent extends StageEvent {
 
   /**
-   * {@inheritdoc}
+   * Adds error information to the event.
    */
   public function addError(array $messages, ?TranslatableMarkup $summary = NULL) {
     $this->results[] = ValidationResult::createError($messages, $summary);
diff --git a/package_manager/src/Event/PreRequireEvent.php b/package_manager/src/Event/PreRequireEvent.php
index 355bb5047f..bcb9bfc633 100644
--- a/package_manager/src/Event/PreRequireEvent.php
+++ b/package_manager/src/Event/PreRequireEvent.php
@@ -3,7 +3,7 @@
 namespace Drupal\package_manager\Event;
 
 /**
- * Event fired before packages are added to the staging area.
+ * Event fired before packages are updated to the staging area.
  */
 class PreRequireEvent extends PreOperationStageEvent {
 }
diff --git a/package_manager/src/FileSyncerFactory.php b/package_manager/src/FileSyncerFactory.php
index ee96aa1e57..f93e31bfec 100644
--- a/package_manager/src/FileSyncerFactory.php
+++ b/package_manager/src/FileSyncerFactory.php
@@ -9,7 +9,7 @@ use PhpTuf\ComposerStager\Infrastructure\FileSyncer\FileSyncerFactory as StagerF
 use Symfony\Component\Process\ExecutableFinder;
 
 /**
- * A file syncer factory which returns file syncers according to configuration.
+ * A file syncer factory which creates a file syncer according to configuration.
  */
 class FileSyncerFactory implements FileSyncerFactoryInterface {
 
diff --git a/package_manager/src/ProcessFactory.php b/package_manager/src/ProcessFactory.php
index 63d3d0d8eb..bb053beadf 100644
--- a/package_manager/src/ProcessFactory.php
+++ b/package_manager/src/ProcessFactory.php
@@ -10,8 +10,6 @@ use Symfony\Component\Process\Process;
 
 /**
  * Defines a process factory which sets the COMPOSER_HOME environment variable.
- *
- * @todo Figure out how to do this in composer_stager.
  */
 final class ProcessFactory implements ProcessFactoryInterface {
 
diff --git a/package_manager/src/Stage.php b/package_manager/src/Stage.php
index 4432089cd0..6d46783830 100644
--- a/package_manager/src/Stage.php
+++ b/package_manager/src/Stage.php
@@ -61,21 +61,21 @@ class Stage {
   protected $pathLocator;
 
   /**
-   * The beginner service from Composer Stager.
+   * The beginner service.
    *
    * @var \PhpTuf\ComposerStager\Domain\BeginnerInterface
    */
   protected $beginner;
 
   /**
-   * The stager service from Composer Stager.
+   * The stager service.
    *
    * @var \PhpTuf\ComposerStager\Domain\StagerInterface
    */
   protected $stager;
 
   /**
-   * The committer service from Composer Stager.
+   * The committer service.
    *
    * @var \PhpTuf\ComposerStager\Domain\CommitterInterface
    */
@@ -117,11 +117,11 @@ class Stage {
    * @param \Drupal\package_manager\PathLocator $path_locator
    *   The path locator service.
    * @param \PhpTuf\ComposerStager\Domain\BeginnerInterface $beginner
-   *   The beginner service from Composer Stager.
+   *   The beginner service.
    * @param \PhpTuf\ComposerStager\Domain\StagerInterface $stager
-   *   The stager service from Composer Stager.
+   *   The stager service.
    * @param \PhpTuf\ComposerStager\Domain\CommitterInterface $committer
-   *   The committer service from Composer Stager.
+   *   The committer service.
    * @param \Drupal\Core\File\FileSystemInterface $file_system
    *   The file system service.
    * @param \Symfony\Contracts\EventDispatcher\EventDispatcherInterface $event_dispatcher
@@ -199,6 +199,9 @@ class Stage {
    *   performing other operations on it. Calling code should store this ID for
    *   as long as the stage needs to exist.
    *
+   * @throws \Drupal\package_manager\Exception\StageException
+   *   Thrown if a staging area already exists.
+   *
    * @see ::claim()
    */
   public function create(): string {
@@ -246,6 +249,8 @@ class Stage {
     $this->dispatch(new PreRequireEvent($this));
     $dir = $this->getStageDirectory();
     $this->stager->stage($command, $dir);
+    // @todo Limit the update command to only packages in $constraints in
+    //   https://www.drupal.org/i/3257849.
     $this->stager->stage(['update', '--with-all-dependencies'], $dir);
     $this->dispatch(new PostRequireEvent($this));
   }
@@ -285,6 +290,9 @@ class Stage {
     // Delete all directories in parent staging directory.
     $parent_stage_dir = static::getStagingRoot();
     if (is_dir($parent_stage_dir)) {
+      // @todo Ensure that even if attempting to delete this directory throws an
+      //   exception the stage is still marked as available in
+      //   https://www.drupal.org/i/3258048.
       $this->fileSystem->deleteRecursive($parent_stage_dir, function (string $path): void {
         $this->fileSystem->chmod($path, 0777);
       });
@@ -324,6 +332,7 @@ class Stage {
       }
     }
     catch (\Throwable $error) {
+      // @todo Simplify exception handling in https://www.drupal.org/i/3258056.
       // If we are not going to be able to create the staging area, mark it as
       // available.
       // @see ::create()
@@ -407,12 +416,16 @@ class Stage {
   }
 
   /**
-   * Ensures that the current user or session owns the staging area.
+   * Validates the ownership of staging area.
+   *
+   * The stage is considered under valid ownership if it was created by current
+   * user or session, using the current class.
    *
    * @throws \LogicException
    *   If ::claim() has not been previously called.
    * @throws \Drupal\package_manager\Exception\StageOwnershipException
-   *   If the current user or session does not own the staging area.
+   *   If the current user or session does not own the staging area, or it was
+   *   created by a different class.
    */
   final protected function checkOwnership(): void {
     if (empty($this->lock)) {
diff --git a/package_manager/src/Validator/ComposerExecutableValidator.php b/package_manager/src/Validator/ComposerExecutableValidator.php
index 2ed03cd378..db516e5089 100644
--- a/package_manager/src/Validator/ComposerExecutableValidator.php
+++ b/package_manager/src/Validator/ComposerExecutableValidator.php
@@ -12,7 +12,7 @@ use PhpTuf\ComposerStager\Domain\Process\Runner\ComposerRunnerInterface;
 use PhpTuf\ComposerStager\Exception\ExceptionInterface;
 
 /**
- * Validates that the Composer executable can be found in the correct version.
+ * Validates the Composer executable is the correct version.
  */
 class ComposerExecutableValidator implements PreOperationStageValidatorInterface, OutputCallbackInterface {
 
diff --git a/package_manager/src/Validator/DiskSpaceValidator.php b/package_manager/src/Validator/DiskSpaceValidator.php
index 31441f8625..7a0a97dc4e 100644
--- a/package_manager/src/Validator/DiskSpaceValidator.php
+++ b/package_manager/src/Validator/DiskSpaceValidator.php
@@ -11,7 +11,7 @@ use Drupal\Core\StringTranslation\TranslationInterface;
 use Drupal\package_manager\PathLocator;
 
 /**
- * Validates that there is enough free disk space to do automatic updates.
+ * Validates that there is enough free disk space to do staging operations.
  */
 class DiskSpaceValidator implements PreOperationStageValidatorInterface {
 
@@ -103,7 +103,8 @@ class DiskSpaceValidator implements PreOperationStageValidatorInterface {
     $vendor_path = $this->pathLocator->getVendorDirectory();
     $messages = [];
 
-    // @todo Make this configurable.
+    // @todo Make this configurable or set to a different value in
+    //   https://www.drupal.org/i/3166416.
     $minimum_mb = 1024;
     $minimum_bytes = Bytes::toNumber($minimum_mb . 'M');
 
diff --git a/package_manager/src/Validator/LockFileValidator.php b/package_manager/src/Validator/LockFileValidator.php
index daf807e601..15a3a977d5 100644
--- a/package_manager/src/Validator/LockFileValidator.php
+++ b/package_manager/src/Validator/LockFileValidator.php
@@ -109,7 +109,7 @@ class LockFileValidator implements PreOperationStageValidatorInterface {
     }
 
     // If we have both hashes, ensure they match.
-    if ($hash && $stored_hash && hash_equals($stored_hash, $hash) == FALSE) {
+    if ($hash && $stored_hash && !hash_equals($stored_hash, $hash)) {
       $error = $this->t('Stored lock file hash does not match the active lock file.');
     }
 
diff --git a/src/Event/ReadinessCheckEvent.php b/src/Event/ReadinessCheckEvent.php
index c226ecd35d..2b8b9a6f86 100644
--- a/src/Event/ReadinessCheckEvent.php
+++ b/src/Event/ReadinessCheckEvent.php
@@ -53,7 +53,7 @@ class ReadinessCheckEvent extends PreOperationStageEvent {
   }
 
   /**
-   * {@inheritdoc}
+   * Adds warning information to the event.
    */
   public function addWarning(array $messages, ?TranslatableMarkup $summary = NULL) {
     $this->results[] = ValidationResult::createWarning($messages, $summary);
-- 
GitLab