From 0e1220077f2c6e1b4d9625f9a13ab9c20cbf4fab Mon Sep 17 00:00:00 2001
From: phenaproxima <phenaproxima@205645.no-reply.drupal.org>
Date: Fri, 1 Jul 2022 17:44:22 +0000
Subject: [PATCH] Issue #3293685 by phenaproxima: Remove need for close
 coupling of Composer Stager API in our container services

---
 composer.json                                 |   5 +-
 package_manager/package_manager.services.yml  | 111 +-----------------
 .../src/PackageManagerServiceProvider.php     |  22 ++++
 .../tests/src/Kernel/ServicesTest.php         |  14 +++
 4 files changed, 45 insertions(+), 107 deletions(-)

diff --git a/composer.json b/composer.json
index c75052d152..71c63e3b5d 100644
--- a/composer.json
+++ b/composer.json
@@ -13,9 +13,10 @@
   "require": {
     "ext-json": "*",
     "drupal/core": "^9.3",
-    "php-tuf/composer-stager": "1.0.0-beta2",
+    "php-tuf/composer-stager": "^1.0.0-beta2",
     "composer/composer": "^2.2.12 || ^2.3.5",
-    "composer-runtime-api": "^2.0.9"
+    "composer-runtime-api": "^2.0.9",
+    "symfony/config": "^4.4 || ^6.1"
   },
   "config": {
     "platform": {
diff --git a/package_manager/package_manager.services.yml b/package_manager/package_manager.services.yml
index bef3aec5c9..81a328a02d 100644
--- a/package_manager/package_manager.services.yml
+++ b/package_manager/package_manager.services.yml
@@ -5,129 +5,30 @@ services:
   Symfony\Component\Process\ExecutableFinder:
     public: false
 
-  # Basic infrastructure services for Composer Stager.
+  # Basic infrastructure services for Composer Stager, overridden by us to
+  # provide additional functionality.
   Drupal\package_manager\ProcessFactory:
     arguments:
       - '@file_system'
       - '@config.factory'
     public: false
-  PhpTuf\ComposerStager\Infrastructure\Service\Filesystem\Filesystem:
-    autowire: true
-    public: false
   Drupal\package_manager\ExecutableFinder:
     arguments:
       $config_factory: '@config.factory'
     autowire: true
     public: false
-  PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactory:
-    autowire: true
-    public: false
-  PhpTuf\ComposerStager\Infrastructure\Service\Finder\RecursiveFileFinder:
-    autowire: true
-    public: false
-  PhpTuf\ComposerStager\Infrastructure\Factory\Process\ProcessFactoryInterface:
-    alias: 'Drupal\package_manager\ProcessFactory'
-  PhpTuf\ComposerStager\Domain\Service\Filesystem\FilesystemInterface:
-    alias: 'PhpTuf\ComposerStager\Infrastructure\Service\Filesystem\Filesystem'
-  PhpTuf\ComposerStager\Infrastructure\Service\Finder\ExecutableFinderInterface:
-    alias: 'Drupal\package_manager\ExecutableFinder'
-  PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface:
-    alias: 'PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactory'
-  PhpTuf\ComposerStager\Infrastructure\Service\Finder\RecursiveFileFinderInterface:
-    alias: 'PhpTuf\ComposerStager\Infrastructure\Service\Finder\RecursiveFileFinder'
-
-  # Executable runners for Composer Stager.
-  PhpTuf\ComposerStager\Infrastructure\Service\ProcessRunner\RsyncRunner:
-    autowire: true
-    public: false
-  PhpTuf\ComposerStager\Infrastructure\Service\ProcessRunner\ComposerRunner:
-    autowire: true
-    public: false
-  PhpTuf\ComposerStager\Domain\Service\ProcessRunner\RsyncRunnerInterface:
-    alias: 'PhpTuf\ComposerStager\Infrastructure\Service\ProcessRunner\RsyncRunner'
-  PhpTuf\ComposerStager\Domain\Service\ProcessRunner\ComposerRunnerInterface:
-    alias: 'PhpTuf\ComposerStager\Infrastructure\Service\ProcessRunner\ComposerRunner'
-
-  # File syncers for Composer Stager.
-  PhpTuf\ComposerStager\Infrastructure\Service\FileSyncer\RsyncFileSyncer:
-    autowire: true
-  PhpTuf\ComposerStager\Infrastructure\Service\FileSyncer\PhpFileSyncer:
-    autowire: true
   Drupal\package_manager\FileSyncerFactory:
     arguments:
       $config_factory: '@config.factory'
     autowire: true
     public: false
+  PhpTuf\ComposerStager\Infrastructure\Service\Finder\ExecutableFinderInterface:
+    alias: 'Drupal\package_manager\ExecutableFinder'
+  PhpTuf\ComposerStager\Infrastructure\Factory\Process\ProcessFactoryInterface:
+    alias: 'Drupal\package_manager\ProcessFactory'
   PhpTuf\ComposerStager\Domain\Service\FileSyncer\FileSyncerInterface:
     factory: ['@Drupal\package_manager\FileSyncerFactory', 'create']
 
-  # Composer Stager preconditions.
-  PhpTuf\ComposerStager\Infrastructure\Aggregate\PreconditionsTree\CommonPreconditions:
-    autowire: true
-    public: false
-  PhpTuf\ComposerStager\Infrastructure\Aggregate\PreconditionsTree\BeginnerPreconditions:
-    autowire: true
-    public: false
-  PhpTuf\ComposerStager\Infrastructure\Aggregate\PreconditionsTree\StagerPreconditions:
-    autowire: true
-    public: false
-  PhpTuf\ComposerStager\Infrastructure\Aggregate\PreconditionsTree\CommitterPreconditions:
-    autowire: true
-    public: false
-  PhpTuf\ComposerStager\Infrastructure\Aggregate\PreconditionsTree\StagingDirIsReady:
-    autowire: true
-    public: false
-  PhpTuf\ComposerStager\Infrastructure\Service\Precondition\ComposerIsAvailable:
-    autowire: true
-    public: false
-  PhpTuf\ComposerStager\Infrastructure\Service\Precondition\ActiveDirExists:
-    autowire: true
-    public: false
-  PhpTuf\ComposerStager\Infrastructure\Service\Precondition\ActiveDirIsWritable:
-    autowire: true
-    public: false
-  PhpTuf\ComposerStager\Infrastructure\Service\Precondition\ActiveAndStagingDirsAreDifferent:
-    autowire: true
-    public: false
-  PhpTuf\ComposerStager\Infrastructure\Service\Precondition\StagingDirExists:
-    autowire: true
-    public: false
-  PhpTuf\ComposerStager\Infrastructure\Service\Precondition\StagingDirIsWritable:
-    autowire: true
-    public: false
-  PhpTuf\ComposerStager\Infrastructure\Service\Precondition\StagingDirDoesNotExist:
-    autowire: true
-    public: false
-  PhpTuf\ComposerStager\Infrastructure\Service\Precondition\CodeBaseContainsNoSymlinks:
-    autowire: true
-    public: false
-  PhpTuf\ComposerStager\Domain\Aggregate\PreconditionsTree\CommitterPreconditionsInterface:
-    alias: 'PhpTuf\ComposerStager\Infrastructure\Aggregate\PreconditionsTree\CommitterPreconditions'
-  PhpTuf\ComposerStager\Domain\Aggregate\PreconditionsTree\BeginnerPreconditionsInterface:
-    alias: 'PhpTuf\ComposerStager\Infrastructure\Aggregate\PreconditionsTree\BeginnerPreconditions'
-  PhpTuf\ComposerStager\Domain\Aggregate\PreconditionsTree\StagerPreconditionsInterface:
-    alias: 'PhpTuf\ComposerStager\Infrastructure\Aggregate\PreconditionsTree\StagerPreconditions'
-  PhpTuf\ComposerStager\Domain\Aggregate\PreconditionsTree\CommonPreconditionsInterface:
-    alias: 'PhpTuf\ComposerStager\Infrastructure\Aggregate\PreconditionsTree\CommonPreconditions'
-  PhpTuf\ComposerStager\Domain\Aggregate\PreconditionsTree\StagingDirIsReadyInterface:
-    alias: 'PhpTuf\ComposerStager\Infrastructure\Aggregate\PreconditionsTree\StagingDirIsReady'
-  PhpTuf\ComposerStager\Domain\Service\Precondition\ComposerIsAvailableInterface:
-    alias: 'PhpTuf\ComposerStager\Infrastructure\Service\Precondition\ComposerIsAvailable'
-  PhpTuf\ComposerStager\Domain\Service\Precondition\ActiveDirExistsInterface:
-    alias: 'PhpTuf\ComposerStager\Infrastructure\Service\Precondition\ActiveDirExists'
-  PhpTuf\ComposerStager\Domain\Service\Precondition\ActiveDirIsWritableInterface:
-    alias: 'PhpTuf\ComposerStager\Infrastructure\Service\Precondition\ActiveDirIsWritable'
-  PhpTuf\ComposerStager\Domain\Service\Precondition\ActiveAndStagingDirsAreDifferentInterface:
-    alias: 'PhpTuf\ComposerStager\Infrastructure\Service\Precondition\ActiveAndStagingDirsAreDifferent'
-  PhpTuf\ComposerStager\Domain\Service\Precondition\StagingDirExistsInterface:
-    alias: 'PhpTuf\ComposerStager\Infrastructure\Service\Precondition\StagingDirExists'
-  PhpTuf\ComposerStager\Domain\Service\Precondition\StagingDirIsWritableInterface:
-    alias: 'PhpTuf\ComposerStager\Infrastructure\Service\Precondition\StagingDirIsWritable'
-  PhpTuf\ComposerStager\Domain\Service\Precondition\StagingDirDoesNotExistInterface:
-    alias: 'PhpTuf\ComposerStager\Infrastructure\Service\Precondition\StagingDirDoesNotExist'
-  PhpTuf\ComposerStager\Domain\Service\Precondition\CodebaseContainsNoSymlinksInterface:
-    alias: 'PhpTuf\ComposerStager\Infrastructure\Service\Precondition\CodeBaseContainsNoSymlinks'
-
   # Services provided to Drupal by Package Manager.
   package_manager.beginner:
     class: PhpTuf\ComposerStager\Domain\Core\Beginner\Beginner
diff --git a/package_manager/src/PackageManagerServiceProvider.php b/package_manager/src/PackageManagerServiceProvider.php
index 8e28cca14c..91960594e5 100644
--- a/package_manager/src/PackageManagerServiceProvider.php
+++ b/package_manager/src/PackageManagerServiceProvider.php
@@ -5,6 +5,10 @@ namespace Drupal\package_manager;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\DependencyInjection\ServiceProviderBase;
 use Drupal\package_manager\EventSubscriber\UpdateDataSubscriber;
+use PhpTuf\ComposerStager\Domain\Core\Beginner\BeginnerInterface;
+use Symfony\Component\Config\FileLocator;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Loader\DirectoryLoader;
 use Symfony\Component\DependencyInjection\Reference;
 
 /**
@@ -23,6 +27,24 @@ final class PackageManagerServiceProvider extends ServiceProviderBase {
   public function register(ContainerBuilder $container) {
     parent::register($container);
 
+    // Use an interface that we know exists to determine the absolute path where
+    // Composer Stager is installed.
+    $mirror = new \ReflectionClass(BeginnerInterface::class);
+    $path = dirname($mirror->getFileName(), 4);
+
+    // Recursively register all classes and interfaces under that directory,
+    // relative to the \PhpTuf\ComposerStager namespace.
+    $loader = new DirectoryLoader($container, new FileLocator());
+    // All the registered services should be auto-wired and private by default.
+    $default_definition = new Definition();
+    $default_definition->setAutowired(TRUE);
+    $default_definition->setPublic(FALSE);
+    $loader->registerClasses($default_definition, 'PhpTuf\ComposerStager\\', $path, [
+      // Ignore classes which we don't want to register as services.
+      $path . '/Domain/Exception',
+      $path . '/Infrastructure/Value',
+    ]);
+
     if (array_key_exists('update', $container->getParameter('container.modules'))) {
       $container->register('package_manager.update_data_subscriber')
         ->setClass(UpdateDataSubscriber::class)
diff --git a/package_manager/tests/src/Kernel/ServicesTest.php b/package_manager/tests/src/Kernel/ServicesTest.php
index ffa23a8a9c..ef91a3c215 100644
--- a/package_manager/tests/src/Kernel/ServicesTest.php
+++ b/package_manager/tests/src/Kernel/ServicesTest.php
@@ -3,6 +3,10 @@
 namespace Drupal\Tests\package_manager\Kernel;
 
 use Drupal\KernelTests\KernelTestBase;
+use Drupal\package_manager\ExecutableFinder;
+use Drupal\package_manager\ProcessFactory;
+use PhpTuf\ComposerStager\Infrastructure\Factory\Process\ProcessFactoryInterface;
+use PhpTuf\ComposerStager\Infrastructure\Service\Finder\ExecutableFinderInterface;
 
 /**
  * Tests that Package Manager services are wired correctly.
@@ -28,6 +32,16 @@ class ServicesTest extends KernelTestBase {
     foreach ($services as $service) {
       $this->assertIsObject($this->container->get($service));
     }
+
+    // Ensure that any overridden Composer Stager services were overridden
+    // correctly.
+    $overrides = [
+      ExecutableFinderInterface::class => ExecutableFinder::class,
+      ProcessFactoryInterface::class => ProcessFactory::class,
+    ];
+    foreach ($overrides as $interface => $expected_class) {
+      $this->assertInstanceOf($expected_class, $this->container->get($interface));
+    }
   }
 
 }
-- 
GitLab