Skip to content

Implement merge conflict handling and restore module directory#6

Merged
roble merged 4 commits into
mainfrom
dev
Mar 18, 2026
Merged

Implement merge conflict handling and restore module directory#6
roble merged 4 commits into
mainfrom
dev

Conversation

@roble

@roble roble commented Mar 18, 2026

Copy link
Copy Markdown
Collaborator

This pull request introduces improvements to the module installer’s update and restore logic, as well as compatibility enhancements and new test coverage. The most significant changes are the addition of a robust restoreStash method, updates to dependency requirements, and new tests to verify correct stash restoration behavior.

Installer logic improvements:

  • Added a new protected method restoreStash to Installer.php that restores a stashed module directory to its original install path, or discards the stash if the original path already exists. This ensures correct handling of module updates and partial restores.
  • Integrated restoreStash into the error handling flow of the installer, ensuring stashes are properly restored or discarded during rollback scenarios.
  • Updated file renaming in stash merging to use SymfonyFilesystem->rename, improving reliability and atomicity.

Compatibility updates:

  • Modified composer.json to allow symfony/finder and symfony/process versions ^7.0 or ^8.0, expanding compatibility with older Symfony versions. Also added a version field to the package metadata. [1] [2]

Testing enhancements:

  • Added tests for restoreStash in ModuleInstallerTest.php, covering both restoration when the original path is missing and stash discard when the original path already exists. This ensures the new logic is thoroughly validated. [1] [2]

Copilot AI review requested due to automatic review settings March 18, 2026 20:41
@roble roble merged commit a62f190 into main Mar 18, 2026
2 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR strengthens the module installer’s update rollback behavior by restoring (or discarding) stashed module directories during failure scenarios, expands Symfony component version compatibility, and adds tests for the new restore behavior.

Changes:

  • Added Installer::restoreStash() and integrated it into the update rollback path.
  • Switched merged-file renames to SymfonyFilesystem->rename(...) for improved reliability/atomicity behavior.
  • Expanded symfony/finder and symfony/process constraints and added PHPUnit coverage for stash restoration/discard behavior.

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 1 comment.

File Description
src/Installer.php Adds restoreStash(), uses Symfony filesystem rename for merge output, and updates rollback logic to restore/discard stashes.
tests/ModuleInstallerTest.php Adds a test shim method and new tests covering stash restoration vs. discard behavior.
composer.json Broadens Symfony dependency constraints and adds explicit package version metadata.
Comments suppressed due to low confidence (1)

src/Installer.php:330

  • The rollback logic is attached as the second argument to then(...), which only handles rejections from parent::update(...). If the fulfillment handler throws (e.g., mergeStash() or the subsequent directory removals throw), this onRejected callback will not run per Promises/A+ semantics, so temp dirs may leak and the stash may not be restored/cleaned up. Consider wrapping the fulfillment handler body in try/catch that performs the same cleanup/restore, or refactor to a chain that ensures cleanup in a finalizer (e.g., ->then(...)->otherwise(...)->always(...)).
        return $prepareBase->then(fn () => parent::update($repo, $initial, $target))->then(
            function () use ($target, $stashPath, $basePath) {
                $this->removeExcludedDirectories($target);
                if ($stashPath !== null) {
                    $installPath = $this->getInstallPath($target);
                    $this->mergeStash($stashPath, $basePath, $installPath);
                    (new Filesystem)->removeDirectory($stashPath);
                    (new Filesystem)->removeDirectory($basePath);
                }
            },
            function (\Throwable $e) use ($stashPath, $basePath, $initial) {
                if ($stashPath !== null) {
                    $this->restoreStash($stashPath, $initial);
                }
                if ($basePath !== null) {
                    (new Filesystem)->removeDirectory($basePath);
                }
                throw $e;

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +397 to +412
$root = new RootPackage('root/app', '1.0.0.0', '1.0.0');
$root->setExtra(['module-dir' => sys_get_temp_dir()]);
$composer->method('getPackage')->willReturn($root);

$installer = new TestableInstaller($io, $composer);

$stash = sys_get_temp_dir().'/module-stash-restore-test-'.uniqid('', true);
mkdir($stash);
file_put_contents($stash.'/custom.php', '<?php // user file');

$pkg = new Package('saucebase/test-module', '1.0.0.0', '1.0.0');

// The original install path does not exist (was cleared during update)
$originalPath = $installer->getInstallPath($pkg);
$this->assertDirectoryDoesNotExist($originalPath);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants