Make installer classes forward promises from downloaders to InstallationManager
parent
816d8e9d1b
commit
5761228068
|
@ -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) {
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue