Merge pull request #9339 from Seldaek/fix-plugin-order
Fix plugin install order for plugins modifying downloadspull/9348/head
commit
5bdb0cfff5
|
@ -248,6 +248,9 @@ class Transaction
|
||||||
*/
|
*/
|
||||||
private function movePluginsToFront(array $operations)
|
private function movePluginsToFront(array $operations)
|
||||||
{
|
{
|
||||||
|
$dlModifyingPluginsNoDeps = array();
|
||||||
|
$dlModifyingPluginsWithDeps = array();
|
||||||
|
$dlModifyingPluginRequires = array();
|
||||||
$pluginsNoDeps = array();
|
$pluginsNoDeps = array();
|
||||||
$pluginsWithDeps = array();
|
$pluginsWithDeps = array();
|
||||||
$pluginRequires = array();
|
$pluginRequires = array();
|
||||||
|
@ -261,6 +264,30 @@ class Transaction
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$isDownloadsModifyingPlugin = $package->getType() === 'composer-plugin' && ($extra = $package->getExtra()) && isset($extra['plugin-modifies-downloads']) && $extra['plugin-modifies-downloads'] === true;
|
||||||
|
|
||||||
|
// is this a downloads modifying plugin or a dependency of one?
|
||||||
|
if ($isDownloadsModifyingPlugin || count(array_intersect($package->getNames(), $dlModifyingPluginRequires))) {
|
||||||
|
// get the package's requires, but filter out any platform requirements
|
||||||
|
$requires = array_filter(array_keys($package->getRequires()), function ($req) {
|
||||||
|
return !PlatformRepository::isPlatformPackage($req);
|
||||||
|
});
|
||||||
|
|
||||||
|
// is this a plugin with no meaningful dependencies?
|
||||||
|
if ($isDownloadsModifyingPlugin && !count($requires)) {
|
||||||
|
// plugins with no dependencies go to the very front
|
||||||
|
array_unshift($dlModifyingPluginsNoDeps, $op);
|
||||||
|
} else {
|
||||||
|
// capture the requirements for this package so those packages will be moved up as well
|
||||||
|
$dlModifyingPluginRequires = array_merge($dlModifyingPluginRequires, $requires);
|
||||||
|
// move the operation to the front
|
||||||
|
array_unshift($dlModifyingPluginsWithDeps, $op);
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($operations[$idx]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// is this package a plugin?
|
// is this package a plugin?
|
||||||
$isPlugin = $package->getType() === 'composer-plugin' || $package->getType() === 'composer-installer';
|
$isPlugin = $package->getType() === 'composer-plugin' || $package->getType() === 'composer-installer';
|
||||||
|
|
||||||
|
@ -286,7 +313,7 @@ class Transaction
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return array_merge($pluginsNoDeps, $pluginsWithDeps, $operations);
|
return array_merge($dlModifyingPluginsNoDeps, $dlModifyingPluginsWithDeps, $pluginsNoDeps, $pluginsWithDeps, $operations);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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,6 +233,67 @@ class InstallationManager
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// execute operations in batches to make sure download-modifying-plugins are installed
|
||||||
|
// before the other packages get downloaded
|
||||||
|
$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' && ($extra = $package->getExtra()) && isset($extra['plugin-modifies-downloads']) && $extra['plugin-modifies-downloads'] === true) {
|
||||||
|
if ($batch) {
|
||||||
|
$batches[] = $batch;
|
||||||
|
}
|
||||||
|
$batches[] = array($index => $operation);
|
||||||
|
$batch = array();
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$batch[$index] = $operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($batch) {
|
||||||
|
$batches[] = $batch;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($batches as $batch) {
|
||||||
|
$this->downloadAndExecuteBatch($repo, $batch, $cleanupPromises, $devMode, $runScripts, $operations);
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$runCleanup();
|
||||||
|
|
||||||
|
if ($handleInterruptsUnix) {
|
||||||
|
pcntl_signal(SIGINT, $prevHandler);
|
||||||
|
}
|
||||||
|
if ($handleInterruptsWindows) {
|
||||||
|
sapi_windows_set_ctrl_handler($prevHandler, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($handleInterruptsUnix) {
|
||||||
|
pcntl_signal(SIGINT, $prevHandler);
|
||||||
|
}
|
||||||
|
if ($handleInterruptsWindows) {
|
||||||
|
sapi_windows_set_ctrl_handler($prevHandler, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// do a last write so that we write the repository even if nothing changed
|
||||||
|
// as that can trigger an update of some files like InstalledVersions.php if
|
||||||
|
// running a new composer version
|
||||||
|
$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) {
|
foreach ($operations as $index => $operation) {
|
||||||
$opType = $operation->getOperationType();
|
$opType = $operation->getOperationType();
|
||||||
|
|
||||||
|
@ -270,15 +330,8 @@ class InstallationManager
|
||||||
}
|
}
|
||||||
|
|
||||||
// execute all downloads first
|
// execute all downloads first
|
||||||
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);
|
|
||||||
if ($progress) {
|
|
||||||
$progress->clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// execute operations in batches to make sure every plugin is installed in the
|
// execute operations in batches to make sure every plugin is installed in the
|
||||||
|
@ -306,32 +359,8 @@ class InstallationManager
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($batches as $batch) {
|
foreach ($batches as $batch) {
|
||||||
$this->executeBatch($repo, $batch, $cleanupPromises, $devMode, $runScripts, $operations);
|
$this->executeBatch($repo, $batch, $cleanupPromises, $devMode, $runScripts, $allOperations);
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
|
||||||
$runCleanup();
|
|
||||||
|
|
||||||
if ($handleInterruptsUnix) {
|
|
||||||
pcntl_signal(SIGINT, $prevHandler);
|
|
||||||
}
|
|
||||||
if ($handleInterruptsWindows) {
|
|
||||||
sapi_windows_set_ctrl_handler($prevHandler, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($handleInterruptsUnix) {
|
|
||||||
pcntl_signal(SIGINT, $prevHandler);
|
|
||||||
}
|
|
||||||
if ($handleInterruptsWindows) {
|
|
||||||
sapi_windows_set_ctrl_handler($prevHandler, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// do a last write so that we write the repository even if nothing changed
|
|
||||||
// as that can trigger an update of some files like InstalledVersions.php if
|
|
||||||
// running a new composer version
|
|
||||||
$repo->write($devMode, $this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -340,6 +369,8 @@ class InstallationManager
|
||||||
*/
|
*/
|
||||||
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,7 +428,13 @@ class InstallationManager
|
||||||
}
|
}
|
||||||
|
|
||||||
// execute all prepare => installs/updates/removes => cleanup steps
|
// execute all prepare => installs/updates/removes => cleanup steps
|
||||||
if (!empty($promises)) {
|
if (count($promises)) {
|
||||||
|
$this->waitOnPromises($promises);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function waitOnPromises(array $promises)
|
||||||
|
{
|
||||||
$progress = null;
|
$progress = null;
|
||||||
if ($this->outputProgress && $this->io instanceof ConsoleIO && !$this->io->isDebug() && count($promises) > 1) {
|
if ($this->outputProgress && $this->io instanceof ConsoleIO && !$this->io->isDebug() && count($promises) > 1) {
|
||||||
$progress = $this->io->getProgressBar();
|
$progress = $this->io->getProgressBar();
|
||||||
|
@ -407,7 +444,6 @@ class InstallationManager
|
||||||
$progress->clear();
|
$progress->clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes install operation.
|
* Executes install operation.
|
||||||
|
|
|
@ -42,8 +42,28 @@ class TransactionTest extends TestCase
|
||||||
$packageG = $this->getPackage('g/g', '1.0.0'),
|
$packageG = $this->getPackage('g/g', '1.0.0'),
|
||||||
$packageA0first = $this->getPackage('a0/first', '1.2.3'),
|
$packageA0first = $this->getPackage('a0/first', '1.2.3'),
|
||||||
$packageFalias2 = $this->getAliasPackage($packageF, 'dev-bar'),
|
$packageFalias2 = $this->getAliasPackage($packageF, 'dev-bar'),
|
||||||
|
$plugin = $this->getPackage('x/plugin', '1.0.0'),
|
||||||
|
$plugin2Dep = $this->getPackage('x/plugin2-dep', '1.0.0'),
|
||||||
|
$plugin2 = $this->getPackage('x/plugin2', '1.0.0'),
|
||||||
|
$dlModifyingPlugin = $this->getPackage('x/downloads-modifying', '1.0.0'),
|
||||||
|
$dlModifyingPlugin2Dep = $this->getPackage('x/downloads-modifying2-dep', '1.0.0'),
|
||||||
|
$dlModifyingPlugin2 = $this->getPackage('x/downloads-modifying2', '1.0.0'),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$plugin->setType('composer-installer');
|
||||||
|
foreach (array($plugin2, $dlModifyingPlugin, $dlModifyingPlugin2) as $pluginPackage) {
|
||||||
|
$pluginPackage->setType('composer-plugin');
|
||||||
|
}
|
||||||
|
|
||||||
|
$plugin2->setRequires(array(
|
||||||
|
'x/plugin2-dep' => new Link('x/plugin2', 'x/plugin2-dep', $this->getVersionConstraint('=', '1.0.0'), Link::TYPE_REQUIRE),
|
||||||
|
));
|
||||||
|
$dlModifyingPlugin2->setRequires(array(
|
||||||
|
'x/downloads-modifying2-dep' => new Link('x/downloads-modifying2', 'x/downloads-modifying2-dep', $this->getVersionConstraint('=', '1.0.0'), Link::TYPE_REQUIRE),
|
||||||
|
));
|
||||||
|
$dlModifyingPlugin->setExtra(array('plugin-modifies-downloads' => true));
|
||||||
|
$dlModifyingPlugin2->setExtra(array('plugin-modifies-downloads' => true));
|
||||||
|
|
||||||
$packageD->setRequires(array(
|
$packageD->setRequires(array(
|
||||||
'f/f' => new Link('d/d', 'f/f', $this->getVersionConstraint('>', '0.2'), Link::TYPE_REQUIRE),
|
'f/f' => new Link('d/d', 'f/f', $this->getVersionConstraint('>', '0.2'), Link::TYPE_REQUIRE),
|
||||||
'g/provider' => new Link('d/d', 'g/provider', $this->getVersionConstraint('>', '0.2'), Link::TYPE_REQUIRE),
|
'g/provider' => new Link('d/d', 'g/provider', $this->getVersionConstraint('>', '0.2'), Link::TYPE_REQUIRE),
|
||||||
|
@ -54,6 +74,12 @@ class TransactionTest extends TestCase
|
||||||
array('job' => 'uninstall', 'package' => $packageC),
|
array('job' => 'uninstall', 'package' => $packageC),
|
||||||
array('job' => 'uninstall', 'package' => $packageE),
|
array('job' => 'uninstall', 'package' => $packageE),
|
||||||
array('job' => 'markAliasUninstalled', 'package' => $packageEalias),
|
array('job' => 'markAliasUninstalled', 'package' => $packageEalias),
|
||||||
|
array('job' => 'install', 'package' => $dlModifyingPlugin),
|
||||||
|
array('job' => 'install', 'package' => $dlModifyingPlugin2Dep),
|
||||||
|
array('job' => 'install', 'package' => $dlModifyingPlugin2),
|
||||||
|
array('job' => 'install', 'package' => $plugin),
|
||||||
|
array('job' => 'install', 'package' => $plugin2Dep),
|
||||||
|
array('job' => 'install', 'package' => $plugin2),
|
||||||
array('job' => 'install', 'package' => $packageA0first),
|
array('job' => 'install', 'package' => $packageA0first),
|
||||||
array('job' => 'update', 'from' => $packageB, 'to' => $packageBnew),
|
array('job' => 'update', 'from' => $packageB, 'to' => $packageBnew),
|
||||||
array('job' => 'install', 'package' => $packageG),
|
array('job' => 'install', 'package' => $packageG),
|
||||||
|
|
Loading…
Reference in New Issue