Skip to content
Snippets Groups Projects
Commit 008b7eff authored by s_leu's avatar s_leu Committed by Adam G-H
Browse files

Issue #3240971 by phenaproxima, s_leu, tedbow: Support install profiles/distributions

parent a480092f
No related branches found
No related tags found
No related merge requests found
Showing with 215 additions and 28 deletions
......@@ -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;
}
}
{
"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."
]
}
}
{
"packages": [
{
"name": "drupal/test-distribution",
"version": "1.0.0",
"require": {
"drupal/core": "*"
}
},
{
"name": "drupal/core",
"version": "9.8.0"
}
],
"packages-dev": []
}
{
"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."
]
}
}
{
"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": []
}
<?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);
}
}
{
"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."
]
}
}
{}
{
"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": []
}
{
"packages": [],
"packages-dev": []
}
......@@ -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();
}
......
......@@ -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();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment