1
0
Fork 0

Execute operations in batches to make sure plugins install in the expected order

pull/8952/head
Jordi Boggiano 2020-06-05 11:11:31 +02:00
parent 9f380d606c
commit 87a0fc5506
No known key found for this signature in database
GPG Key ID: 7BBD42C429EC80BC
2 changed files with 87 additions and 63 deletions

View File

@ -62,6 +62,7 @@ abstract class ArchiveDownloader extends FileDownloader
} while (is_dir($temporaryDir)); } while (is_dir($temporaryDir));
$this->addCleanupPath($package, $temporaryDir); $this->addCleanupPath($package, $temporaryDir);
$this->addCleanupPath($package, $path);
$this->filesystem->ensureDirectoryExists($temporaryDir); $this->filesystem->ensureDirectoryExists($temporaryDir);
$fileName = $this->getFileName($package, $path); $fileName = $this->getFileName($package, $path);
@ -77,6 +78,7 @@ abstract class ArchiveDownloader extends FileDownloader
$filesystem->removeDirectory($path); $filesystem->removeDirectory($path);
$filesystem->removeDirectory($temporaryDir); $filesystem->removeDirectory($temporaryDir);
$self->removeCleanupPath($package, $temporaryDir); $self->removeCleanupPath($package, $temporaryDir);
$self->removeCleanupPath($package, $path);
}; };
$promise = null; $promise = null;
@ -142,6 +144,7 @@ abstract class ArchiveDownloader extends FileDownloader
$filesystem->removeDirectory($temporaryDir); $filesystem->removeDirectory($temporaryDir);
$self->removeCleanupPath($package, $temporaryDir); $self->removeCleanupPath($package, $temporaryDir);
$self->removeCleanupPath($package, $path);
}, function ($e) use ($cleanup) { }, function ($e) use ($cleanup) {
$cleanup(); $cleanup();

View File

@ -272,73 +272,23 @@ class InstallationManager
$this->loop->wait($promises); $this->loop->wait($promises);
} }
foreach ($operations as $index => $operation) { // execute operations in batches to make sure every plugin is installed in the
$opType = $operation->getOperationType(); // right order and activated before the packages depending on it are installed
while ($operations) {
$batch = array();
// ignoring alias ops as they don't need to execute anything foreach ($operations as $index => $operation) {
if (!in_array($opType, array('update', 'install', 'uninstall'))) { unset($operations[$index]);
// output alias ops in debug verbosity as they have no output otherwise $batch[$index] = $operation;
if ($this->io->isDebug()) { if (in_array($operation->getOperationType(), array('update', 'install'), true)) {
$this->io->writeError(' - ' . $operation->show(false)); $package = $operation->getOperationType() === 'update' ? $operation->getTargetPackage() : $operation->getPackage();
if ($package->getType() === 'composer-plugin' || $package->getType() === 'composer-installer') {
break;
}
} }
$this->$opType($repo, $operation);
continue;
} }
if ($opType === 'update') { $this->executeBatch($repo, $batch, $cleanupPromises, $devMode, $runScripts);
$package = $operation->getTargetPackage();
$initialPackage = $operation->getInitialPackage();
} else {
$package = $operation->getPackage();
$initialPackage = null;
}
$installer = $this->getInstaller($package->getType());
$event = 'Composer\Installer\PackageEvents::PRE_PACKAGE_'.strtoupper($opType);
if (defined($event) && $runScripts && $this->eventDispatcher) {
$this->eventDispatcher->dispatchPackageEvent(constant($event), $devMode, $repo, $operations, $operation);
}
$dispatcher = $this->eventDispatcher;
$installManager = $this;
$loop = $this->loop;
$io = $this->io;
$promise = $installer->prepare($opType, $package, $initialPackage);
if (!$promise instanceof PromiseInterface) {
$promise = \React\Promise\resolve();
}
$promise = $promise->then(function () use ($opType, $installManager, $repo, $operation) {
return $installManager->$opType($repo, $operation);
})->then($cleanupPromises[$index])
->then(function () use ($opType, $runScripts, $dispatcher, $installManager, $devMode, $repo, $operations, $operation) {
$repo->write($devMode, $installManager);
$event = 'Composer\Installer\PackageEvents::POST_PACKAGE_'.strtoupper($opType);
if (defined($event) && $runScripts && $dispatcher) {
$dispatcher->dispatchPackageEvent(constant($event), $devMode, $repo, $operations, $operation);
}
}, function ($e) use ($opType, $package, $io) {
$io->writeError(' <error>' . ucfirst($opType) .' of '.$package->getPrettyName().' failed</error>');
throw $e;
});
$promises[] = $promise;
}
// execute all prepare => installs/updates/removes => cleanup steps
if (!empty($promises)) {
$progress = null;
if ($io instanceof ConsoleIO && !$io->isDebug()) {
$progress = $io->getProgressBar();
}
$this->loop->wait($promises, $progress);
if ($progress) {
$progress->clear();
}
} }
} catch (\Exception $e) { } catch (\Exception $e) {
$runCleanup(); $runCleanup();
@ -366,6 +316,77 @@ class InstallationManager
$repo->write($devMode, $this); $repo->write($devMode, $this);
} }
private function executeBatch(RepositoryInterface $repo, array $operations, array $cleanupPromises, $devMode, $runScripts)
{
foreach ($operations as $index => $operation) {
$opType = $operation->getOperationType();
// ignoring alias ops as they don't need to execute anything
if (!in_array($opType, array('update', 'install', 'uninstall'))) {
// output alias ops in debug verbosity as they have no output otherwise
if ($this->io->isDebug()) {
$this->io->writeError(' - ' . $operation->show(false));
}
$this->$opType($repo, $operation);
continue;
}
if ($opType === 'update') {
$package = $operation->getTargetPackage();
$initialPackage = $operation->getInitialPackage();
} else {
$package = $operation->getPackage();
$initialPackage = null;
}
$installer = $this->getInstaller($package->getType());
$event = 'Composer\Installer\PackageEvents::PRE_PACKAGE_'.strtoupper($opType);
if (defined($event) && $runScripts && $this->eventDispatcher) {
$this->eventDispatcher->dispatchPackageEvent(constant($event), $devMode, $repo, $operations, $operation);
}
$dispatcher = $this->eventDispatcher;
$installManager = $this;
$io = $this->io;
$promise = $installer->prepare($opType, $package, $initialPackage);
if (!$promise instanceof PromiseInterface) {
$promise = \React\Promise\resolve();
}
$promise = $promise->then(function () use ($opType, $installManager, $repo, $operation) {
return $installManager->$opType($repo, $operation);
})->then($cleanupPromises[$index])
->then(function () use ($opType, $runScripts, $dispatcher, $installManager, $devMode, $repo, $operations, $operation) {
$repo->write($devMode, $installManager);
$event = 'Composer\Installer\PackageEvents::POST_PACKAGE_'.strtoupper($opType);
if (defined($event) && $runScripts && $dispatcher) {
$dispatcher->dispatchPackageEvent(constant($event), $devMode, $repo, $operations, $operation);
}
}, function ($e) use ($opType, $package, $io) {
$io->writeError(' <error>' . ucfirst($opType) .' of '.$package->getPrettyName().' failed</error>');
throw $e;
});
$promises[] = $promise;
}
// execute all prepare => installs/updates/removes => cleanup steps
if (!empty($promises)) {
$progress = null;
if ($io instanceof ConsoleIO && !$io->isDebug() && count($promises) > 1) {
$progress = $io->getProgressBar();
}
$this->loop->wait($promises, $progress);
if ($progress) {
$progress->clear();
}
}
}
/** /**
* Executes install operation. * Executes install operation.
* *