1
0
Fork 0

Make installer classes forward promises from downloaders to InstallationManager

pull/8949/head
Jordi Boggiano 2020-06-04 10:20:03 +02:00
parent 816d8e9d1b
commit 5761228068
No known key found for this signature in database
GPG Key ID: 7BBD42C429EC80BC
4 changed files with 106 additions and 48 deletions

View File

@ -26,6 +26,7 @@ use Composer\DependencyResolver\Operation\MarkAliasUninstalledOperation;
use Composer\EventDispatcher\EventDispatcher; use Composer\EventDispatcher\EventDispatcher;
use Composer\Util\StreamContextFactory; use Composer\Util\StreamContextFactory;
use Composer\Util\Loop; use Composer\Util\Loop;
use React\Promise\PromiseInterface;
/** /**
* Package operation manager. * Package operation manager.
@ -296,8 +297,8 @@ class InstallationManager
$io = $this->io; $io = $this->io;
$promise = $installer->prepare($opType, $package, $initialPackage); $promise = $installer->prepare($opType, $package, $initialPackage);
if (null === $promise) { if (!$promise instanceof PromiseInterface) {
$promise = new \React\Promise\Promise(function ($resolve, $reject) { $resolve(); }); $promise = \React\Promise\resolve();
} }
$promise = $promise->then(function () use ($opType, $installManager, $repo, $operation) { $promise = $promise->then(function () use ($opType, $installManager, $repo, $operation) {

View File

@ -19,6 +19,7 @@ use Composer\Package\PackageInterface;
use Composer\Util\Filesystem; use Composer\Util\Filesystem;
use Composer\Util\Silencer; use Composer\Util\Silencer;
use Composer\Util\Platform; use Composer\Util\Platform;
use React\Promise\PromiseInterface;
/** /**
* Package installation manager. * Package installation manager.
@ -131,11 +132,19 @@ class LibraryInstaller implements InstallerInterface, BinaryPresenceInterface
$this->binaryInstaller->removeBinaries($package); $this->binaryInstaller->removeBinaries($package);
} }
$this->installCode($package); $promise = $this->installCode($package);
$this->binaryInstaller->installBinaries($package, $this->getInstallPath($package)); if (!$promise instanceof PromiseInterface) {
$promise = \React\Promise\resolve();
}
$binaryInstaller = $this->binaryInstaller;
$installPath = $this->getInstallPath($package);
return $promise->then(function () use ($binaryInstaller, $installPath, $package, $repo) {
$binaryInstaller->installBinaries($package, $installPath);
if (!$repo->hasPackage($package)) { if (!$repo->hasPackage($package)) {
$repo->addPackage(clone $package); $repo->addPackage(clone $package);
} }
});
} }
/** /**
@ -150,12 +159,20 @@ class LibraryInstaller implements InstallerInterface, BinaryPresenceInterface
$this->initializeVendorDir(); $this->initializeVendorDir();
$this->binaryInstaller->removeBinaries($initial); $this->binaryInstaller->removeBinaries($initial);
$this->updateCode($initial, $target); $promise = $this->updateCode($initial, $target);
$this->binaryInstaller->installBinaries($target, $this->getInstallPath($target)); if (!$promise instanceof PromiseInterface) {
$promise = \React\Promise\resolve();
}
$binaryInstaller = $this->binaryInstaller;
$installPath = $this->getInstallPath($target);
return $promise->then(function () use ($binaryInstaller, $installPath, $target, $initial, $repo) {
$binaryInstaller->installBinaries($target, $installPath);
$repo->removePackage($initial); $repo->removePackage($initial);
if (!$repo->hasPackage($target)) { if (!$repo->hasPackage($target)) {
$repo->addPackage(clone $target); $repo->addPackage(clone $target);
} }
});
} }
/** /**
@ -167,17 +184,25 @@ class LibraryInstaller implements InstallerInterface, BinaryPresenceInterface
throw new \InvalidArgumentException('Package is not installed: '.$package); throw new \InvalidArgumentException('Package is not installed: '.$package);
} }
$this->removeCode($package); $promise = $this->removeCode($package);
$this->binaryInstaller->removeBinaries($package); if (!$promise instanceof PromiseInterface) {
$promise = \React\Promise\resolve();
}
$binaryInstaller = $this->binaryInstaller;
$downloadPath = $this->getPackageBasePath($package);
$filesystem = $this->filesystem;
return $promise->then(function () use ($binaryInstaller, $filesystem, $downloadPath, $package, $repo) {
$binaryInstaller->removeBinaries($package);
$repo->removePackage($package); $repo->removePackage($package);
$downloadPath = $this->getPackageBasePath($package);
if (strpos($package->getName(), '/')) { if (strpos($package->getName(), '/')) {
$packageVendorDir = dirname($downloadPath); $packageVendorDir = dirname($downloadPath);
if (is_dir($packageVendorDir) && $this->filesystem->isDirEmpty($packageVendorDir)) { if (is_dir($packageVendorDir) && $filesystem->isDirEmpty($packageVendorDir)) {
Silencer::call('rmdir', $packageVendorDir); Silencer::call('rmdir', $packageVendorDir);
} }
} }
});
} }
/** /**
@ -227,7 +252,7 @@ class LibraryInstaller implements InstallerInterface, BinaryPresenceInterface
protected function installCode(PackageInterface $package) protected function installCode(PackageInterface $package)
{ {
$downloadPath = $this->getInstallPath($package); $downloadPath = $this->getInstallPath($package);
$this->downloadManager->install($package, $downloadPath); return $this->downloadManager->install($package, $downloadPath);
} }
protected function updateCode(PackageInterface $initial, PackageInterface $target) protected function updateCode(PackageInterface $initial, PackageInterface $target)
@ -240,21 +265,31 @@ class LibraryInstaller implements InstallerInterface, BinaryPresenceInterface
if (substr($initialDownloadPath, 0, strlen($targetDownloadPath)) === $targetDownloadPath if (substr($initialDownloadPath, 0, strlen($targetDownloadPath)) === $targetDownloadPath
|| substr($targetDownloadPath, 0, strlen($initialDownloadPath)) === $initialDownloadPath || substr($targetDownloadPath, 0, strlen($initialDownloadPath)) === $initialDownloadPath
) { ) {
$this->removeCode($initial); $promise = $this->removeCode($initial);
$this->installCode($target); if (!$promise instanceof PromiseInterface) {
$promise = \React\Promise\resolve();
}
return; $self = $this;
return $promise->then(function () use ($self, $target) {
$reflMethod = new \ReflectionMethod($self, 'installCode');
$reflMethod->setAccessible(true);
// equivalent of $this->installCode($target) with php 5.3 support
// TODO remove this once 5.3 support is dropped
return $reflMethod->invoke($self, $target);
});
} }
$this->filesystem->rename($initialDownloadPath, $targetDownloadPath); $this->filesystem->rename($initialDownloadPath, $targetDownloadPath);
} }
$this->downloadManager->update($initial, $target, $targetDownloadPath); return $this->downloadManager->update($initial, $target, $targetDownloadPath);
} }
protected function removeCode(PackageInterface $package) protected function removeCode(PackageInterface $package)
{ {
$downloadPath = $this->getPackageBasePath($package); $downloadPath = $this->getPackageBasePath($package);
$this->downloadManager->remove($package, $downloadPath); return $this->downloadManager->remove($package, $downloadPath);
} }
protected function initializeVendorDir() protected function initializeVendorDir()

View File

@ -16,6 +16,7 @@ use Composer\Composer;
use Composer\IO\IOInterface; use Composer\IO\IOInterface;
use Composer\Repository\InstalledRepositoryInterface; use Composer\Repository\InstalledRepositoryInterface;
use Composer\Package\PackageInterface; use Composer\Package\PackageInterface;
use React\Promise\PromiseInterface;
/** /**
* Installer for plugin packages * Installer for plugin packages
@ -65,15 +66,20 @@ class PluginInstaller extends LibraryInstaller
*/ */
public function install(InstalledRepositoryInterface $repo, PackageInterface $package) public function install(InstalledRepositoryInterface $repo, PackageInterface $package)
{ {
parent::install($repo, $package); $promise = parent::install($repo, $package);
try { if (!$promise instanceof PromiseInterface) {
$this->composer->getPluginManager()->registerPackage($package, true); $promise = \React\Promise\resolve();
} catch (\Exception $e) {
// Rollback installation
$this->io->writeError('Plugin initialization failed ('.$e->getMessage().'), uninstalling plugin');
parent::uninstall($repo, $package);
throw $e;
} }
$pluginManager = $this->composer->getPluginManager();
$self = $this;
return $promise->then(function () use ($self, $pluginManager, $package, $repo) {
try {
$pluginManager->registerPackage($package, true);
} catch (\Exception $e) {
$self->rollbackInstall($e, $repo, $package);
}
});
} }
/** /**
@ -81,22 +87,38 @@ class PluginInstaller extends LibraryInstaller
*/ */
public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target) public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target)
{ {
parent::update($repo, $initial, $target); $promise = parent::update($repo, $initial, $target);
if (!$promise instanceof PromiseInterface) {
try { $promise = \React\Promise\resolve();
$this->composer->getPluginManager()->deactivatePackage($initial, true);
$this->composer->getPluginManager()->registerPackage($target, true);
} catch (\Exception $e) {
// Rollback installation
$this->io->writeError('Plugin initialization failed, uninstalling plugin');
parent::uninstall($repo, $target);
throw $e;
} }
$pluginManager = $this->composer->getPluginManager();
$self = $this;
return $promise->then(function () use ($self, $pluginManager, $initial, $target, $repo) {
try {
$pluginManager->deactivatePackage($initial, true);
$pluginManager->registerPackage($target, true);
} catch (\Exception $e) {
$self->rollbackInstall($e, $repo, $target);
}
});
} }
public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package) public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package)
{ {
$this->composer->getPluginManager()->uninstallPackage($package, true); $this->composer->getPluginManager()->uninstallPackage($package, true);
return parent::uninstall($repo, $package);
}
/**
* TODO v3 should make this private once we can drop PHP 5.3 support
* @private
*/
public function rollbackInstall(\Exception $e, InstalledRepositoryInterface $repo, PackageInterface $package)
{
$this->io->writeError('Plugin initialization failed ('.$e->getMessage().'), uninstalling plugin');
parent::uninstall($repo, $package); parent::uninstall($repo, $package);
throw $e;
} }
} }

