Make sure operations are executed in batches, including downloads, when a plugin is present which modifies downloads, fixes #9333
parent
e090274db0
commit
dc88236c07
|
@ -180,7 +180,6 @@ class InstallationManager
|
||||||
*/
|
*/
|
||||||
public function execute(RepositoryInterface $repo, array $operations, $devMode = true, $runScripts = true)
|
public function execute(RepositoryInterface $repo, array $operations, $devMode = true, $runScripts = true)
|
||||||
{
|
{
|
||||||
$promises = array();
|
|
||||||
$cleanupPromises = array();
|
$cleanupPromises = array();
|
||||||
|
|
||||||
$loop = $this->loop;
|
$loop = $this->loop;
|
||||||
|
@ -234,61 +233,14 @@ class InstallationManager
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
foreach ($operations as $index => $operation) {
|
// execute operations in batches to make sure download-modifying-plugins are installed
|
||||||
$opType = $operation->getOperationType();
|
// before the other packages get downloaded
|
||||||
|
|
||||||
// ignoring alias ops as they don't need to execute anything at this stage
|
|
||||||
if (!in_array($opType, array('update', 'install', 'uninstall'))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($opType === 'update') {
|
|
||||||
$package = $operation->getTargetPackage();
|
|
||||||
$initialPackage = $operation->getInitialPackage();
|
|
||||||
} else {
|
|
||||||
$package = $operation->getPackage();
|
|
||||||
$initialPackage = null;
|
|
||||||
}
|
|
||||||
$installer = $this->getInstaller($package->getType());
|
|
||||||
|
|
||||||
$cleanupPromises[$index] = function () use ($opType, $installer, $package, $initialPackage) {
|
|
||||||
// avoid calling cleanup if the download was not even initialized for a package
|
|
||||||
// as without installation source configured nothing will work
|
|
||||||
if (!$package->getInstallationSource()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $installer->cleanup($opType, $package, $initialPackage);
|
|
||||||
};
|
|
||||||
|
|
||||||
if ($opType !== 'uninstall') {
|
|
||||||
$promise = $installer->download($package, $initialPackage);
|
|
||||||
if ($promise) {
|
|
||||||
$promises[] = $promise;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// execute all downloads first
|
|
||||||
if (!empty($promises)) {
|
|
||||||
$progress = null;
|
|
||||||
if ($this->outputProgress && $this->io instanceof ConsoleIO && !$this->io->isDebug() && count($promises) > 1) {
|
|
||||||
$progress = $this->io->getProgressBar();
|
|
||||||
}
|
|
||||||
$this->loop->wait($promises, $progress);
|
|
||||||
if ($progress) {
|
|
||||||
$progress->clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// execute operations in batches to make sure every plugin is installed in the
|
|
||||||
// right order and activated before the packages depending on it are installed
|
|
||||||
$batches = array();
|
$batches = array();
|
||||||
$batch = array();
|
$batch = array();
|
||||||
foreach ($operations as $index => $operation) {
|
foreach ($operations as $index => $operation) {
|
||||||
if (in_array($operation->getOperationType(), array('update', 'install'), true)) {
|
if (in_array($operation->getOperationType(), array('update', 'install'), true)) {
|
||||||
$package = $operation->getOperationType() === 'update' ? $operation->getTargetPackage() : $operation->getPackage();
|
$package = $operation->getOperationType() === 'update' ? $operation->getTargetPackage() : $operation->getPackage();
|
||||||
if ($package->getType() === 'composer-plugin' || $package->getType() === 'composer-installer') {
|
if ($package->getType() === 'composer-plugin' && $extra = $package->getExtra() && isset($extra['plugin-modifies-downloads']) && $extra['plugin-modifies-downloads'] === true) {
|
||||||
if ($batch) {
|
if ($batch) {
|
||||||
$batches[] = $batch;
|
$batches[] = $batch;
|
||||||
}
|
}
|
||||||
|
@ -306,7 +258,7 @@ class InstallationManager
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($batches as $batch) {
|
foreach ($batches as $batch) {
|
||||||
$this->executeBatch($repo, $batch, $cleanupPromises, $devMode, $runScripts, $operations);
|
$this->downloadAndExecuteBatch($repo, $batch, $cleanupPromises, $devMode, $runScripts, $operations);
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$runCleanup();
|
$runCleanup();
|
||||||
|
@ -334,12 +286,91 @@ class InstallationManager
|
||||||
$repo->write($devMode, $this);
|
$repo->write($devMode, $this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $operations List of operations to execute in this batch
|
||||||
|
* @param array $allOperations Complete list of operations to be executed in the install job, used for event listeners
|
||||||
|
*/
|
||||||
|
private function downloadAndExecuteBatch(RepositoryInterface $repo, array $operations, array &$cleanupPromises, $devMode, $runScripts, array $allOperations)
|
||||||
|
{
|
||||||
|
$promises = array();
|
||||||
|
|
||||||
|
foreach ($operations as $index => $operation) {
|
||||||
|
$opType = $operation->getOperationType();
|
||||||
|
|
||||||
|
// ignoring alias ops as they don't need to execute anything at this stage
|
||||||
|
if (!in_array($opType, array('update', 'install', 'uninstall'))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($opType === 'update') {
|
||||||
|
$package = $operation->getTargetPackage();
|
||||||
|
$initialPackage = $operation->getInitialPackage();
|
||||||
|
} else {
|
||||||
|
$package = $operation->getPackage();
|
||||||
|
$initialPackage = null;
|
||||||
|
}
|
||||||
|
$installer = $this->getInstaller($package->getType());
|
||||||
|
|
||||||
|
$cleanupPromises[$index] = function () use ($opType, $installer, $package, $initialPackage) {
|
||||||
|
// avoid calling cleanup if the download was not even initialized for a package
|
||||||
|
// as without installation source configured nothing will work
|
||||||
|
if (!$package->getInstallationSource()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $installer->cleanup($opType, $package, $initialPackage);
|
||||||
|
};
|
||||||
|
|
||||||
|
if ($opType !== 'uninstall') {
|
||||||
|
$promise = $installer->download($package, $initialPackage);
|
||||||
|
if ($promise) {
|
||||||
|
$promises[] = $promise;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// execute all downloads first
|
||||||
|
if (count($promises)) {
|
||||||
|
$this->waitOnPromises($promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
// execute operations in batches to make sure every plugin is installed in the
|
||||||
|
// right order and activated before the packages depending on it are installed
|
||||||
|
$batches = array();
|
||||||
|
$batch = array();
|
||||||
|
foreach ($operations as $index => $operation) {
|
||||||
|
if (in_array($operation->getOperationType(), array('update', 'install'), true)) {
|
||||||
|
$package = $operation->getOperationType() === 'update' ? $operation->getTargetPackage() : $operation->getPackage();
|
||||||
|
if ($package->getType() === 'composer-plugin' || $package->getType() === 'composer-installer') {
|
||||||
|
if ($batch) {
|
||||||
|
$batches[] = $batch;
|
||||||
|
}
|
||||||
|
$batches[] = array($index => $operation);
|
||||||
|
$batch = array();
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$batch[$index] = $operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($batch) {
|
||||||
|
$batches[] = $batch;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($batches as $batch) {
|
||||||
|
$this->executeBatch($repo, $batch, $cleanupPromises, $devMode, $runScripts, $allOperations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $operations List of operations to execute in this batch
|
* @param array $operations List of operations to execute in this batch
|
||||||
* @param array $allOperations Complete list of operations to be executed in the install job, used for event listeners
|
* @param array $allOperations Complete list of operations to be executed in the install job, used for event listeners
|
||||||
*/
|
*/
|
||||||
private function executeBatch(RepositoryInterface $repo, array $operations, array $cleanupPromises, $devMode, $runScripts, array $allOperations)
|
private function executeBatch(RepositoryInterface $repo, array $operations, array $cleanupPromises, $devMode, $runScripts, array $allOperations)
|
||||||
{
|
{
|
||||||
|
$promises = array();
|
||||||
|
|
||||||
foreach ($operations as $index => $operation) {
|
foreach ($operations as $index => $operation) {
|
||||||
$opType = $operation->getOperationType();
|
$opType = $operation->getOperationType();
|
||||||
|
|
||||||
|
@ -397,15 +428,20 @@ class InstallationManager
|
||||||
}
|
}
|
||||||
|
|
||||||
// execute all prepare => installs/updates/removes => cleanup steps
|
// execute all prepare => installs/updates/removes => cleanup steps
|
||||||
if (!empty($promises)) {
|
if (count($promises)) {
|
||||||
$progress = null;
|
$this->waitOnPromises($promises);
|
||||||
if ($this->outputProgress && $this->io instanceof ConsoleIO && !$this->io->isDebug() && count($promises) > 1) {
|
}
|
||||||
$progress = $this->io->getProgressBar();
|
}
|
||||||
}
|
|
||||||
$this->loop->wait($promises, $progress);
|
private function waitOnPromises(array $promises)
|
||||||
if ($progress) {
|
{
|
||||||
$progress->clear();
|
$progress = null;
|
||||||
}
|
if ($this->outputProgress && $this->io instanceof ConsoleIO && !$this->io->isDebug() && count($promises) > 1) {
|
||||||
|
$progress = $this->io->getProgressBar();
|
||||||
|
}
|
||||||
|
$this->loop->wait($promises, $progress);
|
||||||
|
if ($progress) {
|
||||||
|
$progress->clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue