From 008b7eff0cab1af9f2eb5f2302c2a0a93d6dcb04 Mon Sep 17 00:00:00 2001 From: s_leu <s_leu@1336864.no-reply.drupal.org> Date: Mon, 18 Oct 2021 18:20:57 +0000 Subject: [PATCH] Issue #3240971 by phenaproxima, s_leu, tedbow: Support install profiles/distributions --- package_manager/src/ComposerUtility.php | 54 ++++++++++------ .../tests/fixtures/distro_core/composer.json | 12 ++++ .../tests/fixtures/distro_core/composer.lock | 16 +++++ .../distro_core_recommended/composer.json | 12 ++++ .../distro_core_recommended/composer.lock | 23 +++++++ .../tests/src/Kernel/ComposerUtilityTest.php | 63 +++++++++++++++++++ tests/fixtures/fake-site/composer.json | 8 ++- tests/fixtures/fake-site/composer.lock | 24 ++++++- .../no_core_requirements/composer.lock | 4 ++ tests/src/Build/UpdateTestBase.php | 10 ++- tests/src/Kernel/UpdaterTest.php | 17 ++--- 11 files changed, 215 insertions(+), 28 deletions(-) create mode 100644 package_manager/tests/fixtures/distro_core/composer.json create mode 100644 package_manager/tests/fixtures/distro_core/composer.lock create mode 100644 package_manager/tests/fixtures/distro_core_recommended/composer.json create mode 100644 package_manager/tests/fixtures/distro_core_recommended/composer.lock create mode 100644 package_manager/tests/src/Kernel/ComposerUtilityTest.php create mode 100644 tests/fixtures/project_staged_validation/no_core_requirements/composer.lock diff --git a/package_manager/src/ComposerUtility.php b/package_manager/src/ComposerUtility.php index 121d067647..39ca395271 100644 --- a/package_manager/src/ComposerUtility.php +++ b/package_manager/src/ComposerUtility.php @@ -5,6 +5,7 @@ namespace Drupal\package_manager; use Composer\Composer; use Composer\Factory; use Composer\IO\NullIO; +use Composer\Package\PackageInterface; use Drupal\Component\Serialization\Json; /** @@ -85,21 +86,27 @@ class ComposerUtility { } /** - * Returns the names of the core packages required in composer.json. + * Returns the names of the core packages in the lock file. * * All packages listed in ../core_packages.json are considered core packages. * * @return string[] * The names of the required core packages. * - * @throws \LogicException - * If neither drupal/core or drupal/core-recommended are required. - * * @todo Make this return a keyed array of packages, not just names. */ public function getCorePackageNames(): array { - $requirements = array_keys($this->composer->getPackage()->getRequires()); - return array_intersect(static::getCorePackageList(), $requirements); + $core_packages = array_intersect( + array_keys($this->getLockedPackages()), + static::getCorePackageList() + ); + + // If drupal/core-recommended is present, it supersedes drupal/core, since + // drupal/core will always be one of its direct dependencies. + if (in_array('drupal/core-recommended', $core_packages, TRUE)) { + $core_packages = array_diff($core_packages, ['drupal/core']); + } + return array_values($core_packages); } /** @@ -112,24 +119,35 @@ class ComposerUtility { * All Drupal extension packages in the lock file, keyed by name. */ public function getDrupalExtensionPackages(): array { + $filter = function (PackageInterface $package): bool { + $drupal_package_types = [ + 'drupal-module', + 'drupal-theme', + 'drupal-custom-module', + 'drupal-custom-theme', + ]; + return in_array($package->getType(), $drupal_package_types, TRUE); + }; + return array_filter($this->getLockedPackages(), $filter); + } + + /** + * Returns all packages in the lock file. + * + * @return \Composer\Package\PackageInterface[] + * All packages in the lock file, keyed by name. + */ + protected function getLockedPackages(): array { $locked_packages = $this->composer->getLocker() ->getLockedRepository(TRUE) ->getPackages(); - $drupal_package_types = [ - 'drupal-module', - 'drupal-theme', - 'drupal-custom-module', - 'drupal-custom-theme', - ]; - $drupal_packages = []; + $packages = []; foreach ($locked_packages as $package) { - if (in_array($package->getType(), $drupal_package_types, TRUE)) { - $key = $package->getName(); - $drupal_packages[$key] = $package; - } + $key = $package->getName(); + $packages[$key] = $package; } - return $drupal_packages; + return $packages; } } diff --git a/package_manager/tests/fixtures/distro_core/composer.json b/package_manager/tests/fixtures/distro_core/composer.json new file mode 100644 index 0000000000..a7a8274cb6 --- /dev/null +++ b/package_manager/tests/fixtures/distro_core/composer.json @@ -0,0 +1,12 @@ +{ + "require": { + "drupal/test-distribution": "*" + }, + "extra": { + "_comment": [ + "This is a fake composer.json simulating a site which requires a distribution.", + "The required core packages are determined by scanning the lock file.", + "The fake distribution requires Drupal core directly." + ] + } +} diff --git a/package_manager/tests/fixtures/distro_core/composer.lock b/package_manager/tests/fixtures/distro_core/composer.lock new file mode 100644 index 0000000000..56572fd029 --- /dev/null +++ b/package_manager/tests/fixtures/distro_core/composer.lock @@ -0,0 +1,16 @@ +{ + "packages": [ + { + "name": "drupal/test-distribution", + "version": "1.0.0", + "require": { + "drupal/core": "*" + } + }, + { + "name": "drupal/core", + "version": "9.8.0" + } + ], + "packages-dev": [] +} diff --git a/package_manager/tests/fixtures/distro_core_recommended/composer.json b/package_manager/tests/fixtures/distro_core_recommended/composer.json new file mode 100644 index 0000000000..3a62074ca2 --- /dev/null +++ b/package_manager/tests/fixtures/distro_core_recommended/composer.json @@ -0,0 +1,12 @@ +{ + "require": { + "drupal/test-distribution": "*" + }, + "extra": { + "_comment": [ + "This is a fake composer.json simulating a site which requires a distribution.", + "The required core packages are determined by scanning the lock file.", + "The fake distribution uses drupal/core-recommended to require Drupal core." + ] + } +} diff --git a/package_manager/tests/fixtures/distro_core_recommended/composer.lock b/package_manager/tests/fixtures/distro_core_recommended/composer.lock new file mode 100644 index 0000000000..dd29a0514d --- /dev/null +++ b/package_manager/tests/fixtures/distro_core_recommended/composer.lock @@ -0,0 +1,23 @@ +{ + "packages": [ + { + "name": "drupal/test-distribution", + "version": "1.0.0", + "require": { + "drupal/core-recommended": "*" + } + }, + { + "name": "drupal/core-recommended", + "version": "9.8.0", + "require": { + "drupal/core": "9.8.0" + } + }, + { + "name": "drupal/core", + "version": "9.8.0" + } + ], + "packages-dev": [] +} diff --git a/package_manager/tests/src/Kernel/ComposerUtilityTest.php b/package_manager/tests/src/Kernel/ComposerUtilityTest.php new file mode 100644 index 0000000000..3b3cac5d92 --- /dev/null +++ b/package_manager/tests/src/Kernel/ComposerUtilityTest.php @@ -0,0 +1,63 @@ +<?php + +namespace Drupal\Tests\package_manager\Kernel; + +use Drupal\KernelTests\KernelTestBase; +use Drupal\package_manager\ComposerUtility; + +/** + * @coversDefaultClass \Drupal\package_manager\ComposerUtility + * + * @group package_manager + */ +class ComposerUtilityTest extends KernelTestBase { + + /** + * {@inheritdoc} + */ + protected static $modules = ['package_manager']; + + /** + * Data provider for ::testCorePackagesFromLockFile(). + * + * @return array[] + * Sets of arguments to pass to the test method. + */ + public function providerCorePackagesFromLockFile(): array { + $fixtures_dir = __DIR__ . '/../../fixtures'; + + return [ + 'distro with drupal/core-recommended' => [ + // This fixture's lock file mentions drupal/core, which is considered a + // canonical core package, but it will be ignored in favor of + // drupal/core-recommended, which always requires drupal/core as one of + // its direct dependencies. + "$fixtures_dir/distro_core_recommended", + ['drupal/core-recommended'], + ], + 'distro with drupal/core' => [ + "$fixtures_dir/distro_core", + ['drupal/core'], + ], + ]; + } + + /** + * Tests that required core packages are found by scanning the lock file. + * + * @param string $dir + * The path of the fake site fixture. + * @param string[] $expected_packages + * The names of the core packages which should be detected. + * + * @covers ::getCorePackageNames + * + * @dataProvider providerCorePackagesFromLockFile + */ + public function testCorePackagesFromLockFile(string $dir, array $expected_packages): void { + $packages = ComposerUtility::createForDirectory($dir) + ->getCorePackageNames(); + $this->assertSame($expected_packages, $packages); + } + +} diff --git a/tests/fixtures/fake-site/composer.json b/tests/fixtures/fake-site/composer.json index 74d8204d88..21939a78ce 100644 --- a/tests/fixtures/fake-site/composer.json +++ b/tests/fixtures/fake-site/composer.json @@ -1,5 +1,11 @@ { "require": { - "drupal/core": "*" + "drupal/test-distribution": "*" + }, + "extra": { + "_comment": [ + "This is a fake composer.json simulating a site which requires a distribution.", + "The required core packages are determined by scanning the lock file." + ] } } diff --git a/tests/fixtures/fake-site/composer.lock b/tests/fixtures/fake-site/composer.lock index 0967ef424b..dd29a0514d 100644 --- a/tests/fixtures/fake-site/composer.lock +++ b/tests/fixtures/fake-site/composer.lock @@ -1 +1,23 @@ -{} +{ + "packages": [ + { + "name": "drupal/test-distribution", + "version": "1.0.0", + "require": { + "drupal/core-recommended": "*" + } + }, + { + "name": "drupal/core-recommended", + "version": "9.8.0", + "require": { + "drupal/core": "9.8.0" + } + }, + { + "name": "drupal/core", + "version": "9.8.0" + } + ], + "packages-dev": [] +} diff --git a/tests/fixtures/project_staged_validation/no_core_requirements/composer.lock b/tests/fixtures/project_staged_validation/no_core_requirements/composer.lock new file mode 100644 index 0000000000..b44dcb4aea --- /dev/null +++ b/tests/fixtures/project_staged_validation/no_core_requirements/composer.lock @@ -0,0 +1,4 @@ +{ + "packages": [], + "packages-dev": [] +} diff --git a/tests/src/Build/UpdateTestBase.php b/tests/src/Build/UpdateTestBase.php index 56f7f60ec5..8826c0bac9 100644 --- a/tests/src/Build/UpdateTestBase.php +++ b/tests/src/Build/UpdateTestBase.php @@ -181,8 +181,16 @@ END; // @see ::installQuickStart() $this->webRoot = $data['extra']['drupal-scaffold']['locations']['web-root']; - // Update the test site's composer.json and install dependencies. + // Update the test site's composer.json. $this->writeJson($composer, $data); + // Don't install drupal/core-dev, which is defined as a dev dependency in + // both project templates. + // @todo Handle dev dependencies properly once + // https://www.drupal.org/project/automatic_updates/issues/3244412 is + // is resolved. + $this->executeCommand('composer remove --dev --no-update drupal/core-dev'); + $this->assertCommandSuccessful(); + // Install production dependencies. $this->executeCommand('composer install --no-dev'); $this->assertCommandSuccessful(); } diff --git a/tests/src/Kernel/UpdaterTest.php b/tests/src/Kernel/UpdaterTest.php index 602ea49baf..d6bd48448f 100644 --- a/tests/src/Kernel/UpdaterTest.php +++ b/tests/src/Kernel/UpdaterTest.php @@ -2,6 +2,7 @@ namespace Drupal\Tests\automatic_updates\Kernel; +use Drupal\automatic_updates\PathLocator; use Prophecy\Argument; /** @@ -27,6 +28,14 @@ class UpdaterTest extends AutomaticUpdatesKernelTestBase { public function testCorrectVersionsStaged() { $this->setReleaseMetadata(__DIR__ . '/../../fixtures/release-history/drupal.9.8.1-security.xml'); + // Point to a fake site which requires Drupal core via a distribution. The + // lock file should be scanned to determine the core packages, which should + // result in drupal/core-recommended being updated. + $locator = $this->prophesize(PathLocator::class); + $locator->getActiveDirectory()->willReturn(__DIR__ . '/../../fixtures/fake-site'); + $locator->getStageDirectory()->willReturn('/tmp'); + $this->container->set('automatic_updates.path_locator', $locator->reveal()); + $this->container->get('automatic_updates.updater')->begin([ 'drupal' => '9.8.1', ]); @@ -38,13 +47,7 @@ class UpdaterTest extends AutomaticUpdatesKernelTestBase { $stager = $this->prophesize('\PhpTuf\ComposerStager\Domain\StagerInterface'); $command = [ 'require', - 'drupal/core:9.8.1', - // These two plugins are in the root composer.json that ships with a - // git clone of Drupal core, so they will be included when determining - // which core packages to update. - // @see \Drupal\automatic_updates\Updater::getCorePackageNames() - 'drupal/core-project-message:9.8.1', - 'drupal/core-vendor-hardening:9.8.1', + 'drupal/core-recommended:9.8.1', '--update-with-all-dependencies', ]; $stager->stage($command, Argument::cetera())->shouldBeCalled(); -- GitLab