View File

@ -76,7 +76,7 @@ class ProjectInstaller implements InstallerInterface
*/ */
public function prepare($type, PackageInterface $package, PackageInterface $prevPackage = null) public function prepare($type, PackageInterface $package, PackageInterface $prevPackage = null)
{ {
$this->downloadManager->prepare($type, $package, $this->installPath, $prevPackage); return $this->downloadManager->prepare($type, $package, $this->installPath, $prevPackage);
} }
/** /**
@ -84,7 +84,7 @@ class ProjectInstaller implements InstallerInterface
*/ */
public function cleanup($type, PackageInterface $package, PackageInterface $prevPackage = null) public function cleanup($type, PackageInterface $package, PackageInterface $prevPackage = null)
{ {
$this->downloadManager->cleanup($type, $package, $this->installPath, $prevPackage); return $this->downloadManager->cleanup($type, $package, $this->installPath, $prevPackage);
} }
/** /**
@ -92,7 +92,7 @@ class ProjectInstaller implements InstallerInterface
*/ */
public function install(InstalledRepositoryInterface $repo, PackageInterface $package) public function install(InstalledRepositoryInterface $repo, PackageInterface $package)
{ {
$this->downloadManager->install($package, $this->installPath); return $this->downloadManager->install($package, $this->installPath);
} }
/** /**