From 7c5e93ad4679faf005aed5de0f446226f5d3ee86 Mon Sep 17 00:00:00 2001
From: phenaproxima <phenaproxima@205645.no-reply.drupal.org>
Date: Tue, 7 Jun 2022 20:03:13 +0000
Subject: [PATCH] Issue #3277211 by phenaproxima: Update Composer Stager to
 0.5.0

---
 automatic_updates.services.yml                |   2 +
 .../automatic_updates_extensions.services.yml |   1 +
 composer.json                                 |   2 +-
 package_manager/package_manager.install       |   2 +-
 package_manager/package_manager.services.yml  | 103 +++++++++++++++---
 package_manager/src/ExecutableFinder.php      |   6 +-
 package_manager/src/FileSyncerFactory.php     |  21 ++--
 .../src/PackageManagerUninstallValidator.php  |   4 +-
 package_manager/src/ProcessFactory.php        |   6 +-
 package_manager/src/Stage.php                 |  53 +++++----
 .../Validator/ComposerExecutableValidator.php |  12 +-
 .../package_manager_bypass/src/Beginner.php   |   9 +-
 .../package_manager_bypass/src/Committer.php  |  33 +-----
 .../package_manager_bypass/src/Stager.php     |   8 +-
 .../src/ApiController.php                     |   4 +-
 .../ComposerExecutableValidatorTest.php       |   6 +-
 .../tests/src/Kernel/ExecutableFinderTest.php |   2 +-
 .../src/Kernel/FileSyncerFactoryTest.php      |   6 +-
 .../Kernel/PackageManagerKernelTestBase.php   |  46 +++++++-
 19 files changed, 220 insertions(+), 106 deletions(-)

diff --git a/automatic_updates.services.yml b/automatic_updates.services.yml
index a5e5545c3a..5d0fc38d79 100644
--- a/automatic_updates.services.yml
+++ b/automatic_updates.services.yml
@@ -26,6 +26,7 @@ services:
       - '@event_dispatcher'
       - '@tempstore.shared'
       - '@datetime.time'
+      - '@PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface'
   automatic_updates.cron_updater:
     class: Drupal\automatic_updates\CronUpdater
     arguments:
@@ -40,6 +41,7 @@ services:
       - '@event_dispatcher'
       - '@tempstore.shared'
       - '@datetime.time'
+      - '@PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface'
   automatic_updates.staged_projects_validator:
     class: Drupal\automatic_updates\Validator\StagedProjectsValidator
     arguments:
diff --git a/automatic_updates_extensions/automatic_updates_extensions.services.yml b/automatic_updates_extensions/automatic_updates_extensions.services.yml
index dea4d1ff45..2e4867dab5 100644
--- a/automatic_updates_extensions/automatic_updates_extensions.services.yml
+++ b/automatic_updates_extensions/automatic_updates_extensions.services.yml
@@ -11,6 +11,7 @@ services:
       - '@event_dispatcher'
       - '@tempstore.shared'
       - '@datetime.time'
+      - '@PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface'
   automatic_updates_extensions.validator.target_release:
     class: Drupal\automatic_updates_extensions\Validator\UpdateReleaseValidator
     tags:
diff --git a/composer.json b/composer.json
index 1d6b9da4c2..5027023d50 100644
--- a/composer.json
+++ b/composer.json
@@ -13,7 +13,7 @@
   "require": {
     "ext-json": "*",
     "drupal/core": "^9.2",
-    "php-tuf/composer-stager": "0.3.0",
+    "php-tuf/composer-stager": "0.5.0",
     "composer/composer": "^2.2.12 || ^2.3.5",
     "composer-runtime-api": "^2.0.9"
   },
