From 243946b8a4d6fb86ce9f0e648c79c53f9726247c Mon Sep 17 00:00:00 2001 From: Peter Dohogne Date: Mon, 14 Dec 2020 14:09:34 -0500 Subject: [PATCH 1/2] COMOPS-1503: Updating to support Composer 2 --- composer.json | 4 +- docs/class_descriptions.md | 2 + .../Plugin/PluginDefinition.php | 16 ++++ .../ComposerRootUpdatePlugin/README.md | 24 ++--- .../Setup/WebSetupWizardPluginInstaller.php | 60 +++++++++---- .../Updater/RootPackageRetriever.php | 87 +++++++++++++------ .../ComposerRootUpdatePlugin/composer.json | 4 +- .../Updater/RootPackageRetrieverTest.php | 35 +++++++- 8 files changed, 171 insertions(+), 61 deletions(-) diff --git a/composer.json b/composer.json index e65b255..d25d3b0 100644 --- a/composer.json +++ b/composer.json @@ -6,8 +6,8 @@ "AFL-3.0" ], "require": { - "composer/composer": "<=1.10.15", - "composer-plugin-api": "^1.0" + "composer/composer": "<=1.10.19 || >=2.0.0 <=2.0.8", + "composer-plugin-api": "^1.0 || ^2.0" }, "require-dev": { "phpunit/phpunit": "~6.5.0" diff --git a/docs/class_descriptions.md b/docs/class_descriptions.md index 7b786aa..9654471 100644 --- a/docs/class_descriptions.md +++ b/docs/class_descriptions.md @@ -131,6 +131,8 @@ This class manages the plugin's self-installation inside the `var` directory to - **`packageEvent()`** - When Composer installs or updates a required package, this method checks whether it was the plugin package that changed and calls `updateSetupWizardPlugin()` with the new version if so - Triggered by the events defined in [PluginDefinition::getSubscribedEvents()](#plugindefinition) + - **`processEvent()`** + - Helper method used by `packageEvent()` to run `updateSetupWizardPlugin()` when an appropriate [PackageEvent](https://getcomposer.org/apidoc/master/Composer/Installer/PackageEvent.html) is fired - **`doVarInstall()`** - Checks the `composer.lock` file the plugin and calls `updateSetupWizardPlugin()` with the version found there - Called by `composer magento-update-plugin install` and the Magento module setup classes ([InstallData](#installdatarecurringdataupgradedata), [RecurringData](#installdatarecurringdataupgradedata), [UpgradeData](#installdatarecurringdataupgradedata)) diff --git a/src/Magento/ComposerRootUpdatePlugin/Plugin/PluginDefinition.php b/src/Magento/ComposerRootUpdatePlugin/Plugin/PluginDefinition.php index 95805b1..c12233f 100644 --- a/src/Magento/ComposerRootUpdatePlugin/Plugin/PluginDefinition.php +++ b/src/Magento/ComposerRootUpdatePlugin/Plugin/PluginDefinition.php @@ -32,6 +32,22 @@ public function activate(Composer $composer, IOInterface $io) // Method must exist } + /** + * @inheritdoc + */ + public function deactivate(Composer $composer, IOInterface $io) + { + // Method must exist + } + + /** + * @inheritdoc + */ + public function uninstall(Composer $composer, IOInterface $io) + { + // Method must exist + } + /** * @inheritdoc */ diff --git a/src/Magento/ComposerRootUpdatePlugin/README.md b/src/Magento/ComposerRootUpdatePlugin/README.md index 1358eef..da634ab 100644 --- a/src/Magento/ComposerRootUpdatePlugin/README.md +++ b/src/Magento/ComposerRootUpdatePlugin/README.md @@ -4,24 +4,24 @@ The `magento/composer-root-update-plugin` Composer plugin resolves changes that need to be made to the root project `composer.json` file before updating to a new Magento product requirement. -This is accomplished by comparing the root `composer.json` file for the Magento project corresponding to the Magento version and edition in the current installation with the Magento project `composer.json` file for the target Magento product package when the `composer require` command runs and applying any deltas found between the two files if they do not conflict with the existing `composer.json` file in the Magento root directory. +This is accomplished by comparing the root `composer.json` file for the Magento project corresponding to the Magento version and edition in the current installation with the Magento project `composer.json` file for the target Magento product or cloud metapackage when the `composer require` command runs and applying any deltas found between the two files if they do not conflict with the existing `composer.json` file in the Magento root directory. # Getting Started ## System requirements -The `magento/composer-root-update-plugin` package requires Composer version 1.8.0 or earlier. Compatibility with newer Composer versions will be tested and added in future plugin versions. +The `magento/composer-root-update-plugin` package requires Composer version 1.10.19 or earlier, or version 2.0.0 - 2.0.8. Compatibility with newer Composer versions will be tested and added in future plugin versions. ## Installation To install the plugin, run the following commands in the Magento root directory. - composer require magento/composer-root-update-plugin ~0.1 --no-update + composer require magento/composer-root-update-plugin ~1.1 --no-update composer update # Usage -The plugin adds functionality to the `composer require` command when a new Magento product package is required, and in most cases will not need additional options or commands run to function. +The plugin adds functionality to the `composer require` command when a new Magento product or cloud metapackage is required, and in most cases will not need additional options or commands run to function. If the `composer require` command for the target Magento package fails, one of the following may be necessary. @@ -33,9 +33,11 @@ In this case, run the following command with the appropriate values to correct t composer require --base-magento-edition '' --base-magento-version +These options are not valid for Magento Cloud installations. + ## Conflicting custom values -If the `composer.json` file has custom changes that do not match the values the plugin expects according to the installed Magento product, the entries may need to be corrected to values compatible with the target Magento package. +If the `composer.json` file has custom changes that do not match the values the plugin expects according to the installed Magento metapackage, the entries may need to be corrected to values compatible with the target Magento version. To resolve these conflicts interactively, re-run the `composer require` command with the `--interactive-magento-conflicts` option. @@ -128,23 +130,23 @@ For reference, these are the `"require"` and `"require-dev"` sections for defaul ### With `magento/composer-root-update-plugin`: -In the project directory for a Magento Open Source 2.2.8 installation, a user runs `composer require magento/composer-root-update-plugin ~0.1 --no-update` and `composer update` before the Magento Open Source 2.3.1 upgrade commands. +In the project directory for a Magento Open Source 2.2.8 installation, a user runs `composer require magento/composer-root-update-plugin ~1.1 --no-update` and `composer update` before the Magento Open Source 2.3.1 upgrade commands. ``` -$ composer require magento/composer-root-update-plugin ~0.1 --no-update +$ composer require magento/composer-root-update-plugin ~1.1 --no-update ./composer.json has been updated $ composer update Loading composer repositories with package information Updating dependencies (including require-dev) Package operations: 1 install, 0 updates, 0 removals - - Installing magento/composer-root-update-plugin (0.1.0): Downloading (100%) -Installing "magento/composer-root-update-plugin: 0.1.0" for the Web Setup Wizard + - Installing magento/composer-root-update-plugin (1.1.0): Downloading (100%) +Installing "magento/composer-root-update-plugin: 1.1.0" for the Web Setup Wizard Loading composer repositories with package information Updating dependencies Package operations: 18 installs, 0 updates, 0 removals - Installing ... ... - - Installing magento/composer-root-update-plugin (0.1.0): Downloading (100%) + - Installing magento/composer-root-update-plugin (1.1.0): Downloading (100%) Writing lock file Generating autoload files Writing lock file @@ -191,7 +193,7 @@ For reference, these are the `"require"` and `"require-dev"` sections from the ` ``` "require": { "magento/product-community-edition": "2.3.1", - "magento/composer-root-update-plugin": "~0.1" + "magento/composer-root-update-plugin": "~1.1" }, "require-dev": { "allure-framework/allure-phpunit": "~1.2.0", diff --git a/src/Magento/ComposerRootUpdatePlugin/Setup/WebSetupWizardPluginInstaller.php b/src/Magento/ComposerRootUpdatePlugin/Setup/WebSetupWizardPluginInstaller.php index 9d4fc92..f39fe90 100644 --- a/src/Magento/ComposerRootUpdatePlugin/Setup/WebSetupWizardPluginInstaller.php +++ b/src/Magento/ComposerRootUpdatePlugin/Setup/WebSetupWizardPluginInstaller.php @@ -12,6 +12,8 @@ use Composer\Installer; use Composer\Installer\PackageEvent; use Composer\Json\JsonFile; +use Composer\Package\PackageInterface; +use Composer\Plugin\PluginInterface; use Exception; use Magento\ComposerRootUpdatePlugin\Utils\Console; use Magento\ComposerRootUpdatePlugin\Utils\PackageUtils; @@ -52,26 +54,52 @@ public function __construct($console) */ public function packageEvent($event) { - $jobs = $event->getRequest()->getJobs(); $packageName = PluginDefinition::PACKAGE_NAME; - foreach ($jobs as $job) { - if (key_exists('packageName', $job) && $job['packageName'] === $packageName) { - $pkg = $event->getInstalledRepo()->findPackage($packageName, '*'); - if ($pkg !== null) { - $version = $pkg->getPrettyVersion(); - try { - $composer = $event->getComposer(); - $this->updateSetupWizardPlugin( - $composer, - $composer->getConfig()->getConfigSource()->getName(), - $version - ); - } catch (Exception $e) { - $this->console->error("Web Setup Wizard installation of \"$packageName: $version\" failed", $e); + $apiMajorVersion = explode('.', PluginInterface::PLUGIN_API_VERSION)[0]; + if ($apiMajorVersion == '1') { + $jobs = $event->getRequest()->getJobs(); + foreach ($jobs as $job) { + if (key_exists('packageName', $job) && $job['packageName'] === $packageName) { + $pkg = $event->getInstalledRepo()->findPackage($packageName, '*'); + if ($pkg !== null) { + $this->processEvent($pkg, $event); + break; } - break; } } + } elseif ($apiMajorVersion == '2') { + if (strpos($event->getOperation()->show(false), $packageName) !== false) { + $pkg = $event->getLocalRepo()->findPackage($packageName, '*'); + if ($pkg !== null) { + $this->processEvent($pkg, $event); + } + } + } else { + $this->console->error( + "Web Setup Wizard installation of \"$packageName\" failed; unrecognized composer plugin API version" + ); + } + } + + /** + * Helper function to attempt to run updateSetupWizardPlugin when an appropriate PackageEvent is fired + * + * @param $pkg PackageInterface + * @param $event PackageEvent + */ + public function processEvent($pkg, $event) + { + $packageName = PluginDefinition::PACKAGE_NAME; + $version = $pkg->getPrettyVersion(); + try { + $composer = $event->getComposer(); + $this->updateSetupWizardPlugin( + $composer, + $composer->getConfig()->getConfigSource()->getName(), + $version + ); + } catch (Exception $e) { + $this->console->error("Web Setup Wizard installation of \"$packageName: $version\" failed", $e); } } diff --git a/src/Magento/ComposerRootUpdatePlugin/Updater/RootPackageRetriever.php b/src/Magento/ComposerRootUpdatePlugin/Updater/RootPackageRetriever.php index 6e43d73..143bc73 100644 --- a/src/Magento/ComposerRootUpdatePlugin/Updater/RootPackageRetriever.php +++ b/src/Magento/ComposerRootUpdatePlugin/Updater/RootPackageRetriever.php @@ -14,7 +14,9 @@ use Composer\Package\RootPackageInterface; use Composer\Package\Version\VersionParser; use Composer\Package\Version\VersionSelector; +use Composer\Plugin\PluginInterface; use Composer\Repository\CompositeRepository; +use Composer\Repository\RepositorySet; use Composer\Repository\VcsRepository; use Magento\ComposerRootUpdatePlugin\ComposerReimplementation\AccessibleRootPackageLoader; use Magento\ComposerRootUpdatePlugin\Utils\PackageUtils; @@ -249,6 +251,7 @@ protected function fetchMageRootFromRepo( $phpVersion = null, $preferredStability = 'stable' ) { + $apiMajorVersion = explode('.', PluginInterface::PLUGIN_API_VERSION)[0]; $packageName = $this->pkgUtils->getProjectPackageName($edition); $parsedConstraint = (new VersionParser())->parseConstraints($constraint); @@ -263,26 +266,6 @@ protected function fetchMageRootFromRepo( : $minStability; $this->console->comment("Minimum stability for \"$packageName: $constraint\": $stability", IOInterface::DEBUG); - $pool = new Pool( - $stability, - $stabilityFlags, - [$packageName => $parsedConstraint] - ); - if ($edition == PackageUtils::CLOUD_PKG_EDITION) { - // magento/magento-cloud-template exists on github, not the composer repo - $repoConfig = [ - 'url' => 'https://github.com/magento/magento-cloud', - 'type' => 'vcs' - ]; - $pool->addRepository(new VcsRepository( - $repoConfig, - $this->console->getIO(), - $this->composer->getConfig() - )); - } else { - $pool->addRepository(new CompositeRepository($this->composer->getRepositoryManager()->getRepositories())); - } - $metapackageName = $this->pkgUtils->getMetapackageName($edition); if ($edition != PackageUtils::CLOUD_PKG_EDITION && !$this->pkgUtils->isConstraintStrict($constraint)) { $this->console->warning( @@ -293,13 +276,65 @@ protected function fetchMageRootFromRepo( } $phpVersion = $ignorePlatformReqs ? null : $phpVersion; + $versionSelector = null; + $result = null; + if ($apiMajorVersion == '1') { + $pool = new Pool( + $stability, + $stabilityFlags, + [$packageName => $parsedConstraint] + ); + if ($edition == PackageUtils::CLOUD_PKG_EDITION) { + // magento/magento-cloud-template exists on github, not the composer repo + $repoConfig = [ + 'url' => 'https://github.com/magento/magento-cloud', + 'type' => 'vcs' + ]; + $pool->addRepository(new VcsRepository( + $repoConfig, + $this->console->getIO(), + $this->composer->getConfig() + )); + } else { + $pool->addRepository(new CompositeRepository($this->composer->getRepositoryManager()->getRepositories())); + } + + $versionSelector = new VersionSelector($pool); + $result = ($versionSelector)->findBestCandidate( + $packageName, + $constraint, + $phpVersion, + $preferredStability + ); + } elseif ($apiMajorVersion == '2') { + $repositorySet = new RepositorySet($minStability, $stabilityFlags); + if ($edition == PackageUtils::CLOUD_PKG_EDITION) { + // magento/magento-cloud-template exists on github, not the composer repo + $repoConfig = [ + 'url' => 'https://github.com/magento/magento-cloud', + 'type' => 'vcs' + ]; + $repositorySet->addRepository(new VcsRepository( + $repoConfig, + $this->console->getIO(), + $this->composer->getConfig(), + $this->composer->getLoop()->getHttpDownloader() + )); + } else { + $repositorySet->addRepository(new CompositeRepository($this->composer->getRepositoryManager()->getRepositories())); + } - $result = (new VersionSelector($pool))->findBestCandidate( - $packageName, - $constraint, - $phpVersion, - $preferredStability - ); + $versionSelector = new VersionSelector($repositorySet); + $result = ($versionSelector)->findBestCandidate( + $packageName, + $constraint, + $preferredStability + ); + } else { + $this->console->error( + "Fetching Magento root composer failed; unrecognized composer plugin API version" + ); + } if (!$result) { $err = "Could not find a Magento project package matching \"$metapackageName $constraint\""; diff --git a/src/Magento/ComposerRootUpdatePlugin/composer.json b/src/Magento/ComposerRootUpdatePlugin/composer.json index 64b8f35..b312cdb 100644 --- a/src/Magento/ComposerRootUpdatePlugin/composer.json +++ b/src/Magento/ComposerRootUpdatePlugin/composer.json @@ -8,8 +8,8 @@ "AFL-3.0" ], "require": { - "composer/composer": "<=1.10.15", - "composer-plugin-api": "^1.0" + "composer/composer": "<=1.10.19 || >=2.0.0 <=2.0.8", + "composer-plugin-api": "^1.0 || ^2.0" }, "suggest": { "magento/framework": "Enables the Magento Composer Root Update Plugin's functionality for the Web Setup Wizard" diff --git a/tests/Unit/Magento/ComposerRootUpdatePlugin/Updater/RootPackageRetrieverTest.php b/tests/Unit/Magento/ComposerRootUpdatePlugin/Updater/RootPackageRetrieverTest.php index 1ca9360..18c1057 100644 --- a/tests/Unit/Magento/ComposerRootUpdatePlugin/Updater/RootPackageRetrieverTest.php +++ b/tests/Unit/Magento/ComposerRootUpdatePlugin/Updater/RootPackageRetrieverTest.php @@ -13,6 +13,7 @@ use Composer\Package\Package; use Composer\Package\PackageInterface; use Composer\Package\RootPackageInterface; +use Composer\Plugin\PluginInterface; use Composer\Repository\ComposerRepository; use Composer\Repository\RepositoryInterface; use Composer\Repository\RepositoryManager; @@ -90,6 +91,14 @@ public function testOriginalRootFromLocker() public function testGetOriginalRootFromRepo() { $this->repo->method('whatProvides')->willReturn(['1.1.0.0' => $this->originalRoot, '2.0.0.0' => $this->targetRoot]); + $this->repo->method('loadPackages')->willReturn( + [ + 'namesFound' => [$this->originalRoot->getName()], + 'packages' => [ + spl_object_hash($this->originalRoot) => $this->originalRoot + ] + ] + ); $retriever = new RootPackageRetriever($this->console, $this->composer, 'enterprise', '2.0.0'); $retrievedOriginal = $retriever->getOriginalRootPackage(false); @@ -100,6 +109,7 @@ public function testGetOriginalRootFromRepo() public function testGetOriginalRootNotOnRepo_Override() { $this->repo->method('whatProvides')->willReturn(['2.0.0.0' => $this->targetRoot]); + $this->repo->method('loadPackages')->willReturn(['namesFound' => [], 'packages'=>[]]); $retriever = new RootPackageRetriever($this->console, $this->composer, 'enterprise', '2.0.0'); $retrievedOriginal = $retriever->getOriginalRootPackage(true); @@ -110,6 +120,7 @@ public function testGetOriginalRootNotOnRepo_Override() public function testGetOriginalRootNotOnRepo_NoOverride() { $this->repo->method('whatProvides')->willReturn(['2.0.0.0' => $this->targetRoot]); + $this->repo->method('loadPackages')->willReturn(['namesFound' => [], 'packages'=>[]]); $retriever = new RootPackageRetriever($this->console, $this->composer, 'enterprise', '2.0.0'); $retrievedOriginal = $retriever->getOriginalRootPackage(false); @@ -120,6 +131,7 @@ public function testGetOriginalRootNotOnRepo_NoOverride() public function testGetOriginalRootNotOnRepo_Confirm() { $this->repo->method('whatProvides')->willReturn(['2.0.0.0' => $this->targetRoot]); + $this->repo->method('loadPackages')->willReturn(['namesFound' => [], 'packages'=>[]]); $this->console->setInteractive(true); $this->io->method('isInteractive')->willReturn(true); $this->io->method('askConfirmation')->willReturn(true); @@ -133,6 +145,7 @@ public function testGetOriginalRootNotOnRepo_Confirm() public function testGetOriginalRootNotOnRepo_NoConfirm() { $this->repo->method('whatProvides')->willReturn(['2.0.0.0' => $this->targetRoot]); + $this->repo->method('loadPackages')->willReturn(['namesFound' => [], 'packages'=>[]]); $this->console->setInteractive(true); $this->io->method('isInteractive')->willReturn(true); $this->io->method('askConfirmation')->willReturn(false); @@ -148,6 +161,14 @@ public function testGetTargetRootFromRepo() $this->repo->method('whatProvides')->willReturn( ['1.1.0.0' => $this->originalRoot, '2.0.0.0' => $this->targetRoot] ); + $this->repo->expects($this->any())->method('loadPackages')->willReturn( + [ + 'namesFound' => [$this->originalRoot->getName()], + 'packages' => [ + spl_object_hash($this->targetRoot) => $this->targetRoot + ] + ] + ); $retriever = new RootPackageRetriever($this->console, $this->composer, 'enterprise', '2.0.0'); $retrievedTarget = $retriever->getTargetRootPackage(); @@ -158,6 +179,7 @@ public function testGetTargetRootFromRepo() public function testGetTargetRootNotOnRepo() { $this->repo->method('whatProvides')->willReturn(['1.1.0.0' => $this->originalRoot]); + $this->repo->method('loadPackages')->willReturn(['namesFound' => [], 'packages'=>[]]); $retriever = new RootPackageRetriever($this->console, $this->composer, 'enterprise', '2.0.0'); $retrievedTarget = $retriever->getTargetRootPackage(); @@ -175,6 +197,7 @@ public function testGetUserRoot() protected function setUp() { + $apiMajorVersion = explode('.', PluginInterface::PLUGIN_API_VERSION)[0]; $this->io = $this->getMockForAbstractClass(IOInterface::class); $this->console = new Console($this->io); @@ -222,10 +245,14 @@ protected function setUp() $this->targetRoot->method('getStabilityPriority')->willReturn(0); $repoManager = $this->createPartialMock(RepositoryManager::class, ['getRepositories']); - $this->repo = $this->createPartialMock(ComposerRepository::class, ['hasProviders', 'whatProvides', 'loadRootServerFile']); - $this->repo->method('hasProviders')->willReturn(true); - $this->mockProtectedProperty($this->repo, 'rfs', $this->createPartialMock(RemoteFilesystem::class, [])); - $this->repo->method('loadRootServerFile')->willReturn(true); + if ($apiMajorVersion == '1') { + $this->repo = $this->createPartialMock(ComposerRepository::class, ['hasProviders', 'whatProvides', 'loadRootServerFile']); + $this->repo->method('hasProviders')->willReturn(true); + $this->mockProtectedProperty($this->repo, 'rfs', $this->createPartialMock(RemoteFilesystem::class, [])); + $this->repo->method('loadRootServerFile')->willReturn(true); + } elseif ($apiMajorVersion == '2') { + $this->repo = $this->createPartialMock(ComposerRepository::class, ['hasProviders', 'whatProvides', 'loadRootServerFile', 'loadPackages']); + } $repoManager->method('getRepositories')->willReturn([$this->repo]); $this->composer->method('getRepositoryManager')->willReturn($repoManager); From a453f0662428e226c5eee224e8bfc475d84b5c32 Mon Sep 17 00:00:00 2001 From: Peter Dohogne Date: Tue, 15 Dec 2020 18:45:11 -0500 Subject: [PATCH 2/2] Separating out findBestCandidate() code for readability --- docs/class_descriptions.md | 6 + .../Setup/WebSetupWizardPluginInstaller.php | 7 +- .../Updater/RootPackageRetriever.php | 229 +++++++++++++----- 3 files changed, 176 insertions(+), 66 deletions(-) diff --git a/docs/class_descriptions.md b/docs/class_descriptions.md index 9654471..1999f01 100644 --- a/docs/class_descriptions.md +++ b/docs/class_descriptions.md @@ -210,6 +210,12 @@ This class contains methods to retrieve Composer [Package](https://getcomposer.o - Returns the existing root project package, including all user customizations - **`fetchMageRootFromRepo()`** - Given a Magento edition and version constraint, fetch the best-fit Magento root project package from the Composer repository or GitHub (in the case of cloud) + - **`findBestCandidate()`** + - Wrapper function around different versions of [VersionSelector::findBestCandidate()](https://getcomposer.org/apidoc/master/Composer/Package/Version/VersionSelector.html) + - **`findBestCandidateComposer1()`** + - Helper function to run [VersionSelector::findBestCandidate()](https://getcomposer.org/apidoc/master/Composer/Package/Version/VersionSelector.html) on Composer version 1.x.x + - **`findBestCandidateComposer2()`** + - Helper function to run [VersionSelector::findBestCandidate()](https://getcomposer.org/apidoc/master/Composer/Package/Version/VersionSelector.html) on Composer version 2.x.x - **`parseVersionAndEditionFromLock()`** - Inspect the `composer.lock` file for the currently-installed Magento product or cloud metapackage and parse out the edition and version for use by `getOriginalRootPackage()` - **`getTargetLabel()`** diff --git a/src/Magento/ComposerRootUpdatePlugin/Setup/WebSetupWizardPluginInstaller.php b/src/Magento/ComposerRootUpdatePlugin/Setup/WebSetupWizardPluginInstaller.php index f39fe90..c80b87b 100644 --- a/src/Magento/ComposerRootUpdatePlugin/Setup/WebSetupWizardPluginInstaller.php +++ b/src/Magento/ComposerRootUpdatePlugin/Setup/WebSetupWizardPluginInstaller.php @@ -13,7 +13,6 @@ use Composer\Installer\PackageEvent; use Composer\Json\JsonFile; use Composer\Package\PackageInterface; -use Composer\Plugin\PluginInterface; use Exception; use Magento\ComposerRootUpdatePlugin\Utils\Console; use Magento\ComposerRootUpdatePlugin\Utils\PackageUtils; @@ -55,8 +54,8 @@ public function __construct($console) public function packageEvent($event) { $packageName = PluginDefinition::PACKAGE_NAME; - $apiMajorVersion = explode('.', PluginInterface::PLUGIN_API_VERSION)[0]; - if ($apiMajorVersion == '1') { + $composerMajorVersion = explode('.', Composer::VERSION)[0]; + if ($composerMajorVersion == '1') { $jobs = $event->getRequest()->getJobs(); foreach ($jobs as $job) { if (key_exists('packageName', $job) && $job['packageName'] === $packageName) { @@ -67,7 +66,7 @@ public function packageEvent($event) } } } - } elseif ($apiMajorVersion == '2') { + } elseif ($composerMajorVersion == '2') { if (strpos($event->getOperation()->show(false), $packageName) !== false) { $pkg = $event->getLocalRepo()->findPackage($packageName, '*'); if ($pkg !== null) { diff --git a/src/Magento/ComposerRootUpdatePlugin/Updater/RootPackageRetriever.php b/src/Magento/ComposerRootUpdatePlugin/Updater/RootPackageRetriever.php index 143bc73..ab2103b 100644 --- a/src/Magento/ComposerRootUpdatePlugin/Updater/RootPackageRetriever.php +++ b/src/Magento/ComposerRootUpdatePlugin/Updater/RootPackageRetriever.php @@ -8,14 +8,13 @@ use Composer\Composer; use Composer\DependencyResolver\Pool; -use Composer\IO\IOInterface; use Composer\Package\BasePackage; use Composer\Package\PackageInterface; use Composer\Package\RootPackageInterface; use Composer\Package\Version\VersionParser; use Composer\Package\Version\VersionSelector; -use Composer\Plugin\PluginInterface; use Composer\Repository\CompositeRepository; +use Composer\Repository\PlatformRepository; use Composer\Repository\RepositorySet; use Composer\Repository\VcsRepository; use Magento\ComposerRootUpdatePlugin\ComposerReimplementation\AccessibleRootPackageLoader; @@ -251,9 +250,8 @@ protected function fetchMageRootFromRepo( $phpVersion = null, $preferredStability = 'stable' ) { - $apiMajorVersion = explode('.', PluginInterface::PLUGIN_API_VERSION)[0]; $packageName = $this->pkgUtils->getProjectPackageName($edition); - $parsedConstraint = (new VersionParser())->parseConstraints($constraint); + $phpVersion = $ignorePlatformReqs ? null : $phpVersion; $minStability = $this->composer->getPackage()->getMinimumStability(); if (!$minStability) { @@ -261,10 +259,6 @@ protected function fetchMageRootFromRepo( } $rootPackageLoader = new AccessibleRootPackageLoader(); $stabilityFlags = $rootPackageLoader->extractStabilityFlags($packageName, $constraint, $minStability); - $stability = key_exists($packageName, $stabilityFlags) - ? array_search($stabilityFlags[$packageName], BasePackage::$stabilities) - : $minStability; - $this->console->comment("Minimum stability for \"$packageName: $constraint\": $stability", IOInterface::DEBUG); $metapackageName = $this->pkgUtils->getMetapackageName($edition); if ($edition != PackageUtils::CLOUD_PKG_EDITION && !$this->pkgUtils->isConstraintStrict($constraint)) { @@ -275,76 +269,187 @@ protected function fetchMageRootFromRepo( ); } - $phpVersion = $ignorePlatformReqs ? null : $phpVersion; - $versionSelector = null; - $result = null; - if ($apiMajorVersion == '1') { - $pool = new Pool( - $stability, - $stabilityFlags, - [$packageName => $parsedConstraint] - ); - if ($edition == PackageUtils::CLOUD_PKG_EDITION) { - // magento/magento-cloud-template exists on github, not the composer repo - $repoConfig = [ - 'url' => 'https://github.com/magento/magento-cloud', - 'type' => 'vcs' - ]; - $pool->addRepository(new VcsRepository( - $repoConfig, - $this->console->getIO(), - $this->composer->getConfig() - )); - } else { - $pool->addRepository(new CompositeRepository($this->composer->getRepositoryManager()->getRepositories())); + $bestCandidate = $this->findBestCandidate( + $packageName, + $edition, + $constraint, + $minStability, + $stabilityFlags, + $preferredStability, + $ignorePlatformReqs, + $phpVersion + ); + + if (!$bestCandidate) { + $err = "Could not find a Magento project package matching \"$metapackageName $constraint\""; + if ($phpVersion) { + $err = "$err for PHP version $phpVersion"; } + $this->console->error($err); + } - $versionSelector = new VersionSelector($pool); - $result = ($versionSelector)->findBestCandidate( + return $bestCandidate; + } + + /** + * Wrapper functions around different versions of VersionSelector::findBestCandidate() + * + * @param string $packageName + * @param string $edition + * @param string $constraint + * @param string $minStability + * @param array $stabilityFlags + * @param string $preferredStability + * @param bool $ignorePlatformReqs + * @param string $phpVersion + * @return PackageInterface|false + * + * @see VersionSelector::findBestCandidate() + */ + protected function findBestCandidate( + $packageName, + $edition, + $constraint, + $minStability, + $stabilityFlags, + $preferredStability, + $ignorePlatformReqs, + $phpVersion + ) { + $composerMajorVersion = explode('.', Composer::VERSION)[0]; + $bestCandidate = null; + if ($composerMajorVersion == '1') { + $bestCandidate = $this->findBestCandidateComposer1( $packageName, + $edition, $constraint, - $phpVersion, - $preferredStability + $minStability, + $stabilityFlags, + $preferredStability, + $phpVersion ); - } elseif ($apiMajorVersion == '2') { - $repositorySet = new RepositorySet($minStability, $stabilityFlags); - if ($edition == PackageUtils::CLOUD_PKG_EDITION) { - // magento/magento-cloud-template exists on github, not the composer repo - $repoConfig = [ - 'url' => 'https://github.com/magento/magento-cloud', - 'type' => 'vcs' - ]; - $repositorySet->addRepository(new VcsRepository( - $repoConfig, - $this->console->getIO(), - $this->composer->getConfig(), - $this->composer->getLoop()->getHttpDownloader() - )); - } else { - $repositorySet->addRepository(new CompositeRepository($this->composer->getRepositoryManager()->getRepositories())); - } - - $versionSelector = new VersionSelector($repositorySet); - $result = ($versionSelector)->findBestCandidate( + } elseif ($composerMajorVersion == '2') { + $bestCandidate = $this->findBestCandidateComposer2( $packageName, + $edition, $constraint, - $preferredStability + $minStability, + $stabilityFlags, + $preferredStability, + $ignorePlatformReqs ); } else { $this->console->error( "Fetching Magento root composer failed; unrecognized composer plugin API version" ); } + return $bestCandidate; + } + + /** + * Helper function to run VersionSelector::findBestCandidate() on Composer version 1.x.x + * + * @param string $packageName + * @param string $edition + * @param string $constraint + * @param string $minStability + * @param array $stabilityFlags + * @param string $preferredStability + * @param string $phpVersion + * @return PackageInterface|false + */ + private function findBestCandidateComposer1( + $packageName, + $edition, + $constraint, + $minStability, + $stabilityFlags, + $preferredStability, + $phpVersion + ) { + $parsedConstraint = (new VersionParser())->parseConstraints($constraint); + $stability = key_exists($packageName, $stabilityFlags) + ? array_search($stabilityFlags[$packageName], BasePackage::$stabilities) + : $minStability; + + $pool = new Pool( + $stability, + $stabilityFlags, + [$packageName => $parsedConstraint] + ); + + if ($edition == PackageUtils::CLOUD_PKG_EDITION) { + // magento/magento-cloud-template exists on github, not the composer repo + $repoConfig = [ + 'url' => 'https://github.com/magento/magento-cloud', + 'type' => 'vcs' + ]; + $pool->addRepository(new VcsRepository( + $repoConfig, + $this->console->getIO(), + $this->composer->getConfig() + )); + } else { + $pool->addRepository(new CompositeRepository($this->composer->getRepositoryManager()->getRepositories())); + } - if (!$result) { - $err = "Could not find a Magento project package matching \"$metapackageName $constraint\""; - if ($phpVersion) { - $err = "$err for PHP version $phpVersion"; - } - $this->console->error($err); + return (new VersionSelector($pool))->findBestCandidate( + $packageName, + $constraint, + $phpVersion, + $preferredStability + ); + } + + /** + * Helper function to run VersionSelector::findBestCandidate() on Composer version 2.x.x + * + * @param string $packageName + * @param string $edition + * @param string $constraint + * @param string $minStability + * @param array $stabilityFlags + * @param string $preferredStability + * @param bool $ignorePlatformReqs + * @return PackageInterface|false + */ + private function findBestCandidateComposer2( + $packageName, + $edition, + $constraint, + $minStability, + $stabilityFlags, + $preferredStability, + $ignorePlatformReqs + ) { + $platformOverrides = $this->composer->getConfig()->get('platform') ?: array(); + $platformRepo = new PlatformRepository(array(), $platformOverrides); + $repositorySet = new RepositorySet($minStability, $stabilityFlags); + + if ($edition == PackageUtils::CLOUD_PKG_EDITION) { + // magento/magento-cloud-template exists on github, not the composer repo + $repoConfig = [ + 'url' => 'https://github.com/magento/magento-cloud', + 'type' => 'vcs' + ]; + $repositorySet->addRepository(new VcsRepository( + $repoConfig, + $this->console->getIO(), + $this->composer->getConfig(), + $this->composer->getLoop()->getHttpDownloader() + )); + } else { + $repositorySet->addRepository( + new CompositeRepository($this->composer->getRepositoryManager()->getRepositories()) + ); } - return $result; + return (new VersionSelector($repositorySet, $platformRepo))->findBestCandidate( + $packageName, + $constraint, + $preferredStability, + $ignorePlatformReqs + ); } /**