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\Util\StreamContextFactory;
use Composer\Util\Loop;
use React\Promise\PromiseInterface;
/**
* Package operation manager.
@ -296,8 +297,8 @@ class InstallationManager
$io = $this->io;
$promise = $installer->prepare($opType, $package, $initialPackage);
if (null === $promise) {
$promise = new \React\Promise\Promise(function ($resolve, $reject) { $resolve(); });
if (!$promise instanceof PromiseInterface) {
$promise = \React\Promise\resolve();
}
$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\Silencer;
use Composer\Util\Platform;
use React\Promise\PromiseInterface;
/**
* Package installation manager.
@ -131,11 +132,19 @@ class LibraryInstaller implements InstallerInterface, BinaryPresenceInterface
$this->binaryInstaller->removeBinaries($package);
}
$this->installCode($package);
$this->binaryInstaller->installBinaries($package, $this->getInstallPath($package));
$promise = $this->installCode($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)) {
$repo->addPackage(clone $package);
}
});
}
/**
@ -150,12 +159,20 @@ class LibraryInstaller implements InstallerInterface, BinaryPresenceInterface
$this->initializeVendorDir();
$this->binaryInstaller->removeBinaries($initial);
$this->updateCode($initial, $target);
$this->binaryInstaller->installBinaries($target, $this->getInstallPath($target));
$promise = $this->updateCode($initial, $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);
if (!$repo->hasPackage($target)) {
$repo->addPackage(clone $target);
}
});
}
/**
@ -167,17 +184,25 @@ class LibraryInstaller implements InstallerInterface, BinaryPresenceInterface
throw new \InvalidArgumentException('Package is not installed: '.$package);
}
$this->removeCode($package);
$this->binaryInstaller->removeBinaries($package);
$promise = $this->removeCode($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);
$downloadPath = $this->getPackageBasePath($package);
if (strpos($package->getName(), '/')) {
$packageVendorDir = dirname($downloadPath);
if (is_dir($packageVendorDir) && $this->filesystem->isDirEmpty($packageVendorDir)) {
if (is_dir($packageVendorDir) && $filesystem->isDirEmpty($packageVendorDir)) {
Silencer::call('rmdir', $packageVendorDir);
}
}
});
}
/**
@ -227,7 +252,7 @@ class LibraryInstaller implements InstallerInterface, BinaryPresenceInterface
protected function installCode(PackageInterface $package)
{
$downloadPath = $this->getInstallPath($package);
$this->downloadManager->install($package, $downloadPath);
return $this->downloadManager->install($package, $downloadPath);
}
protected function updateCode(PackageInterface $initial, PackageInterface $target)
@ -240,21 +265,31 @@ class LibraryInstaller implements InstallerInterface, BinaryPresenceInterface
if (substr($initialDownloadPath, 0, strlen($targetDownloadPath)) === $targetDownloadPath
|| substr($targetDownloadPath, 0, strlen($initialDownloadPath)) === $initialDownloadPath
) {
$this->removeCode($initial);
$this->installCode($target);
$promise = $this->removeCode($initial);
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->downloadManager->update($initial, $target, $targetDownloadPath);
return $this->downloadManager->update($initial, $target, $targetDownloadPath);
}
protected function removeCode(PackageInterface $package)
{
$downloadPath = $this->getPackageBasePath($package);
$this->downloadManager->remove($package, $downloadPath);
return $this->downloadManager->remove($package, $downloadPath);
}
protected function initializeVendorDir()

View File

@ -16,6 +16,7 @@ use Composer\Composer;
use Composer\IO\IOInterface;
use Composer\Repository\InstalledRepositoryInterface;
use Composer\Package\PackageInterface;
use React\Promise\PromiseInterface;
/**
* Installer for plugin packages
@ -65,15 +66,20 @@ class PluginInstaller extends LibraryInstaller
*/
public function install(InstalledRepositoryInterface $repo, PackageInterface $package)
{
parent::install($repo, $package);
try {
$this->composer->getPluginManager()->registerPackage($package, true);
} catch (\Exception $e) {
// Rollback installation
$this->io->writeError('Plugin initialization failed ('.$e->getMessage().'), uninstalling plugin');
parent::uninstall($repo, $package);
throw $e;
$promise = parent::install($repo, $package);
if (!$promise instanceof PromiseInterface) {
$promise = \React\Promise\resolve();
}
$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)
{
parent::update($repo, $initial, $target);
try {
$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;
$promise = parent::update($repo, $initial, $target);
if (!$promise instanceof PromiseInterface) {
$promise = \React\Promise\resolve();
}
$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)
{
$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);
throw $e;
}
}

View File

@ -76,7 +76,7 @@ class ProjectInstaller implements InstallerInterface
*/
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)
{
$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)
{
$this->downloadManager->install($package, $this->installPath);
return $this->downloadManager->install($package, $this->installPath);
}
/**