diff --git a/package_manager/package_manager.install b/package_manager/package_manager.install
index c4fb7265e2..a533010e3c 100644
--- a/package_manager/package_manager.install
+++ b/package_manager/package_manager.install
@@ -11,7 +11,7 @@
 function package_manager_requirements(string $phase) {
   $requirements = [];
 
-  if (!class_exists('PhpTuf\ComposerStager\Domain\Beginner')) {
+  if (!class_exists('\PhpTuf\ComposerStager\Domain\Core\Beginner\Beginner')) {
     $requirements['package_manager_composer_dependencies'] = [
       'title' => 'Missing dependency',
       'description' => t('External dependencies for Package Manager are not available. Composer must be used to download the module with dependencies.'),
diff --git a/package_manager/package_manager.services.yml b/package_manager/package_manager.services.yml
index 31701a6b04..60f7142c4a 100644
--- a/package_manager/package_manager.services.yml
+++ b/package_manager/package_manager.services.yml
@@ -11,7 +11,7 @@ services:
       - '@file_system'
       - '@config.factory'
     public: false
-  PhpTuf\ComposerStager\Infrastructure\Filesystem\Filesystem:
+  PhpTuf\ComposerStager\Infrastructure\Service\Filesystem\Filesystem:
     autowire: true
     public: false
   Drupal\package_manager\ExecutableFinder:
@@ -19,47 +19,114 @@ services:
       $config_factory: '@config.factory'
     autowire: true
     public: false
-  PhpTuf\ComposerStager\Infrastructure\Process\ProcessFactoryInterface:
+  PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactory:
+    autowire: true
+    public: false
+  PhpTuf\ComposerStager\Infrastructure\Factory\Process\ProcessFactoryInterface:
     alias: 'Drupal\package_manager\ProcessFactory'
-  PhpTuf\ComposerStager\Domain\Filesystem\FilesystemInterface:
-    alias: 'PhpTuf\ComposerStager\Infrastructure\Filesystem\Filesystem'
-  PhpTuf\ComposerStager\Infrastructure\Process\ExecutableFinderInterface:
+  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'
 
   # Executable runners for Composer Stager.
-  PhpTuf\ComposerStager\Infrastructure\Process\Runner\RsyncRunner:
+  PhpTuf\ComposerStager\Infrastructure\Service\ProcessRunner\RsyncRunner:
     autowire: true
     public: false
-  PhpTuf\ComposerStager\Infrastructure\Process\Runner\ComposerRunner:
+  PhpTuf\ComposerStager\Infrastructure\Service\ProcessRunner\ComposerRunner:
     autowire: true
     public: false
-  PhpTuf\ComposerStager\Domain\Process\Runner\RsyncRunnerInterface:
-    alias: 'PhpTuf\ComposerStager\Infrastructure\Process\Runner\RsyncRunner'
-  PhpTuf\ComposerStager\Domain\Process\Runner\ComposerRunnerInterface:
-    alias: 'PhpTuf\ComposerStager\Infrastructure\Process\Runner\ComposerRunner'
+  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\FileSyncer\RsyncFileSyncer:
+  PhpTuf\ComposerStager\Infrastructure\Service\FileSyncer\RsyncFileSyncer:
     autowire: true
-  PhpTuf\ComposerStager\Infrastructure\FileSyncer\PhpFileSyncer:
+  PhpTuf\ComposerStager\Infrastructure\Service\FileSyncer\PhpFileSyncer:
     autowire: true
   Drupal\package_manager\FileSyncerFactory:
     arguments:
       $config_factory: '@config.factory'
     autowire: true
     public: false
-  PhpTuf\ComposerStager\Domain\FileSyncer\FileSyncerInterface:
+  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\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'
+
   # Services provided to Drupal by Package Manager.
   package_manager.beginner:
-    class: PhpTuf\ComposerStager\Domain\Beginner
+    class: PhpTuf\ComposerStager\Domain\Core\Beginner\Beginner
     autowire: true
   package_manager.stager:
-    class: PhpTuf\ComposerStager\Domain\Stager
+    class: PhpTuf\ComposerStager\Domain\Core\Stager\Stager
     autowire: true
   package_manager.committer:
-    class: PhpTuf\ComposerStager\Domain\Committer
+    class: PhpTuf\ComposerStager\Domain\Core\Committer\Committer
     autowire: true
   package_manager.path_locator:
     class: Drupal\package_manager\PathLocator
@@ -70,7 +137,7 @@ services:
   package_manager.validator.composer_executable:
     class: Drupal\package_manager\Validator\ComposerExecutableValidator
     arguments:
-      - '@PhpTuf\ComposerStager\Domain\Process\Runner\ComposerRunnerInterface'
+      - '@PhpTuf\ComposerStager\Domain\Service\ProcessRunner\ComposerRunnerInterface'
       - '@module_handler'
       - '@string_translation'
     tags:
diff --git a/package_manager/src/ExecutableFinder.php b/package_manager/src/ExecutableFinder.php
index d18f45be13..05c03183d5 100644
--- a/package_manager/src/ExecutableFinder.php
+++ b/package_manager/src/ExecutableFinder.php
@@ -3,8 +3,8 @@
 namespace Drupal\package_manager;
 
 use Drupal\Core\Config\ConfigFactoryInterface;
-use PhpTuf\ComposerStager\Infrastructure\Process\ExecutableFinderInterface;
-use PhpTuf\ComposerStager\Infrastructure\Process\ExecutableFinder as StagerExecutableFinder;
+use PhpTuf\ComposerStager\Infrastructure\Service\Finder\ExecutableFinder as StagerExecutableFinder;
+use PhpTuf\ComposerStager\Infrastructure\Service\Finder\ExecutableFinderInterface;
 use Symfony\Component\Process\ExecutableFinder as SymfonyExecutableFinder;
 
 /**
@@ -15,7 +15,7 @@ class ExecutableFinder implements ExecutableFinderInterface {
   /**
    * The decorated executable finder.
    *
-   * @var \PhpTuf\ComposerStager\Infrastructure\Process\ExecutableFinder
+   * @var \PhpTuf\ComposerStager\Infrastructure\Service\Finder\ExecutableFinder
    */
   private $decorated;
 
diff --git a/package_manager/src/FileSyncerFactory.php b/package_manager/src/FileSyncerFactory.php
index 65fdfbcb73..98a28f545c 100644
--- a/package_manager/src/FileSyncerFactory.php
+++ b/package_manager/src/FileSyncerFactory.php
@@ -3,36 +3,35 @@
 namespace Drupal\package_manager;
 
 use Drupal\Core\Config\ConfigFactoryInterface;
-use PhpTuf\ComposerStager\Domain\FileSyncer\FileSyncerFactoryInterface;
-use PhpTuf\ComposerStager\Domain\FileSyncer\FileSyncerInterface;
-use PhpTuf\ComposerStager\Infrastructure\FileSyncer\FileSyncerFactory as StagerFileSyncerFactory;
-use PhpTuf\ComposerStager\Infrastructure\FileSyncer\PhpFileSyncer;
-use PhpTuf\ComposerStager\Infrastructure\FileSyncer\RsyncFileSyncer;
+use PhpTuf\ComposerStager\Domain\Service\FileSyncer\FileSyncerInterface;
+use PhpTuf\ComposerStager\Infrastructure\Factory\FileSyncer\FileSyncerFactory as StagerFileSyncerFactory;
+use PhpTuf\ComposerStager\Infrastructure\Service\FileSyncer\PhpFileSyncer;
+use PhpTuf\ComposerStager\Infrastructure\Service\FileSyncer\RsyncFileSyncer;
 use Symfony\Component\Process\ExecutableFinder;
 
 /**
  * A file syncer factory which creates a file syncer according to configuration.
  */
-class FileSyncerFactory implements FileSyncerFactoryInterface {
+class FileSyncerFactory {
 
   /**
    * The decorated file syncer factory.
    *
-   * @var \PhpTuf\ComposerStager\Domain\FileSyncer\FileSyncerFactoryInterface
+   * @var \PhpTuf\ComposerStager\Infrastructure\Factory\FileSyncer\FileSyncerFactory
    */
   protected $decorated;
 
   /**
    * The PHP file syncer service.
    *
-   * @var \PhpTuf\ComposerStager\Infrastructure\FileSyncer\PhpFileSyncer
+   * @var \PhpTuf\ComposerStager\Infrastructure\Service\FileSyncer\PhpFileSyncer
    */
   protected $phpFileSyncer;
 
   /**
    * The rsync file syncer service.
    *
-   * @var \PhpTuf\ComposerStager\Infrastructure\FileSyncer\RsyncFileSyncer
+   * @var \PhpTuf\ComposerStager\Infrastructure\Service\FileSyncer\RsyncFileSyncer
    */
   protected $rsyncFileSyncer;
 
@@ -48,9 +47,9 @@ class FileSyncerFactory implements FileSyncerFactoryInterface {
    *
    * @param \Symfony\Component\Process\ExecutableFinder $executable_finder
    *   The Symfony executable finder.
-   * @param \PhpTuf\ComposerStager\Infrastructure\FileSyncer\PhpFileSyncer $php_file_syncer
+   * @param \PhpTuf\ComposerStager\Infrastructure\Service\FileSyncer\PhpFileSyncer $php_file_syncer
    *   The PHP file syncer service.
-   * @param \PhpTuf\ComposerStager\Infrastructure\FileSyncer\RsyncFileSyncer $rsync_file_syncer
+   * @param \PhpTuf\ComposerStager\Infrastructure\Service\FileSyncer\RsyncFileSyncer $rsync_file_syncer
    *   The rsync file syncer service.
    * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
    *   The config factory service.
diff --git a/package_manager/src/PackageManagerUninstallValidator.php b/package_manager/src/PackageManagerUninstallValidator.php
index dd9ab87607..8348ed090d 100644
--- a/package_manager/src/PackageManagerUninstallValidator.php
+++ b/package_manager/src/PackageManagerUninstallValidator.php
@@ -4,6 +4,7 @@ namespace Drupal\package_manager;
 
 use Drupal\Core\Extension\ModuleUninstallValidatorInterface;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
+use PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface;
 use Symfony\Component\DependencyInjection\ContainerAwareInterface;
 use Symfony\Component\DependencyInjection\ContainerAwareTrait;
 
@@ -28,7 +29,8 @@ class PackageManagerUninstallValidator implements ModuleUninstallValidatorInterf
       $this->container->get('file_system'),
       $this->container->get('event_dispatcher'),
       $this->container->get('tempstore.shared'),
-      $this->container->get('datetime.time')
+      $this->container->get('datetime.time'),
+      $this->container->get(PathFactoryInterface::class)
     );
     if ($stage->isAvailable() || !$stage->isApplying()) {
       return [];
diff --git a/package_manager/src/ProcessFactory.php b/package_manager/src/ProcessFactory.php
index fa1a8565d0..d7089d80dc 100644
--- a/package_manager/src/ProcessFactory.php
+++ b/package_manager/src/ProcessFactory.php
@@ -4,8 +4,8 @@ namespace Drupal\package_manager;
 
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\File\FileSystemInterface;
-use PhpTuf\ComposerStager\Infrastructure\Process\ProcessFactory as StagerProcessFactory;
-use PhpTuf\ComposerStager\Infrastructure\Process\ProcessFactoryInterface;
+use PhpTuf\ComposerStager\Infrastructure\Factory\Process\ProcessFactoryInterface;
+use PhpTuf\ComposerStager\Infrastructure\Factory\Process\ProcessFactory as StagerProcessFactory;
 use Symfony\Component\Process\Process;
 
 /**
@@ -16,7 +16,7 @@ final class ProcessFactory implements ProcessFactoryInterface {
   /**
    * The decorated process factory.
    *
-   * @var \PhpTuf\ComposerStager\Infrastructure\Process\ProcessFactoryInterface
+   * @var \PhpTuf\ComposerStager\Infrastructure\Factory\Process\ProcessFactoryInterface
    */
   private $decorated;
 
diff --git a/package_manager/src/Stage.php b/package_manager/src/Stage.php
index 49acc42a4f..c8194d7ae4 100644
--- a/package_manager/src/Stage.php
+++ b/package_manager/src/Stage.php
@@ -21,9 +21,11 @@ use Drupal\package_manager\Event\StageEvent;
 use Drupal\package_manager\Exception\StageException;
 use Drupal\package_manager\Exception\StageOwnershipException;
 use Drupal\package_manager\Exception\StageValidationException;
-use PhpTuf\ComposerStager\Domain\BeginnerInterface;
-use PhpTuf\ComposerStager\Domain\CommitterInterface;
-use PhpTuf\ComposerStager\Domain\StagerInterface;
+use PhpTuf\ComposerStager\Domain\Core\Beginner\BeginnerInterface;
+use PhpTuf\ComposerStager\Domain\Core\Committer\CommitterInterface;
+use PhpTuf\ComposerStager\Domain\Core\Stager\StagerInterface;
+use PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface;
+use PhpTuf\ComposerStager\Infrastructure\Value\PathList\PathList;
 use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
 
 /**
@@ -102,21 +104,21 @@ class Stage {
   /**
    * The beginner service.
    *
-   * @var \PhpTuf\ComposerStager\Domain\BeginnerInterface
+   * @var \PhpTuf\ComposerStager\Domain\Core\Beginner\BeginnerInterface
    */
   protected $beginner;
 
   /**
    * The stager service.
    *
-   * @var \PhpTuf\ComposerStager\Domain\StagerInterface
+   * @var \PhpTuf\ComposerStager\Domain\Core\Stager\StagerInterface
    */
   protected $stager;
 
   /**
    * The committer service.
    *
-   * @var \PhpTuf\ComposerStager\Domain\CommitterInterface
+   * @var \PhpTuf\ComposerStager\Domain\Core\Committer\CommitterInterface
    */
   protected $committer;
 
@@ -148,6 +150,13 @@ class Stage {
    */
   protected $time;
 
+  /**
+   * The path factory service.
+   *
+   * @var \PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface
+   */
+  protected $pathFactory;
+
   /**
    * The lock info for the stage.
    *
@@ -164,11 +173,11 @@ class Stage {
    *   The config factory service.
    * @param \Drupal\package_manager\PathLocator $path_locator
    *   The path locator service.
-   * @param \PhpTuf\ComposerStager\Domain\BeginnerInterface $beginner
+   * @param \PhpTuf\ComposerStager\Domain\Core\Beginner\BeginnerInterface $beginner
    *   The beginner service.
-   * @param \PhpTuf\ComposerStager\Domain\StagerInterface $stager
+   * @param \PhpTuf\ComposerStager\Domain\Core\Stager\StagerInterface $stager
    *   The stager service.
-   * @param \PhpTuf\ComposerStager\Domain\CommitterInterface $committer
+   * @param \PhpTuf\ComposerStager\Domain\Core\Committer\CommitterInterface $committer
    *   The committer service.
    * @param \Drupal\Core\File\FileSystemInterface $file_system
    *   The file system service.
@@ -178,8 +187,10 @@ class Stage {
    *   The shared tempstore factory.
    * @param \Drupal\Component\Datetime\TimeInterface $time
    *   The time service.
+   * @param \PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface $path_factory
+   *   The path factory service.
    */
-  public function __construct(ConfigFactoryInterface $config_factory, PathLocator $path_locator, BeginnerInterface $beginner, StagerInterface $stager, CommitterInterface $committer, FileSystemInterface $file_system, EventDispatcherInterface $event_dispatcher, SharedTempStoreFactory $shared_tempstore, TimeInterface $time) {
+  public function __construct(ConfigFactoryInterface $config_factory, PathLocator $path_locator, BeginnerInterface $beginner, StagerInterface $stager, CommitterInterface $committer, FileSystemInterface $file_system, EventDispatcherInterface $event_dispatcher, SharedTempStoreFactory $shared_tempstore, TimeInterface $time, PathFactoryInterface $path_factory) {
     $this->configFactory = $config_factory;
     $this->pathLocator = $path_locator;
     $this->beginner = $beginner;
@@ -189,6 +200,7 @@ class Stage {
     $this->eventDispatcher = $event_dispatcher;
     $this->time = $time;
     $this->tempStore = $shared_tempstore->get('package_manager_stage');
+    $this->pathFactory = $path_factory;
   }
 
   /**
@@ -275,15 +287,15 @@ class Stage {
     $this->tempStore->set(static::TEMPSTORE_LOCK_KEY, [$id, static::class]);
     $this->claim($id);
 
-    $active_dir = $this->pathLocator->getProjectRoot();
-    $stage_dir = $this->getStageDirectory();
+    $active_dir = $this->pathFactory->create($this->pathLocator->getProjectRoot());
+    $stage_dir = $this->pathFactory->create($this->getStageDirectory());
 
     $event = new PreCreateEvent($this);
     // If an error occurs and we won't be able to create the stage, mark it as
     // available.
     $this->dispatch($event, [$this, 'markAsAvailable']);
 
-    $this->beginner->begin($active_dir, $stage_dir, $event->getExcludedPaths(), NULL, $timeout);
+    $this->beginner->begin($active_dir, $stage_dir, new PathList($event->getExcludedPaths()), NULL, $timeout);
     $this->dispatch(new PostCreateEvent($this));
     return $id;
   }
@@ -302,23 +314,24 @@ class Stage {
     $this->checkOwnership();
 
     $this->dispatch(new PreRequireEvent($this, $runtime, $dev));
-    $dir = $this->getStageDirectory();
+    $active_dir = $this->pathFactory->create($this->pathLocator->getProjectRoot());
+    $stage_dir = $this->pathFactory->create($this->getStageDirectory());
 
     // Change the runtime and dev requirements as needed, but don't update
     // the installed packages yet.
     if ($runtime) {
       $command = array_merge(['require', '--no-update'], $runtime);
-      $this->stager->stage($command, $dir);
+      $this->stager->stage($command, $active_dir, $stage_dir);
     }
     if ($dev) {
       $command = array_merge(['require', '--dev', '--no-update'], $dev);
-      $this->stager->stage($command, $dir);
+      $this->stager->stage($command, $active_dir, $stage_dir);
     }
 
     // If constraints were changed, update those packages.
     if ($runtime || $dev) {
       $command = array_merge(['update', '--with-all-dependencies'], $runtime, $dev);
-      $this->stager->stage($command, $dir);
+      $this->stager->stage($command, $active_dir, $stage_dir);
     }
 
     $this->dispatch(new PostRequireEvent($this, $runtime, $dev));
@@ -335,8 +348,8 @@ class Stage {
   public function apply(?int $timeout = 600): void {
     $this->checkOwnership();
 
-    $active_dir = $this->pathLocator->getProjectRoot();
-    $stage_dir = $this->getStageDirectory();
+    $active_dir = $this->pathFactory->create($this->pathLocator->getProjectRoot());
+    $stage_dir = $this->pathFactory->create($this->getStageDirectory());
 
     // If an error occurs while dispatching the events, ensure that ::destroy()
     // doesn't think we're in the middle of applying the staged changes to the
@@ -349,7 +362,7 @@ class Stage {
     $this->tempStore->set(self::TEMPSTORE_APPLY_TIME_KEY, $this->time->getRequestTime());
     $this->dispatch($event, $release_apply);
 
-    $this->committer->commit($stage_dir, $active_dir, $event->getExcludedPaths(), NULL, $timeout);
+    $this->committer->commit($stage_dir, $active_dir, new PathList($event->getExcludedPaths()), NULL, $timeout);
 
     // Rebuild the container and clear all caches, to ensure that new services
     // are picked up.
diff --git a/package_manager/src/Validator/ComposerExecutableValidator.php b/package_manager/src/Validator/ComposerExecutableValidator.php
index d985321bd4..b6252e5bf8 100644
--- a/package_manager/src/Validator/ComposerExecutableValidator.php
+++ b/package_manager/src/Validator/ComposerExecutableValidator.php
@@ -9,14 +9,14 @@ use Drupal\package_manager\Event\PreCreateEvent;
 use Drupal\package_manager\Event\PreOperationStageEvent;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Core\StringTranslation\TranslationInterface;
-use PhpTuf\ComposerStager\Domain\Process\OutputCallbackInterface;
-use PhpTuf\ComposerStager\Domain\Process\Runner\ComposerRunnerInterface;
-use PhpTuf\ComposerStager\Exception\ExceptionInterface;
+use PhpTuf\ComposerStager\Domain\Exception\ExceptionInterface;
+use PhpTuf\ComposerStager\Domain\Service\ProcessOutputCallback\ProcessOutputCallbackInterface;
+use PhpTuf\ComposerStager\Domain\Service\ProcessRunner\ComposerRunnerInterface;
 
 /**
  * Validates the Composer executable is the correct version.
  */
-class ComposerExecutableValidator implements PreOperationStageValidatorInterface, OutputCallbackInterface {
+class ComposerExecutableValidator implements PreOperationStageValidatorInterface, ProcessOutputCallbackInterface {
 
   use StringTranslationTrait;
 
@@ -30,7 +30,7 @@ class ComposerExecutableValidator implements PreOperationStageValidatorInterface
   /**
    * The Composer runner.
    *
-   * @var \PhpTuf\ComposerStager\Domain\Process\Runner\ComposerRunnerInterface
+   * @var \PhpTuf\ComposerStager\Domain\Service\ProcessRunner\ComposerRunnerInterface
    */
   protected $composer;
 
@@ -51,7 +51,7 @@ class ComposerExecutableValidator implements PreOperationStageValidatorInterface
   /**
    * Constructs a ComposerExecutableValidator object.
    *
-   * @param \PhpTuf\ComposerStager\Domain\Process\Runner\ComposerRunnerInterface $composer
+   * @param \PhpTuf\ComposerStager\Domain\Service\ProcessRunner\ComposerRunnerInterface $composer
    *   The Composer runner.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
    *   The module handler service.
diff --git a/package_manager/tests/modules/package_manager_bypass/src/Beginner.php b/package_manager/tests/modules/package_manager_bypass/src/Beginner.php
index 5fc291a1ee..6509c0fcf1 100644
--- a/package_manager/tests/modules/package_manager_bypass/src/Beginner.php
+++ b/package_manager/tests/modules/package_manager_bypass/src/Beginner.php
@@ -2,8 +2,11 @@
 
 namespace Drupal\package_manager_bypass;
 
-use PhpTuf\ComposerStager\Domain\BeginnerInterface;
-use PhpTuf\ComposerStager\Domain\Process\OutputCallbackInterface;
+use PhpTuf\ComposerStager\Domain\Core\Beginner\BeginnerInterface;
+use PhpTuf\ComposerStager\Domain\Service\ProcessOutputCallback\ProcessOutputCallbackInterface;
+use PhpTuf\ComposerStager\Domain\Service\ProcessRunner\ProcessRunnerInterface;
+use PhpTuf\ComposerStager\Domain\Value\Path\PathInterface;
+use PhpTuf\ComposerStager\Domain\Value\PathList\PathListInterface;
 
 /**
  * Defines an update beginner which doesn't do anything.
@@ -13,7 +16,7 @@ class Beginner extends InvocationRecorderBase implements BeginnerInterface {
   /**
    * {@inheritdoc}
    */
-  public function begin(string $activeDir, string $stagingDir, ?array $exclusions = [], ?OutputCallbackInterface $callback = NULL, ?int $timeout = 120): void {
+  public function begin(PathInterface $activeDir, PathInterface $stagingDir, ?PathListInterface $exclusions = NULL, ?ProcessOutputCallbackInterface $callback = NULL, ?int $timeout = ProcessRunnerInterface::DEFAULT_TIMEOUT): void {
     $this->saveInvocationArguments($activeDir, $stagingDir, $exclusions, $timeout);
   }
 
diff --git a/package_manager/tests/modules/package_manager_bypass/src/Committer.php b/package_manager/tests/modules/package_manager_bypass/src/Committer.php
index f5f40847e4..ea528824a9 100644
--- a/package_manager/tests/modules/package_manager_bypass/src/Committer.php
+++ b/package_manager/tests/modules/package_manager_bypass/src/Committer.php
@@ -2,43 +2,22 @@
 
 namespace Drupal\package_manager_bypass;
 
-use PhpTuf\ComposerStager\Domain\CommitterInterface;
-use PhpTuf\ComposerStager\Domain\Process\OutputCallbackInterface;
+use PhpTuf\ComposerStager\Domain\Core\Committer\CommitterInterface;
+use PhpTuf\ComposerStager\Domain\Service\ProcessOutputCallback\ProcessOutputCallbackInterface;
+use PhpTuf\ComposerStager\Domain\Service\ProcessRunner\ProcessRunnerInterface;
+use PhpTuf\ComposerStager\Domain\Value\Path\PathInterface;
+use PhpTuf\ComposerStager\Domain\Value\PathList\PathListInterface;
 
 /**
  * Defines an update committer which doesn't do any actual committing.
  */
 class Committer extends InvocationRecorderBase implements CommitterInterface {
 
-  /**
-   * The decorated committer service.
-   *
-   * @var \PhpTuf\ComposerStager\Domain\CommitterInterface
-   */
-  private $decorated;
-
-  /**
-   * Constructs a Committer object.
-   *
-   * @param \PhpTuf\ComposerStager\Domain\CommitterInterface $decorated
-   *   The decorated committer service.
-   */
-  public function __construct(CommitterInterface $decorated) {
-    $this->decorated = $decorated;
-  }
-
   /**
    * {@inheritdoc}
    */
-  public function commit(string $stagingDir, string $activeDir, ?array $exclusions = [], ?OutputCallbackInterface $callback = NULL, ?int $timeout = 120): void {
+  public function commit(PathInterface $stagingDir, PathInterface $activeDir, ?PathListInterface $exclusions = NULL, ?ProcessOutputCallbackInterface $callback = NULL, ?int $timeout = ProcessRunnerInterface::DEFAULT_TIMEOUT): void {
     $this->saveInvocationArguments($stagingDir, $activeDir, $exclusions, $timeout);
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function directoryExists(string $stagingDir): bool {
-    return $this->decorated->directoryExists($stagingDir);
-  }
-
 }
diff --git a/package_manager/tests/modules/package_manager_bypass/src/Stager.php b/package_manager/tests/modules/package_manager_bypass/src/Stager.php
index 237eccab38..38a67fb3e7 100644
--- a/package_manager/tests/modules/package_manager_bypass/src/Stager.php
+++ b/package_manager/tests/modules/package_manager_bypass/src/Stager.php
@@ -2,8 +2,10 @@
 
 namespace Drupal\package_manager_bypass;
 
-use PhpTuf\ComposerStager\Domain\Process\OutputCallbackInterface;
-use PhpTuf\ComposerStager\Domain\StagerInterface;
+use PhpTuf\ComposerStager\Domain\Core\Stager\StagerInterface;
+use PhpTuf\ComposerStager\Domain\Service\ProcessOutputCallback\ProcessOutputCallbackInterface;
+use PhpTuf\ComposerStager\Domain\Service\ProcessRunner\ProcessRunnerInterface;
+use PhpTuf\ComposerStager\Domain\Value\Path\PathInterface;
 
 /**
  * Defines an update stager which doesn't actually do anything.
@@ -13,7 +15,7 @@ class Stager extends InvocationRecorderBase implements StagerInterface {
   /**
    * {@inheritdoc}
    */
-  public function stage(array $composerCommand, string $stagingDir, ?OutputCallbackInterface $callback = NULL, ?int $timeout = 120): void {
+  public function stage(array $composerCommand, PathInterface $activeDir, PathInterface $stagingDir, ?ProcessOutputCallbackInterface $callback = NULL, ?int $timeout = ProcessRunnerInterface::DEFAULT_TIMEOUT): void {
     $this->saveInvocationArguments($composerCommand, $stagingDir);
   }
 
diff --git a/package_manager/tests/modules/package_manager_test_api/src/ApiController.php b/package_manager/tests/modules/package_manager_test_api/src/ApiController.php
index 45eb27f0f8..2d586cdf6f 100644
--- a/package_manager/tests/modules/package_manager_test_api/src/ApiController.php
+++ b/package_manager/tests/modules/package_manager_test_api/src/ApiController.php
@@ -5,6 +5,7 @@ namespace Drupal\package_manager_test_api;
 use Drupal\Core\Controller\ControllerBase;
 use Drupal\package_manager\PathLocator;
 use Drupal\package_manager\Stage;
+use PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\JsonResponse;
 use Symfony\Component\HttpFoundation\Request;
@@ -54,7 +55,8 @@ class ApiController extends ControllerBase {
       $container->get('file_system'),
       $container->get('event_dispatcher'),
       $container->get('tempstore.shared'),
-      $container->get('datetime.time')
+      $container->get('datetime.time'),
+      $container->get(PathFactoryInterface::class)
     );
     return new static(
       $stage,
diff --git a/package_manager/tests/src/Kernel/ComposerExecutableValidatorTest.php b/package_manager/tests/src/Kernel/ComposerExecutableValidatorTest.php
index 7fd7b69989..a9d60ac959 100644
--- a/package_manager/tests/src/Kernel/ComposerExecutableValidatorTest.php
+++ b/package_manager/tests/src/Kernel/ComposerExecutableValidatorTest.php
@@ -7,8 +7,8 @@ use Drupal\Core\Url;
 use Drupal\package_manager\Event\PreCreateEvent;
 use Drupal\package_manager\Validator\ComposerExecutableValidator;
 use Drupal\package_manager\ValidationResult;
-use PhpTuf\ComposerStager\Domain\Process\Runner\ComposerRunnerInterface;
-use PhpTuf\ComposerStager\Exception\IOException;
+use PhpTuf\ComposerStager\Domain\Exception\IOException;
+use PhpTuf\ComposerStager\Domain\Service\ProcessRunner\ComposerRunnerInterface;
 use Prophecy\Argument;
 
 /**
@@ -21,7 +21,7 @@ class ComposerExecutableValidatorTest extends PackageManagerKernelTestBase {
   /**
    * The mocked Composer runner.
    *
-   * @var \Prophecy\Prophecy\ObjectProphecy|\PhpTuf\ComposerStager\Domain\Process\Runner\ComposerRunnerInterface
+   * @var \Prophecy\Prophecy\ObjectProphecy|\PhpTuf\ComposerStager\Domain\Service\ProcessRunner\ComposerRunnerInterface
    */
   private $composerRunner;
 
diff --git a/package_manager/tests/src/Kernel/ExecutableFinderTest.php b/package_manager/tests/src/Kernel/ExecutableFinderTest.php
index 1dc88e6647..ff7ed9933a 100644
--- a/package_manager/tests/src/Kernel/ExecutableFinderTest.php
+++ b/package_manager/tests/src/Kernel/ExecutableFinderTest.php
@@ -4,7 +4,7 @@ namespace Drupal\Tests\package_manager\Kernel;
 
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\package_manager\ExecutableFinder;
-use PhpTuf\ComposerStager\Infrastructure\Process\ExecutableFinderInterface;
+use PhpTuf\ComposerStager\Infrastructure\Service\Finder\ExecutableFinderInterface;
 use Symfony\Component\Process\ExecutableFinder as SymfonyExecutableFinder;
 
 /**
diff --git a/package_manager/tests/src/Kernel/FileSyncerFactoryTest.php b/package_manager/tests/src/Kernel/FileSyncerFactoryTest.php
index dacde715a1..6516815ce9 100644
--- a/package_manager/tests/src/Kernel/FileSyncerFactoryTest.php
+++ b/package_manager/tests/src/Kernel/FileSyncerFactoryTest.php
@@ -3,9 +3,9 @@
 namespace Drupal\Tests\package_manager\Kernel;
 
 use Drupal\KernelTests\KernelTestBase;
-use PhpTuf\ComposerStager\Domain\FileSyncer\FileSyncerInterface;
-use PhpTuf\ComposerStager\Infrastructure\FileSyncer\PhpFileSyncer;
-use PhpTuf\ComposerStager\Infrastructure\FileSyncer\RsyncFileSyncer;
+use PhpTuf\ComposerStager\Domain\Service\FileSyncer\FileSyncerInterface;
+use PhpTuf\ComposerStager\Infrastructure\Service\FileSyncer\PhpFileSyncer;
+use PhpTuf\ComposerStager\Infrastructure\Service\FileSyncer\RsyncFileSyncer;
 
 /**
  * @covers \Drupal\package_manager\FileSyncerFactory
diff --git a/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php b/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php
index 3a04735d0c..c42d0bf025 100644
--- a/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php
+++ b/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php
@@ -16,6 +16,10 @@ use org\bovigo\vfs\vfsStream;
 use org\bovigo\vfs\vfsStreamDirectory;
 use org\bovigo\vfs\vfsStreamFile;
 use org\bovigo\vfs\visitor\vfsStreamAbstractVisitor;
+use PhpTuf\ComposerStager\Domain\Value\Path\PathInterface;
+use PhpTuf\ComposerStager\Infrastructure\Factory\Path\PathFactoryInterface;
+use PhpTuf\ComposerStager\Infrastructure\Value\Path\AbstractPath;
+use Symfony\Component\DependencyInjection\Definition;
 
 /**
  * Base class for kernel tests of Package Manager's functionality.
@@ -73,6 +77,13 @@ abstract class PackageManagerKernelTestBase extends KernelTestBase {
   public function register(ContainerBuilder $container) {
     parent::register($container);
 
+    // Ensure that Composer Stager uses the test path factory, which is aware
+    // of the virtual file system.
+    $definition = new Definition(TestPathFactory::class);
+    $class = $definition->getClass();
+    $container->setDefinition($class, $definition->setPublic(FALSE));
+    $container->setAlias(PathFactoryInterface::class, $class);
+
     foreach ($this->disableValidators as $service_id) {
       if ($container->hasDefinition($service_id)) {
         $container->getDefinition($service_id)->clearTag('event_subscriber');
@@ -96,7 +107,8 @@ abstract class PackageManagerKernelTestBase extends KernelTestBase {
       $this->container->get('file_system'),
       $this->container->get('event_dispatcher'),
       $this->container->get('tempstore.shared'),
-      $this->container->get('datetime.time')
+      $this->container->get('datetime.time'),
+      new TestPathFactory()
     );
   }
 
@@ -284,6 +296,38 @@ trait TestStageTrait {
   }
 
 }
+
+/**
+ * Defines a path value object that is aware of the virtual file system.
+ */
+class TestPath extends AbstractPath {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doResolve(string $basePath): string {
+    if (str_starts_with($this->path, vfsStream::SCHEME . '://')) {
+      return $this->path;
+    }
+    return implode(DIRECTORY_SEPARATOR, [$basePath, $this->path]);
+  }
+
+}
+
+/**
+ * Defines a path factory that is aware of the virtual file system.
+ */
+class TestPathFactory implements PathFactoryInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(string $path): PathInterface {
+    return new TestPath($path);
+  }
+
+}
+
 /**
  * Defines a stage specifically for testing purposes.
  */
-- 
GitLab