From f4b80818a2aee49006573ae511446dd099d9afa9 Mon Sep 17 00:00:00 2001 From: catch <catch@35733.no-reply.drupal.org> Date: Sun, 21 Aug 2022 16:35:26 +0900 Subject: [PATCH] Issue #3272110 by Mile23, ravi.shankar, Spokje, xjm, andypost, joachim: Drupal 9 and 10's Drupal\Component composer.json files are totally out of date --- composer.json | 3 +- composer/Composer.php | 12 + composer/Generator/ComponentGenerator.php | 234 ++++++++++++++++++ composer/Util/SemanticVersion.php | 24 ++ .../Drupal/Component/Annotation/composer.json | 42 ++-- .../Drupal/Component/Assertion/composer.json | 31 ++- .../Component/ClassFinder/composer.json | 31 ++- .../Drupal/Component/Datetime/composer.json | 36 +-- .../DependencyInjection/composer.json | 53 ++-- core/lib/Drupal/Component/Diff/composer.json | 31 ++- .../Drupal/Component/Discovery/composer.json | 38 +-- .../Component/EventDispatcher/composer.json | 37 +-- .../Drupal/Component/FileCache/composer.json | 31 ++- .../Component/FileSecurity/composer.json | 31 ++- .../Drupal/Component/FileSystem/composer.json | 31 ++- .../Component/FrontMatter/composer.json | 36 +-- .../Drupal/Component/Gettext/composer.json | 41 +-- core/lib/Drupal/Component/Graph/composer.json | 31 ++- .../Component/HttpFoundation/composer.json | 33 ++- .../Drupal/Component/PhpStorage/composer.json | 36 +-- .../lib/Drupal/Component/Plugin/composer.json | 41 +-- .../Component/ProxyBuilder/composer.json | 32 ++- .../lib/Drupal/Component/Render/composer.json | 36 +-- .../Component/Serialization/composer.json | 33 ++- .../Component/Transliteration/composer.json | 33 ++- .../Drupal/Component/Utility/composer.json | 31 ++- core/lib/Drupal/Component/Uuid/composer.json | 36 +-- .../Drupal/Component/Version/composer.json | 31 ++- .../Component/ComponentsIsolatedBuildTest.php | 86 +++++++ .../Component/ComponentsTaggedReleaseTest.php | 78 ++++++ .../Composer/ComposerBuildTestBase.php | 62 +++++ .../Template/ComposerProjectTemplatesTest.php | 24 +- 32 files changed, 1007 insertions(+), 358 deletions(-) create mode 100644 composer/Generator/ComponentGenerator.php create mode 100644 composer/Util/SemanticVersion.php create mode 100644 core/tests/Drupal/BuildTests/Composer/Component/ComponentsIsolatedBuildTest.php create mode 100644 core/tests/Drupal/BuildTests/Composer/Component/ComponentsTaggedReleaseTest.php create mode 100644 core/tests/Drupal/BuildTests/Composer/ComposerBuildTestBase.php diff --git a/composer.json b/composer.json index 722912c2103b..f5475d93abc6 100644 --- a/composer.json +++ b/composer.json @@ -108,7 +108,8 @@ "@composer update phpunit/phpunit --with-dependencies --no-progress" ], "post-update-cmd": [ - "Drupal\\Composer\\Composer::generateMetapackages" + "Drupal\\Composer\\Composer::generateMetapackages", + "Drupal\\Composer\\Composer::generateComponentPackages" ], "phpcs": "phpcs --standard=core/phpcs.xml.dist --", "phpcbf": "phpcbf --standard=core/phpcs.xml.dist --" diff --git a/composer/Composer.php b/composer/Composer.php index 040e8dd73c16..bdf53cfad137 100644 --- a/composer/Composer.php +++ b/composer/Composer.php @@ -6,6 +6,7 @@ use Composer\Script\Event; use Composer\Semver\Comparator; use Composer\Semver\VersionParser; +use Drupal\Composer\Generator\ComponentGenerator; use Drupal\Composer\Generator\PackageGenerator; use Symfony\Component\Finder\Finder; @@ -30,6 +31,17 @@ public static function generateMetapackages(Event $event): void { $generator->generate($event->getIO(), getcwd()); } + /** + * Update component packages whenever composer.lock is updated. + * + * @param \Composer\Script\Event $event + * The Composer event. + */ + public static function generateComponentPackages(Event $event): void { + $generator = new ComponentGenerator(); + $generator->generate($event, getcwd()); + } + /** * Set the version of Drupal; used in release process and by the test suite. * diff --git a/composer/Generator/ComponentGenerator.php b/composer/Generator/ComponentGenerator.php new file mode 100644 index 000000000000..c82468cbe7b0 --- /dev/null +++ b/composer/Generator/ComponentGenerator.php @@ -0,0 +1,234 @@ +<?php + +namespace Drupal\Composer\Generator; + +use Composer\IO\IOInterface; +use Composer\Semver\VersionParser; +use Composer\Script\Event; +use Composer\Util\Filesystem; +use Drupal\Composer\Composer; +use Drupal\Composer\Generator\Util\DrupalCoreComposer; +use Drupal\Composer\Util\SemanticVersion; +use Symfony\Component\Finder\Finder; + +/** + * Reconciles Drupal component dependencies with core. + */ +class ComponentGenerator { + + /** + * Relative path from Drupal root to the component directory. + * + * @var string + */ + protected static $relativeComponentPath = 'core/lib/Drupal/Component'; + + /** + * Full path to the component directory. + * + * @var string + */ + protected $componentBaseDir; + + /** + * Data from drupal/drupal's composer.json file. + * + * @var \Drupal\Composer\Generator\Util\DrupalCoreComposer + */ + protected $drupalProjectInfo; + + /** + * Data from drupal/core's composer.json file. + * + * @var \Drupal\Composer\Generator\Util\DrupalCoreComposer + */ + protected $drupalCoreInfo; + + /** + * ComponentGenerator constructor. + */ + public function __construct() { + $this->componentBaseDir = dirname(__DIR__, 2) . '/' . static::$relativeComponentPath; + } + + /** + * Find all the composer.json files for components. + * + * @return \Symfony\Component\Finder\Finder + * A Finder object with all the composer.json files for components. + */ + public function getComponentPathsFinder(): Finder { + $composer_json_finder = new Finder(); + $composer_json_finder->name('composer.json') + ->in($this->componentBaseDir) + ->ignoreUnreadableDirs() + ->depth(1); + return $composer_json_finder; + } + + /** + * Reconcile Drupal's components whenever composer.lock is updated. + * + * @param \Composer\Script\Event $event + * The Composer event. + * @param string $base_dir + * Directory where drupal/drupal repository is located. + */ + public function generate(Event $event, string $base_dir): void { + $io = $event->getIO(); + // General information from drupal/drupal and drupal/core composer.json + // and composer.lock files. + $this->drupalProjectInfo = DrupalCoreComposer::createFromPath($base_dir); + $this->drupalCoreInfo = DrupalCoreComposer::createFromPath($base_dir . '/core'); + + $changed = FALSE; + /** @var \Symfony\Component\Finder\SplFileInfo $component_composer_json */ + foreach ($this->getComponentPathsFinder()->getIterator() as $component_composer_json) { + $changed |= $this->generateComponentPackage($event, $component_composer_json->getRelativePathname()); + } + + // Remind the user not to miss files in a patch. + if ($changed) { + $io->write("If you make a patch, ensure that the files above are included."); + } + } + + /** + * Generate the component JSON files. + * + * @param \Composer\Script\Event $event + * The Composer event. + * @param string $component_pathname + * Relative path to the composer.json file for a component. + * + * @return bool + * TRUE if the generated component package is different from what is on + * disk. + */ + protected function generateComponentPackage(Event $event, string $component_pathname): bool { + $io = $event->getIO(); + $composer_json_path = $this->componentBaseDir . '/' . $component_pathname; + $original_composer_json = file_exists($composer_json_path) ? file_get_contents($composer_json_path) : ''; + + // Modify the original data. + $composer_json_data = $this->getPackage($io, $original_composer_json); + $updated_composer_json = static::encode($composer_json_data); + + // Exit early if nothing changed. + if (trim($original_composer_json, " \t\r\0\x0B") === trim($updated_composer_json, " \t\r\0\x0B")) { + return FALSE; + } + + // Warn the user that a component file has been updated. + $display_path = static::$relativeComponentPath . '/' . $component_pathname; + $io->write("Updated component file <info>$display_path</info>."); + + // Write the composer.json file back to disk. + $fs = new Filesystem(); + $fs->ensureDirectoryExists(dirname($composer_json_path)); + file_put_contents($composer_json_path, $updated_composer_json); + + return TRUE; + } + + /** + * Reconcile component dependencies with core. + * + * @param \Composer\IO\IOInterface $io + * IO object for messages to the user. + * @param string $original_json + * Contents of the component's composer.json file. + * + * @return array + * Structured data to be turned back into JSON. + */ + protected function getPackage(IOInterface $io, string $original_json): array { + $original_data = json_decode($original_json, TRUE); + $package_data = array_merge($original_data, $this->initialPackageMetadata()); + + $core_info = $this->drupalCoreInfo->rootComposerJson(); + + $stability = VersionParser::parseStability(\Drupal::VERSION); + + // List of packages which we didn't find in either core requirement. + $not_in_core = []; + + // Traverse required packages. + foreach (array_keys($original_data['require'] ?? []) as $package_name) { + // Reconcile locked constraints from drupal/drupal. We might have a locked + // version of a dependency that's not present in drupal/core. + if ($info = $this->drupalProjectInfo->packageLockInfo($package_name)) { + $package_data['require'][$package_name] = $info['version']; + } + // The package wasn't in the lock file, which means we need to tell the + // user. But there are some packages we want to exclude from this list. + elseif ($package_name !== 'php' && (strpos($package_name, 'drupal/core-') === FALSE)) { + $not_in_core[$package_name] = $package_name; + } + + // Reconcile looser constraints from drupal/core, and we're totally OK + // with over-writing the locked ones from above. + if ($constraint = $core_info['require'][$package_name] ?? FALSE) { + $package_data['require'][$package_name] = $constraint; + } + + // Reconcile dependencies on other Drupal components, so we can set the + // constraint to our current version. + if (strpos($package_name, 'drupal/core-') !== FALSE) { + if ($stability === 'stable') { + // Set the constraint to ^maj.min. + $package_data['require'][$package_name] = SemanticVersion::majorMinorConstraint(\Drupal::VERSION); + } + else { + // For non-stable releases, set the constraint to the branch version. + $package_data['require'][$package_name] = Composer::drupalVersionBranch(); + // Also for non-stable releases which depend on another component, + // set the minimum stability. We do this so we can test build the + // components. Minimum-stability is otherwise ignored for packages + // which aren't the root package, so for any other purpose, this is + // unneeded. + $package_data['minimum-stability'] = $stability; + } + } + } + if ($not_in_core) { + $io->error($package_data['name'] . ' requires packages not present in drupal/drupal: ' . implode(', ', $not_in_core)); + } + + return $package_data; + } + + /** + * Utility function to encode package json in a consistent way. + * + * @param array $composer_json_data + * Data to encode into a json string. + * + * @return string + * Encoded version of provided json data. + */ + public static function encode(array $composer_json_data): string { + return json_encode($composer_json_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n"; + } + + /** + * Common default metadata for all components. + * + * @return array + * An array containing the common default metadata for all components. + */ + protected function initialPackageMetadata(): array { + return [ + 'extra' => [ + '_readme' => [ + 'This file was partially generated automatically. See: https://www.drupal.org/node/3293830', + ], + ], + // Always reconcile PHP version. + 'require' => [ + 'php' => '>=7.3.0', + ], + ]; + } + +} diff --git a/composer/Util/SemanticVersion.php b/composer/Util/SemanticVersion.php new file mode 100644 index 000000000000..c42e1a6f1c06 --- /dev/null +++ b/composer/Util/SemanticVersion.php @@ -0,0 +1,24 @@ +<?php + +namespace Drupal\Composer\Util; + +/** + * Utility methods for manipulating semantic versions. + */ +class SemanticVersion { + + /** + * Given a version, generate a loose ^major.minor constraint. + * + * @param string $version + * Semantic version string. Example: 9.5.0-beta23. + * + * @return string + * Constraint string for major and minor. Example: ^9.5 + */ + public static function majorMinorConstraint(string $version): string { + preg_match('/^(\d+)\.(\d+)\.\d+/', $version, $matches); + return '^' . $matches[1] . '.' . $matches[2]; + } + +} diff --git a/core/lib/Drupal/Component/Annotation/composer.json b/core/lib/Drupal/Component/Annotation/composer.json index c252a2f97148..a631867d4367 100644 --- a/core/lib/Drupal/Component/Annotation/composer.json +++ b/core/lib/Drupal/Component/Annotation/composer.json @@ -1,19 +1,27 @@ { - "name": "drupal/core-annotation", - "description": "Annotation discovery and implementation of plugins.", - "keywords": ["drupal"], - "homepage": "https://www.drupal.org/project/drupal", - "license": "GPL-2.0-or-later", - "require": { - "php": ">=7.3.0", - "doctrine/annotations": "^1.4", - "drupal/core-file-cache": "^8.8", - "drupal/core-plugin": "^8.8", - "drupal/core-utility": "^8.8" - }, - "autoload": { - "psr-4": { - "Drupal\\Component\\Annotation\\": "" - } - } + "name": "drupal/core-annotation", + "description": "Annotation discovery and implementation of plugins.", + "keywords": [ + "drupal" + ], + "homepage": "https://www.drupal.org/project/drupal", + "license": "GPL-2.0-or-later", + "require": { + "php": ">=8.1.0", + "doctrine/annotations": "^1.13", + "drupal/core-file-cache": "10.1.x-dev", + "drupal/core-plugin": "10.1.x-dev", + "drupal/core-utility": "10.1.x-dev" + }, + "autoload": { + "psr-4": { + "Drupal\\Component\\Annotation\\": "" + } + }, + "extra": { + "_readme": [ + "This file was partially generated automatically. See: https://www.drupal.org/node/3293830" + ] + }, + "minimum-stability": "dev" } diff --git a/core/lib/Drupal/Component/Assertion/composer.json b/core/lib/Drupal/Component/Assertion/composer.json index 661654b13362..49e2e0e75bbc 100644 --- a/core/lib/Drupal/Component/Assertion/composer.json +++ b/core/lib/Drupal/Component/Assertion/composer.json @@ -1,15 +1,22 @@ { - "name": "drupal/core-assertion", - "description": "Provides helper functionality for runtime assertions.", - "keywords": ["drupal"], - "homepage": "https://www.drupal.org/project/drupal", - "license": "GPL-2.0-or-later", - "require": { - "php": ">=7.3.0" - }, - "autoload": { - "psr-4": { - "Drupal\\Component\\Assertion\\": "" + "name": "drupal/core-assertion", + "description": "Provides helper functionality for runtime assertions.", + "keywords": [ + "drupal" + ], + "homepage": "https://www.drupal.org/project/drupal", + "license": "GPL-2.0-or-later", + "require": { + "php": ">=8.1.0" + }, + "autoload": { + "psr-4": { + "Drupal\\Component\\Assertion\\": "" + } + }, + "extra": { + "_readme": [ + "This file was partially generated automatically. See: https://www.drupal.org/node/3293830" + ] } - } } diff --git a/core/lib/Drupal/Component/ClassFinder/composer.json b/core/lib/Drupal/Component/ClassFinder/composer.json index 9efde0acdda7..d31e53402777 100644 --- a/core/lib/Drupal/Component/ClassFinder/composer.json +++ b/core/lib/Drupal/Component/ClassFinder/composer.json @@ -1,15 +1,22 @@ { - "name": "drupal/core-class-finder", - "description": "This class provides a class finding utility.", - "keywords": ["drupal"], - "homepage": "https://www.drupal.org/project/drupal", - "license": "GPL-2.0-or-later", - "require": { - "php": ">=7.3.0" - }, - "autoload": { - "psr-4": { - "Drupal\\Component\\ClassFinder\\": "" + "name": "drupal/core-class-finder", + "description": "This class provides a class finding utility.", + "keywords": [ + "drupal" + ], + "homepage": "https://www.drupal.org/project/drupal", + "license": "GPL-2.0-or-later", + "require": { + "php": ">=8.1.0" + }, + "autoload": { + "psr-4": { + "Drupal\\Component\\ClassFinder\\": "" + } + }, + "extra": { + "_readme": [ + "This file was partially generated automatically. See: https://www.drupal.org/node/3293830" + ] } - } } diff --git a/core/lib/Drupal/Component/Datetime/composer.json b/core/lib/Drupal/Component/Datetime/composer.json index b06d7433a308..7a1429803d6e 100644 --- a/core/lib/Drupal/Component/Datetime/composer.json +++ b/core/lib/Drupal/Component/Datetime/composer.json @@ -1,16 +1,24 @@ { - "name": "drupal/core-datetime", - "description": "This class wraps the PHP DateTime class with more flexible initialization parameters, allowing a date to be created from an existing date object, a timestamp, a string with an unknown format, a string with a known format, or an array of date parts. It also adds an errors array and a __toString() method to the date object.", - "keywords": ["drupal"], - "homepage": "https://www.drupal.org/project/drupal", - "license": "GPL-2.0-or-later", - "require": { - "php": ">=7.3.0", - "drupal/core-utility": "^8.8" - }, - "autoload": { - "psr-4": { - "Drupal\\Component\\Datetime\\": "" - } - } + "name": "drupal/core-datetime", + "description": "This class wraps the PHP DateTime class with more flexible initialization parameters, allowing a date to be created from an existing date object, a timestamp, a string with an unknown format, a string with a known format, or an array of date parts. It also adds an errors array and a __toString() method to the date object.", + "keywords": [ + "drupal" + ], + "homepage": "https://www.drupal.org/project/drupal", + "license": "GPL-2.0-or-later", + "require": { + "php": ">=8.1.0", + "drupal/core-utility": "10.1.x-dev" + }, + "autoload": { + "psr-4": { + "Drupal\\Component\\Datetime\\": "" + } + }, + "extra": { + "_readme": [ + "This file was partially generated automatically. See: https://www.drupal.org/node/3293830" + ] + }, + "minimum-stability": "dev" } diff --git a/core/lib/Drupal/Component/DependencyInjection/composer.json b/core/lib/Drupal/Component/DependencyInjection/composer.json index e370fa3ed783..edf38af9998f 100644 --- a/core/lib/Drupal/Component/DependencyInjection/composer.json +++ b/core/lib/Drupal/Component/DependencyInjection/composer.json @@ -1,26 +1,33 @@ { - "name": "drupal/core-dependency-injection", - "description": "Dependency Injection container optimized for Drupal's needs.", - "keywords": ["drupal", "dependency injection"], - "type": "library", - "homepage": "https://www.drupal.org/project/drupal", - "license": "GPL-2.0-or-later", - "support": { - "issues": "https://www.drupal.org/project/issues/drupal", - "irc": "irc://irc.freenode.net/drupal-contribute", - "source": "https://www.drupal.org/project/drupal/git-instructions" - }, - "require": { - "php": ">=7.3.0", - "symfony/dependency-injection": "^4.4", - "symfony/service-contracts": "^1.1|^2" - }, - "suggest": { - "symfony/expression-language": "For using expressions in service container configuration" - }, - "autoload": { - "psr-4": { - "Drupal\\Component\\DependencyInjection\\": "" + "name": "drupal/core-dependency-injection", + "description": "Dependency Injection container optimized for Drupal's needs.", + "keywords": [ + "drupal", + "dependency injection" + ], + "type": "library", + "homepage": "https://www.drupal.org/project/drupal", + "license": "GPL-2.0-or-later", + "support": { + "issues": "https://www.drupal.org/project/issues/drupal", + "source": "https://www.drupal.org/project/drupal/git-instructions" + }, + "require": { + "php": ">=8.1.0", + "symfony/dependency-injection": "^6.1", + "symfony/service-contracts": "v3.1.0" + }, + "suggest": { + "symfony/expression-language": "For using expressions in service container configuration" + }, + "autoload": { + "psr-4": { + "Drupal\\Component\\DependencyInjection\\": "" + } + }, + "extra": { + "_readme": [ + "This file was partially generated automatically. See: https://www.drupal.org/node/3293830" + ] } - } } diff --git a/core/lib/Drupal/Component/Diff/composer.json b/core/lib/Drupal/Component/Diff/composer.json index 7af1986afd43..972c5e2f771b 100644 --- a/core/lib/Drupal/Component/Diff/composer.json +++ b/core/lib/Drupal/Component/Diff/composer.json @@ -1,15 +1,22 @@ { - "name": "drupal/core-diff", - "description": "Diff.", - "keywords": ["drupal"], - "homepage": "https://www.drupal.org/project/drupal", - "license": "GPL-2.0-or-later", - "require": { - "php": ">=7.3.0" - }, - "autoload": { - "psr-4": { - "Drupal\\Component\\Diff\\": "" + "name": "drupal/core-diff", + "description": "Diff.", + "keywords": [ + "drupal" + ], + "homepage": "https://www.drupal.org/project/drupal", + "license": "GPL-2.0-or-later", + "require": { + "php": ">=8.1.0" + }, + "autoload": { + "psr-4": { + "Drupal\\Component\\Diff\\": "" + } + }, + "extra": { + "_readme": [ + "This file was partially generated automatically. See: https://www.drupal.org/node/3293830" + ] } - } } diff --git a/core/lib/Drupal/Component/Discovery/composer.json b/core/lib/Drupal/Component/Discovery/composer.json index 59fe78eeceee..5d3ea3487c10 100644 --- a/core/lib/Drupal/Component/Discovery/composer.json +++ b/core/lib/Drupal/Component/Discovery/composer.json @@ -1,17 +1,25 @@ { - "name": "drupal/core-discovery", - "description": "Discovery.", - "keywords": ["drupal"], - "homepage": "https://www.drupal.org/project/drupal", - "license": "GPL-2.0-or-later", - "require": { - "php": ">=7.3.0", - "drupal/core-file-cache": "^8.8", - "drupal/core-serialization": "^8.8" - }, - "autoload": { - "psr-4": { - "Drupal\\Component\\Discovery\\": "" - } - } + "name": "drupal/core-discovery", + "description": "Discovery.", + "keywords": [ + "drupal" + ], + "homepage": "https://www.drupal.org/project/drupal", + "license": "GPL-2.0-or-later", + "require": { + "php": ">=8.1.0", + "drupal/core-file-cache": "10.1.x-dev", + "drupal/core-serialization": "10.1.x-dev" + }, + "autoload": { + "psr-4": { + "Drupal\\Component\\Discovery\\": "" + } + }, + "extra": { + "_readme": [ + "This file was partially generated automatically. See: https://www.drupal.org/node/3293830" + ] + }, + "minimum-stability": "dev" } diff --git a/core/lib/Drupal/Component/EventDispatcher/composer.json b/core/lib/Drupal/Component/EventDispatcher/composer.json index 3574908ffbd7..fadafccc8195 100644 --- a/core/lib/Drupal/Component/EventDispatcher/composer.json +++ b/core/lib/Drupal/Component/EventDispatcher/composer.json @@ -1,18 +1,25 @@ { - "name": "drupal/core-event-dispatcher", - "description": "EventDispatcher.", - "keywords": ["drupal"], - "homepage": "https://www.drupal.org/project/drupal", - "license": "GPL-2.0-or-later", - "require": { - "php": ">=7.3.0", - "symfony/dependency-injection": "^4.4", - "symfony/event-dispatcher": "^4.4", - "symfony/event-dispatcher-contracts": "^1.1" - }, - "autoload": { - "psr-4": { - "Drupal\\Component\\EventDispatcher\\": "" + "name": "drupal/core-event-dispatcher", + "description": "EventDispatcher.", + "keywords": [ + "drupal" + ], + "homepage": "https://www.drupal.org/project/drupal", + "license": "GPL-2.0-or-later", + "require": { + "php": ">=8.1.0", + "symfony/dependency-injection": "^6.1", + "symfony/event-dispatcher": "^6.1", + "symfony/event-dispatcher-contracts": "v3.1.0" + }, + "autoload": { + "psr-4": { + "Drupal\\Component\\EventDispatcher\\": "" + } + }, + "extra": { + "_readme": [ + "This file was partially generated automatically. See: https://www.drupal.org/node/3293830" + ] } - } } diff --git a/core/lib/Drupal/Component/FileCache/composer.json b/core/lib/Drupal/Component/FileCache/composer.json index 68f202b57a4d..c262143bb5c8 100644 --- a/core/lib/Drupal/Component/FileCache/composer.json +++ b/core/lib/Drupal/Component/FileCache/composer.json @@ -1,15 +1,22 @@ { - "name": "drupal/core-file-cache", - "description": "FileCache.", - "keywords": ["drupal"], - "homepage": "https://www.drupal.org/project/drupal", - "license": "GPL-2.0-or-later", - "require": { - "php": ">=7.3.0" - }, - "autoload": { - "psr-4": { - "Drupal\\Component\\FileCache\\": "" + "name": "drupal/core-file-cache", + "description": "FileCache.", + "keywords": [ + "drupal" + ], + "homepage": "https://www.drupal.org/project/drupal", + "license": "GPL-2.0-or-later", + "require": { + "php": ">=8.1.0" + }, + "autoload": { + "psr-4": { + "Drupal\\Component\\FileCache\\": "" + } + }, + "extra": { + "_readme": [ + "This file was partially generated automatically. See: https://www.drupal.org/node/3293830" + ] } - } } diff --git a/core/lib/Drupal/Component/FileSecurity/composer.json b/core/lib/Drupal/Component/FileSecurity/composer.json index 33a11ae6ef2f..880de76a6c58 100644 --- a/core/lib/Drupal/Component/FileSecurity/composer.json +++ b/core/lib/Drupal/Component/FileSecurity/composer.json @@ -1,15 +1,22 @@ { - "name": "drupal/core-file-security", - "description": "FileSecurity.", - "keywords": ["drupal"], - "homepage": "https://www.drupal.org/project/drupal", - "license": "GPL-2.0-or-later", - "require": { - "php": ">=7.3.0" - }, - "autoload": { - "psr-4": { - "Drupal\\Component\\FileSecurity\\": "" + "name": "drupal/core-file-security", + "description": "FileSecurity.", + "keywords": [ + "drupal" + ], + "homepage": "https://www.drupal.org/project/drupal", + "license": "GPL-2.0-or-later", + "require": { + "php": ">=8.1.0" + }, + "autoload": { + "psr-4": { + "Drupal\\Component\\FileSecurity\\": "" + } + }, + "extra": { + "_readme": [ + "This file was partially generated automatically. See: https://www.drupal.org/node/3293830" + ] } - } } diff --git a/core/lib/Drupal/Component/FileSystem/composer.json b/core/lib/Drupal/Component/FileSystem/composer.json index 41fa20509495..975c5d554a34 100644 --- a/core/lib/Drupal/Component/FileSystem/composer.json +++ b/core/lib/Drupal/Component/FileSystem/composer.json @@ -1,15 +1,22 @@ { - "name": "drupal/core-filesystem", - "description": "FileSystem.", - "keywords": ["drupal"], - "homepage": "https://www.drupal.org/project/drupal", - "license": "GPL-2.0-or-later", - "require": { - "php": ">=7.3.0" - }, - "autoload": { - "psr-4": { - "Drupal\\Component\\FileSystem\\": "" + "name": "drupal/core-filesystem", + "description": "FileSystem.", + "keywords": [ + "drupal" + ], + "homepage": "https://www.drupal.org/project/drupal", + "license": "GPL-2.0-or-later", + "require": { + "php": ">=8.1.0" + }, + "autoload": { + "psr-4": { + "Drupal\\Component\\FileSystem\\": "" + } + }, + "extra": { + "_readme": [ + "This file was partially generated automatically. See: https://www.drupal.org/node/3293830" + ] } - } } diff --git a/core/lib/Drupal/Component/FrontMatter/composer.json b/core/lib/Drupal/Component/FrontMatter/composer.json index 15a0b081eca1..2fc1da7a757f 100644 --- a/core/lib/Drupal/Component/FrontMatter/composer.json +++ b/core/lib/Drupal/Component/FrontMatter/composer.json @@ -1,16 +1,24 @@ { - "name": "drupal/core-front-matter", - "description": "Component for parsing front matter from a source.", - "keywords": ["drupal"], - "homepage": "https://www.drupal.org/project/drupal", - "license": "GPL-2.0-or-later", - "require": { - "php": ">=7.3.0", - "drupal/core-serialization": "^8.8" - }, - "autoload": { - "psr-4": { - "Drupal\\Component\\FrontMatter\\": "" - } - } + "name": "drupal/core-front-matter", + "description": "Component for parsing front matter from a source.", + "keywords": [ + "drupal" + ], + "homepage": "https://www.drupal.org/project/drupal", + "license": "GPL-2.0-or-later", + "require": { + "php": ">=8.1.0", + "drupal/core-serialization": "10.1.x-dev" + }, + "autoload": { + "psr-4": { + "Drupal\\Component\\FrontMatter\\": "" + } + }, + "extra": { + "_readme": [ + "This file was partially generated automatically. See: https://www.drupal.org/node/3293830" + ] + }, + "minimum-stability": "dev" } diff --git a/core/lib/Drupal/Component/Gettext/composer.json b/core/lib/Drupal/Component/Gettext/composer.json index 7320270e3852..9b6a7f3e8bb3 100644 --- a/core/lib/Drupal/Component/Gettext/composer.json +++ b/core/lib/Drupal/Component/Gettext/composer.json @@ -1,20 +1,25 @@ { - "name": "drupal/core-gettext", - "description": "PHP library for reading PO files.", - "type": "library", - "license": "GPL-2.0-or-later", - "support": { - "issues": "https://www.drupal.org/project/issues/drupal", - "irc": "irc://irc.freenode.net/drupal-contribute", - "source": "https://www.drupal.org/project/drupal/git-instructions" - }, - "require": { - "php": ">=7.3.0", - "drupal/core-render": "^9.4" - }, - "autoload": { - "psr-4": { - "Drupal\\Component\\Gettext\\": "" - } - } + "name": "drupal/core-gettext", + "description": "PHP library for reading PO files.", + "type": "library", + "license": "GPL-2.0-or-later", + "support": { + "issues": "https://www.drupal.org/project/issues/drupal", + "source": "https://www.drupal.org/project/drupal/git-instructions" + }, + "require": { + "php": ">=8.1.0", + "drupal/core-render": "10.1.x-dev" + }, + "autoload": { + "psr-4": { + "Drupal\\Component\\Gettext\\": "" + } + }, + "extra": { + "_readme": [ + "This file was partially generated automatically. See: https://www.drupal.org/node/3293830" + ] + }, + "minimum-stability": "dev" } diff --git a/core/lib/Drupal/Component/Graph/composer.json b/core/lib/Drupal/Component/Graph/composer.json index dbcc0eebb15a..c6c4e2b02ce2 100644 --- a/core/lib/Drupal/Component/Graph/composer.json +++ b/core/lib/Drupal/Component/Graph/composer.json @@ -1,15 +1,22 @@ { - "name": "drupal/core-graph", - "description": "Graph.", - "keywords": ["drupal"], - "homepage": "https://www.drupal.org/project/drupal", - "license": "GPL-2.0-or-later", - "require": { - "php": ">=7.3.0" - }, - "autoload": { - "psr-4": { - "Drupal\\Component\\Graph\\": "" + "name": "drupal/core-graph", + "description": "Graph.", + "keywords": [ + "drupal" + ], + "homepage": "https://www.drupal.org/project/drupal", + "license": "GPL-2.0-or-later", + "require": { + "php": ">=8.1.0" + }, + "autoload": { + "psr-4": { + "Drupal\\Component\\Graph\\": "" + } + }, + "extra": { + "_readme": [ + "This file was partially generated automatically. See: https://www.drupal.org/node/3293830" + ] } - } } diff --git a/core/lib/Drupal/Component/HttpFoundation/composer.json b/core/lib/Drupal/Component/HttpFoundation/composer.json index 0c481d6011fe..e2d1c69354b2 100644 --- a/core/lib/Drupal/Component/HttpFoundation/composer.json +++ b/core/lib/Drupal/Component/HttpFoundation/composer.json @@ -1,16 +1,23 @@ { - "name": "drupal/core-http-foundation", - "description": "HttpFoundation.", - "keywords": ["drupal"], - "homepage": "https://www.drupal.org/project/drupal", - "license": "GPL-2.0-or-later", - "require": { - "php": ">=7.3.0", - "symfony/http-foundation": "^4.4" - }, - "autoload": { - "psr-4": { - "Drupal\\Component\\HttpFoundation\\": "" + "name": "drupal/core-http-foundation", + "description": "HttpFoundation.", + "keywords": [ + "drupal" + ], + "homepage": "https://www.drupal.org/project/drupal", + "license": "GPL-2.0-or-later", + "require": { + "php": ">=8.1.0", + "symfony/http-foundation": "^6.1.1" + }, + "autoload": { + "psr-4": { + "Drupal\\Component\\HttpFoundation\\": "" + } + }, + "extra": { + "_readme": [ + "This file was partially generated automatically. See: https://www.drupal.org/node/3293830" + ] } - } } diff --git a/core/lib/Drupal/Component/PhpStorage/composer.json b/core/lib/Drupal/Component/PhpStorage/composer.json index 776426ba35b6..603314d00f8e 100644 --- a/core/lib/Drupal/Component/PhpStorage/composer.json +++ b/core/lib/Drupal/Component/PhpStorage/composer.json @@ -1,16 +1,24 @@ { - "name": "drupal/core-php-storage", - "description": "PhpStorage.", - "keywords": ["drupal"], - "homepage": "https://www.drupal.org/project/drupal", - "license": "GPL-2.0-or-later", - "require": { - "php": ">=7.3.0", - "drupal/core-file-security": "^8.8" - }, - "autoload": { - "psr-4": { - "Drupal\\Component\\PhpStorage\\": "" - } - } + "name": "drupal/core-php-storage", + "description": "PhpStorage.", + "keywords": [ + "drupal" + ], + "homepage": "https://www.drupal.org/project/drupal", + "license": "GPL-2.0-or-later", + "require": { + "php": ">=8.1.0", + "drupal/core-file-security": "10.1.x-dev" + }, + "autoload": { + "psr-4": { + "Drupal\\Component\\PhpStorage\\": "" + } + }, + "extra": { + "_readme": [ + "This file was partially generated automatically. See: https://www.drupal.org/node/3293830" + ] + }, + "minimum-stability": "dev" } diff --git a/core/lib/Drupal/Component/Plugin/composer.json b/core/lib/Drupal/Component/Plugin/composer.json index 8814a8ef3a9d..6f4cdbae6425 100644 --- a/core/lib/Drupal/Component/Plugin/composer.json +++ b/core/lib/Drupal/Component/Plugin/composer.json @@ -1,19 +1,28 @@ { - "name": "drupal/core-plugin", - "description": "Base building block for a scalable and extensible plug-in system for PHP components and application framework extensions.", - "keywords": ["drupal", "plugin", "plugins"], - "homepage": "https://www.drupal.org/project/drupal", - "license": "GPL-2.0-or-later", - "require": { - "php": ">=7.3.0", - "symfony/validator": "^4.4" - }, - "autoload": { - "psr-4": { - "Drupal\\Component\\Plugin\\": "" + "name": "drupal/core-plugin", + "description": "Base building block for a scalable and extensible plug-in system for PHP components and application framework extensions.", + "keywords": [ + "drupal", + "plugin", + "plugins" + ], + "homepage": "https://www.drupal.org/project/drupal", + "license": "GPL-2.0-or-later", + "require": { + "php": ">=8.1.0", + "symfony/validator": "^6.1" + }, + "autoload": { + "psr-4": { + "Drupal\\Component\\Plugin\\": "" + } + }, + "suggest": { + "symfony/validator": "Leveraged in the use of context aware plugins." + }, + "extra": { + "_readme": [ + "This file was partially generated automatically. See: https://www.drupal.org/node/3293830" + ] } - }, - "suggest": { - "symfony/validator": "Leveraged in the use of context aware plugins." - } } diff --git a/core/lib/Drupal/Component/ProxyBuilder/composer.json b/core/lib/Drupal/Component/ProxyBuilder/composer.json index 9b798e9265d1..68b9be598cd8 100644 --- a/core/lib/Drupal/Component/ProxyBuilder/composer.json +++ b/core/lib/Drupal/Component/ProxyBuilder/composer.json @@ -1,15 +1,23 @@ { - "name": "drupal/core-proxy-builder", - "description": "Provides a lightweight mechanism to provide lazy loaded proxies.", - "keywords": ["drupal", "proxy"], - "homepage": "https://www.drupal.org/project/drupal", - "license": "GPL-2.0-or-later", - "require": { - "php": ">=7.3.0" - }, - "autoload": { - "psr-4": { - "Drupal\\Component\\ProxyBuilder\\": "" + "name": "drupal/core-proxy-builder", + "description": "Provides a lightweight mechanism to provide lazy loaded proxies.", + "keywords": [ + "drupal", + "proxy" + ], + "homepage": "https://www.drupal.org/project/drupal", + "license": "GPL-2.0-or-later", + "require": { + "php": ">=8.1.0" + }, + "autoload": { + "psr-4": { + "Drupal\\Component\\ProxyBuilder\\": "" + } + }, + "extra": { + "_readme": [ + "This file was partially generated automatically. See: https://www.drupal.org/node/3293830" + ] } - } } diff --git a/core/lib/Drupal/Component/Render/composer.json b/core/lib/Drupal/Component/Render/composer.json index 5b35fcb2a3fd..ba1f47c04bbb 100644 --- a/core/lib/Drupal/Component/Render/composer.json +++ b/core/lib/Drupal/Component/Render/composer.json @@ -1,16 +1,24 @@ { - "name": "drupal/core-render", - "description": "Renders placeholder variables for HTML and plain-text display.", - "keywords": ["drupal"], - "homepage": "https://www.drupal.org/project/drupal", - "license": "GPL-2.0-or-later", - "require": { - "php": ">=7.3.0", - "drupal/core-utility": "^8.8" - }, - "autoload": { - "psr-4": { - "Drupal\\Component\\Render\\": "" - } - } + "name": "drupal/core-render", + "description": "Renders placeholder variables for HTML and plain-text display.", + "keywords": [ + "drupal" + ], + "homepage": "https://www.drupal.org/project/drupal", + "license": "GPL-2.0-or-later", + "require": { + "php": ">=8.1.0", + "drupal/core-utility": "10.1.x-dev" + }, + "autoload": { + "psr-4": { + "Drupal\\Component\\Render\\": "" + } + }, + "extra": { + "_readme": [ + "This file was partially generated automatically. See: https://www.drupal.org/node/3293830" + ] + }, + "minimum-stability": "dev" } diff --git a/core/lib/Drupal/Component/Serialization/composer.json b/core/lib/Drupal/Component/Serialization/composer.json index 7b40c55ddc31..52fb12a5d2ae 100644 --- a/core/lib/Drupal/Component/Serialization/composer.json +++ b/core/lib/Drupal/Component/Serialization/composer.json @@ -1,16 +1,23 @@ { - "name": "drupal/core-serialization", - "description": "Serialization.", - "keywords": ["drupal"], - "homepage": "https://www.drupal.org/project/drupal", - "license": "GPL-2.0-or-later", - "require": { - "php": ">=7.3.0", - "symfony/yaml": "^4.4" - }, - "autoload": { - "psr-4": { - "Drupal\\Component\\Serialization\\": "" + "name": "drupal/core-serialization", + "description": "Serialization.", + "keywords": [ + "drupal" + ], + "homepage": "https://www.drupal.org/project/drupal", + "license": "GPL-2.0-or-later", + "require": { + "php": ">=8.1.0", + "symfony/yaml": "^6.1" + }, + "autoload": { + "psr-4": { + "Drupal\\Component\\Serialization\\": "" + } + }, + "extra": { + "_readme": [ + "This file was partially generated automatically. See: https://www.drupal.org/node/3293830" + ] } - } } diff --git a/core/lib/Drupal/Component/Transliteration/composer.json b/core/lib/Drupal/Component/Transliteration/composer.json index abdce4fc813c..7e42ba488ab7 100644 --- a/core/lib/Drupal/Component/Transliteration/composer.json +++ b/core/lib/Drupal/Component/Transliteration/composer.json @@ -1,16 +1,23 @@ { - "name": "drupal/core-transliteration", - "description": "Transliteration.", - "type": "library", - "license": "GPL-2.0-or-later", - "support": { - "issues": "https://www.drupal.org/project/issues/drupal", - "irc": "irc://irc.freenode.net/drupal-contribute", - "source": "https://www.drupal.org/project/drupal/git-instructions" - }, - "autoload": { - "psr-4": { - "Drupal\\Component\\Transliteration\\": "" + "name": "drupal/core-transliteration", + "description": "Transliteration.", + "type": "library", + "license": "GPL-2.0-or-later", + "support": { + "issues": "https://www.drupal.org/project/issues/drupal", + "source": "https://www.drupal.org/project/drupal/git-instructions" + }, + "autoload": { + "psr-4": { + "Drupal\\Component\\Transliteration\\": "" + } + }, + "extra": { + "_readme": [ + "This file was partially generated automatically. See: https://www.drupal.org/node/3293830" + ] + }, + "require": { + "php": ">=8.1.0" } - } } diff --git a/core/lib/Drupal/Component/Utility/composer.json b/core/lib/Drupal/Component/Utility/composer.json index be455d0f4f00..da6c4a096be8 100644 --- a/core/lib/Drupal/Component/Utility/composer.json +++ b/core/lib/Drupal/Component/Utility/composer.json @@ -1,15 +1,22 @@ { - "name": "drupal/core-utility", - "description": "Mostly static utility classes for string, xss, array, image, and other commonly needed manipulations.", - "keywords": ["drupal"], - "homepage": "https://www.drupal.org/project/drupal", - "license": "GPL-2.0-or-later", - "require": { - "php": ">=7.3.0" - }, - "autoload": { - "psr-4": { - "Drupal\\Component\\Utility\\": "" + "name": "drupal/core-utility", + "description": "Mostly static utility classes for string, xss, array, image, and other commonly needed manipulations.", + "keywords": [ + "drupal" + ], + "homepage": "https://www.drupal.org/project/drupal", + "license": "GPL-2.0-or-later", + "require": { + "php": ">=8.1.0" + }, + "autoload": { + "psr-4": { + "Drupal\\Component\\Utility\\": "" + } + }, + "extra": { + "_readme": [ + "This file was partially generated automatically. See: https://www.drupal.org/node/3293830" + ] } - } } diff --git a/core/lib/Drupal/Component/Uuid/composer.json b/core/lib/Drupal/Component/Uuid/composer.json index 0a4681e0eeba..0e021a23c446 100644 --- a/core/lib/Drupal/Component/Uuid/composer.json +++ b/core/lib/Drupal/Component/Uuid/composer.json @@ -1,19 +1,23 @@ { - "name": "drupal/core-uuid", - "description": "UUID generation and validation.", - "type": "library", - "license": "GPL-2.0-or-later", - "support": { - "issues": "https://www.drupal.org/project/issues/drupal", - "irc": "irc://irc.freenode.net/drupal-contribute", - "source": "https://www.drupal.org/project/drupal/git-instructions" - }, - "require": { - "php": ">=7.3.0" - }, - "autoload": { - "psr-4": { - "Drupal\\Component\\Uuid\\": "" + "name": "drupal/core-uuid", + "description": "UUID generation and validation.", + "type": "library", + "license": "GPL-2.0-or-later", + "support": { + "issues": "https://www.drupal.org/project/issues/drupal", + "source": "https://www.drupal.org/project/drupal/git-instructions" + }, + "require": { + "php": ">=8.1.0" + }, + "autoload": { + "psr-4": { + "Drupal\\Component\\Uuid\\": "" + } + }, + "extra": { + "_readme": [ + "This file was partially generated automatically. See: https://www.drupal.org/node/3293830" + ] } - } } diff --git a/core/lib/Drupal/Component/Version/composer.json b/core/lib/Drupal/Component/Version/composer.json index 02e2814dda42..d7785a3a49ab 100644 --- a/core/lib/Drupal/Component/Version/composer.json +++ b/core/lib/Drupal/Component/Version/composer.json @@ -1,15 +1,22 @@ { - "name": "drupal/core-version", - "description": "Utility classes for process Drupal specific version information.", - "keywords": ["drupal"], - "homepage": "https://www.drupal.org/project/drupal", - "license": "GPL-2.0-or-later", - "require": { - "php": ">=7.3.0" - }, - "autoload": { - "psr-4": { - "Drupal\\Component\\Version\\": "" + "name": "drupal/core-version", + "description": "Utility classes for process Drupal specific version information.", + "keywords": [ + "drupal" + ], + "homepage": "https://www.drupal.org/project/drupal", + "license": "GPL-2.0-or-later", + "require": { + "php": ">=8.1.0" + }, + "autoload": { + "psr-4": { + "Drupal\\Component\\Version\\": "" + } + }, + "extra": { + "_readme": [ + "This file was partially generated automatically. See: https://www.drupal.org/node/3293830" + ] } - } } diff --git a/core/tests/Drupal/BuildTests/Composer/Component/ComponentsIsolatedBuildTest.php b/core/tests/Drupal/BuildTests/Composer/Component/ComponentsIsolatedBuildTest.php new file mode 100644 index 000000000000..fdad24db7d65 --- /dev/null +++ b/core/tests/Drupal/BuildTests/Composer/Component/ComponentsIsolatedBuildTest.php @@ -0,0 +1,86 @@ +<?php + +namespace Drupal\BuildTests\Composer\Component; + +use Drupal\BuildTests\Composer\ComposerBuildTestBase; +use Drupal\Composer\Composer; + +/** + * Try to install dependencies per component, using Composer. + * + * @group #slow + * @group Composer + * @group Component + * + * @coversNothing + * + * @requires externalCommand composer + */ +class ComponentsIsolatedBuildTest extends ComposerBuildTestBase { + + /** + * Provides an array with relative paths to the component paths. + * + * @return array + * An array with relative paths to the component paths. + */ + public function provideComponentPaths(): array { + $data = []; + // During the dataProvider phase, there is not a workspace directory yet. + // So we will find relative paths and assemble them with the workspace + // path later. + $drupal_root = $this->getDrupalRoot(); + $composer_json_finder = $this->getComponentPathsFinder($drupal_root); + + /** @var \Symfony\Component\Finder\SplFileInfo $path */ + foreach ($composer_json_finder->getIterator() as $path) { + $data[] = ['/' . $path->getRelativePath()]; + } + return $data; + } + + /** + * Test whether components' composer.json can be installed in isolation. + * + * @dataProvider provideComponentPaths + */ + public function testComponentComposerJson(string $component_path): void { + // Only copy the components. Copy all of them because some of them depend on + // each other. + $finder = $this->getCodebaseFinder(); + $finder->in($this->getDrupalRoot() . static::$componentsPath); + $this->copyCodebase($finder->getIterator()); + + $working_dir = $this->getWorkingPath() . static::$componentsPath . $component_path; + + // We add path repositories so we can wire internal dependencies together. + $this->addExpectedRepositories($working_dir); + + // Perform the installation. + $this->executeCommand("composer install --working-dir=$working_dir --no-interaction --no-progress"); + $this->assertCommandSuccessful(); + } + + /** + * Adds expected repositories as path repositories to package under test. + * + * @param string $working_dir + * The working directory. + */ + protected function addExpectedRepositories(string $working_dir): void { + $repo_paths = [ + 'Render' => 'drupal/core-render', + 'Utility' => 'drupal/core-utility', + ]; + foreach ($repo_paths as $path => $package_name) { + $path_repo = $this->getWorkingPath() . static::$componentsPath . '/' . $path; + $repo_name = strtolower($path); + // Add path repositories with the current version number to the current + // package under test. + $drupal_version = Composer::drupalVersionBranch(); + $this->executeCommand("composer config repositories.$repo_name " . + "'{\"type\": \"path\",\"url\": \"$path_repo\",\"options\": {\"versions\": {\"$package_name\": \"$drupal_version\"}}}' --working-dir=$working_dir"); + } + } + +} diff --git a/core/tests/Drupal/BuildTests/Composer/Component/ComponentsTaggedReleaseTest.php b/core/tests/Drupal/BuildTests/Composer/Component/ComponentsTaggedReleaseTest.php new file mode 100644 index 000000000000..946a57d20ec7 --- /dev/null +++ b/core/tests/Drupal/BuildTests/Composer/Component/ComponentsTaggedReleaseTest.php @@ -0,0 +1,78 @@ +<?php + +namespace Drupal\BuildTests\Composer\Component; + +use Drupal\BuildTests\Composer\ComposerBuildTestBase; +use Drupal\Composer\Composer; + +/** + * Demonstrate that the Component generator responds to release tagging. + * + * @group #slow + * @group Composer + * @group Component + * + * @coversNothing + * + * @requires externalCommand composer + */ +class ComponentsTaggedReleaseTest extends ComposerBuildTestBase { + + /** + * Highly arbitrary version and constraint expectations. + * + * @return array + * - First element is the tag that should be applied to \Drupal::version. + * - Second element is the resulting constraint which should be present in + * the component core dependencies. + */ + public function providerVersionConstraint(): array { + return [ + // [Tag, constraint] + '1.0.x-dev' => ['1.0.x-dev', '1.0.x-dev'], + '1.0.0-beta1' => ['1.0.0-beta1', '1.0.0-beta1'], + '1.0.0-rc1' => ['1.0.0-rc1', '1.0.0-rc1'], + '1.0.0' => ['1.0.0', '^1.0'], + ]; + } + + /** + * Validate release tagging and regeneration of dependencies. + * + * @dataProvider providerVersionConstraint + */ + public function testReleaseTagging(string $tag, string $constraint): void { + $this->copyCodebase(); + $drupal_root = $this->getWorkspaceDirectory(); + + // Set the core version. + Composer::setDrupalVersion($drupal_root, $tag); + $this->assertDrupalVersion($tag, $drupal_root); + + // Emulate the release script. + // @see https://github.com/xjm/drupal_core_release/blob/main/tag.sh + $this->executeCommand("COMPOSER_ROOT_VERSION=\"$tag\" composer update drupal/core*"); + $this->assertCommandSuccessful(); + $this->assertErrorOutputContains('generateComponentPackages'); + + // Find all the components. + $component_finder = $this->getComponentPathsFinder($drupal_root); + + // Loop through all the component packages. + /** @var \Symfony\Component\Finder\SplFileInfo $composer_json */ + foreach ($component_finder->getIterator() as $composer_json) { + $composer_json_data = json_decode(file_get_contents($composer_json->getPathname()), TRUE); + $requires = array_merge( + $composer_json_data['require'] ?? [], + $composer_json_data['require-dev'] ?? [] + ); + // Required packages from drupal/core-* should have our constraint. + foreach ($requires as $package => $req_constraint) { + if (strpos($package, 'drupal/core-') !== FALSE) { + $this->assertEquals($constraint, $req_constraint); + } + } + } + } + +} diff --git a/core/tests/Drupal/BuildTests/Composer/ComposerBuildTestBase.php b/core/tests/Drupal/BuildTests/Composer/ComposerBuildTestBase.php new file mode 100644 index 000000000000..619fd8d10484 --- /dev/null +++ b/core/tests/Drupal/BuildTests/Composer/ComposerBuildTestBase.php @@ -0,0 +1,62 @@ +<?php + +namespace Drupal\BuildTests\Composer; + +use Drupal\BuildTests\Framework\BuildTestBase; +use Symfony\Component\Finder\Finder; + +/** + * Base class for Composer build tests. + * + * @coversNothing + */ +abstract class ComposerBuildTestBase extends BuildTestBase { + + /** + * Relative path from Drupal root to the Components directory. + * + * @var string + */ + protected static $componentsPath = '/core/lib/Drupal/Component'; + + /** + * Assert that the VERSION constant in Drupal.php is the expected value. + * + * @param string $expectedVersion + * The expected version. + * @param string $dir + * The path to the site root. + * + * @internal + */ + protected function assertDrupalVersion(string $expectedVersion, string $dir): void { + $drupal_php_path = $dir . '/core/lib/Drupal.php'; + $this->assertFileExists($drupal_php_path); + + // Read back the Drupal version that was set and assert it matches + // expectations + $this->executeCommand("php -r 'include \"$drupal_php_path\"; print \Drupal::VERSION;'"); + $this->assertCommandSuccessful(); + $this->assertCommandOutputContains($expectedVersion); + } + + /** + * Find all the composer.json files for components. + * + * @param string $drupal_root + * The Drupal root directory. + * + * @return \Symfony\Component\Finder\Finder + * A Finder object with all the composer.json files for components. + */ + protected function getComponentPathsFinder(string $drupal_root): Finder { + $finder = new Finder(); + $finder->name('composer.json') + ->in($drupal_root . static::$componentsPath) + ->ignoreUnreadableDirs() + ->depth(1); + + return $finder; + } + +} diff --git a/core/tests/Drupal/BuildTests/Composer/Template/ComposerProjectTemplatesTest.php b/core/tests/Drupal/BuildTests/Composer/Template/ComposerProjectTemplatesTest.php index 32c3e05de4f5..413b4d25cdf0 100644 --- a/core/tests/Drupal/BuildTests/Composer/Template/ComposerProjectTemplatesTest.php +++ b/core/tests/Drupal/BuildTests/Composer/Template/ComposerProjectTemplatesTest.php @@ -4,7 +4,7 @@ use Composer\Json\JsonFile; use Composer\Semver\VersionParser; -use Drupal\BuildTests\Framework\BuildTestBase; +use Drupal\BuildTests\Composer\ComposerBuildTestBase; use Drupal\Composer\Composer; /** @@ -25,7 +25,7 @@ * * @requires externalCommand composer */ -class ComposerProjectTemplatesTest extends BuildTestBase { +class ComposerProjectTemplatesTest extends ComposerBuildTestBase { /** * The minimum stability requirement for dependencies. @@ -297,26 +297,6 @@ public function testTemplateCreateProject($project, $package_dir, $docroot_dir) } } - /** - * Assert that the VERSION constant in Drupal.php is the expected value. - * - * @param string $expectedVersion - * The expected version. - * @param string $dir - * The path to the site root. - * - * @internal - */ - protected function assertDrupalVersion(string $expectedVersion, string $dir): void { - $drupal_php_path = $dir . '/core/lib/Drupal.php'; - $this->assertFileExists($drupal_php_path); - - // Read back the Drupal version that was set and assert it matches expectations. - $this->executeCommand("php -r 'include \"$drupal_php_path\"; print \Drupal::VERSION;'"); - $this->assertCommandSuccessful(); - $this->assertCommandOutputContains($expectedVersion); - } - /** * Creates a test package that points to the templates. * -- GitLab