Commit 770237b6 authored by alexpott's avatar alexpott

Issue #3087626 by greg.1.anderson, Mixologic, jibran, Mile23, alexpott, xjm:...

Issue #3087626 by greg.1.anderson, Mixologic, jibran, Mile23, alexpott, xjm: Convert drupal/core-recommended & c. into a subtree split
parent 1bfa7c6d
......@@ -70,11 +70,15 @@
}
},
"scripts": {
"pre-install-cmd": "Drupal\\Core\\Composer\\Composer::ensureComposerVersion",
"pre-update-cmd": "Drupal\\Core\\Composer\\Composer::ensureComposerVersion",
"pre-install-cmd": "Drupal\\Composer\\Composer::ensureComposerVersion",
"pre-update-cmd": "Drupal\\Composer\\Composer::ensureComposerVersion",
"pre-autoload-dump": "Drupal\\Core\\Composer\\Composer::preAutoloadDump",
"drupal-phpunit-upgrade-check": "Drupal\\Core\\Composer\\Composer::upgradePHPUnit",
"drupal-phpunit-upgrade": "@composer update phpunit/phpunit symfony/phpunit-bridge phpspec/prophecy symfony/yaml --with-dependencies --no-progress",
"post-update-cmd": [
"Drupal\\Composer\\Composer::generateMetapackages",
"Drupal\\Composer\\Composer::ensureBehatDriverVersions"
],
"phpcs": "phpcs --standard=core/phpcs.xml.dist --runtime-set installed_paths $($COMPOSER_BINARY config vendor-dir)/drupal/coder/coder_sniffer --",
"phpcbf": "phpcbf --standard=core/phpcs.xml.dist --runtime-set installed_paths $($COMPOSER_BINARY config vendor-dir)/drupal/coder/coder_sniffer --"
},
......
......@@ -946,14 +946,14 @@
"authors": [
{
"name": "Nicholas Humfrey",
"role": "Developer",
"email": "njh@aelius.com",
"homepage": "http://www.aelius.com/njh/"
"homepage": "http://www.aelius.com/njh/",
"role": "Developer"
},
{
"name": "Alexey Zakhlestin",
"role": "Developer",
"email": "indeyets@gmail.com"
"email": "indeyets@gmail.com",
"role": "Developer"
}
],
"description": "EasyRdf is a PHP library designed to make it easy to consume and produce RDF.",
......@@ -3562,16 +3562,16 @@
},
{
"name": "behat/mink-selenium2-driver",
"version": "dev-master",
"version": "1.3.x-dev",
"source": {
"type": "git",
"url": "https://github.com/minkphp/MinkSelenium2Driver.git",
"reference": "8684ee4e634db7abda9039ea53545f86fc1e105a"
"reference": "0a09c4341621fca937a726827611b20ce3e2c259"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/minkphp/MinkSelenium2Driver/zipball/8684ee4e634db7abda9039ea53545f86fc1e105a",
"reference": "8684ee4e634db7abda9039ea53545f86fc1e105a",
"url": "https://api.github.com/repos/minkphp/MinkSelenium2Driver/zipball/0a09c4341621fca937a726827611b20ce3e2c259",
"reference": "0a09c4341621fca937a726827611b20ce3e2c259",
"shasum": ""
},
"require": {
......@@ -3598,15 +3598,15 @@
"MIT"
],
"authors": [
{
"name": "Konstantin Kudryashov",
"email": "ever.zet@gmail.com",
"homepage": "http://everzet.com"
},
{
"name": "Pete Otaqui",
"email": "pete@otaqui.com",
"homepage": "https://github.com/pete-otaqui"
},
{
"name": "Konstantin Kudryashov",
"email": "ever.zet@gmail.com",
"homepage": "http://everzet.com"
}
],
"description": "Selenium2 (WebDriver) driver for Mink framework",
......@@ -3619,7 +3619,7 @@
"testing",
"webdriver"
],
"time": "2018-10-10T12:39:06+00:00"
"time": "2019-09-02T09:46:54+00:00"
},
{
"name": "composer/ca-bundle",
......@@ -4420,18 +4420,18 @@
"authors": [
{
"name": "Arne Blankerts",
"role": "Developer",
"email": "arne@blankerts.de"
"email": "arne@blankerts.de",
"role": "Developer"
},
{
"name": "Sebastian Heuer",
"role": "Developer",
"email": "sebastian@phpeople.de"
"email": "sebastian@phpeople.de",
"role": "Developer"
},
{
"name": "Sebastian Bergmann",
"role": "Developer",
"email": "sebastian@phpunit.de"
"email": "sebastian@phpunit.de",
"role": "Developer"
}
],
"description": "Library for handling version information and constraints",
......@@ -4790,8 +4790,8 @@
"authors": [
{
"name": "Sebastian Bergmann",
"role": "lead",
"email": "sebastian@phpunit.de"
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "Simple template engine.",
......@@ -5594,8 +5594,8 @@
"authors": [
{
"name": "Sebastian Bergmann",
"role": "lead",
"email": "sebastian@phpunit.de"
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "Library that helps with managing the version number of Git-hosted PHP projects",
......
<?php
namespace Drupal\Composer;
use Composer\Composer as ComposerApp;
use Composer\Script\Event;
use Composer\Semver\Comparator;
use Drupal\Composer\Generator\PackageGenerator;
use Drupal\Composer\Generator\Util\DrupalCoreComposer;
/**
* Provides static functions for composer script events. See also
* core/lib/Drupal/Composer/Composer.php, which contains similar
* scripts needed by projects that include drupal/core. Scripts that
* are only needed by drupal/drupal go here.
*
* @see https://getcomposer.org/doc/articles/scripts.md
*/
class Composer {
/**
* Update metapackages whenever composer.lock is updated.
*
* @param \Composer\Script\Event $event
*/
public static function generateMetapackages(Event $event) {
$generator = new PackageGenerator();
$generator->generate($event->getIO(), getcwd());
}
/**
* Ensure that the minimum required version of Composer is running.
* Throw an exception if Composer is too old.
*/
public static function ensureComposerVersion() {
$composerVersion = method_exists(ComposerApp::class, 'getVersion') ?
ComposerApp::getVersion() : ComposerApp::VERSION;
if (Comparator::lessThan($composerVersion, '1.9.0')) {
throw new \RuntimeException("Drupal core development requires Composer 1.9.0, but Composer $composerVersion is installed. Please run 'composer self-update'.");
}
}
/**
* Ensure that the right version of behat/mink-selenium2-driver is locked.
* Throw an exception if we do not have 1.3.x-dev.
*
* @todo: Remove this once https://www.drupal.org/node/3078671 is fixed.
*/
public static function ensureBehatDriverVersions() {
$drupalCoreComposer = DrupalCoreComposer::createFromPath(getcwd());
$expectedVersion = '1.3.x-dev';
$behatMinkSelenium2DriverInfo = $drupalCoreComposer->packageLockInfo('behat/mink-selenium2-driver', TRUE);
if ($behatMinkSelenium2DriverInfo['version'] != $expectedVersion) {
$drupalVersion = static::drupalVersionBranch();
$message = <<< __EOT__
Drupal requires behat/mink-selenium2-driver:$expectedVersion in its composer.json
file, but it is pinned to {$behatMinkSelenium2DriverInfo['version']} in the composer.lock file.
This sometimes happens when Composer becomes confused. To fix:
1. `git checkout -- composer.lock`, or otherwise reset to a known-good lock file.
2. `rm -rf vendor`
3. `composer install`
4. `COMPOSER_ROOT_VERSION={$drupalVersion} composer update ...` (where ... is
the update arguments you wish to run, e.g. --lock).
__EOT__;
throw new \RuntimeException($message);
}
}
/**
* Return the branch name the current Drupal version is associated with.
*
* @return string
* A branch name, e.g. 8.9.x or 9.0.x.
*/
public static function drupalVersionBranch() {
return preg_replace('#\.[0-9]+-dev#', '.x-dev', \Drupal::VERSION);
}
}
<?php
namespace Drupal\Composer\Generator\Builder;
/**
* Builder to produce metapackage for drupal/core-recommended.
*/
class DrupalCoreRecommendedBuilder extends DrupalPackageBuilder {
/**
* {@inheritdoc}
*/
public function getPath() {
return 'CoreRecommended';
}
/**
* {@inheritdoc}
*/
public function getPackage() {
$composer = $this->initialPackageMetadata();
// Pull up the composer lock data.
$composerLockData = $this->drupalCoreInfo->composerLock();
if (!isset($composerLockData['packages'])) {
return $composer;
}
// Make a list of packages we do not want to put in the 'require' section.
$remove_list = ['drupal/core', 'wikimedia/composer-merge-plugin'];
// Copy the 'packages' section from the Composer lock into our 'require'
// section. There is also a 'packages-dev' section, but we do not need
// to pin 'require-dev' versions, as 'require-dev' dependencies are never
// included from subprojects. Use 'drupal/core-dev-dependencies' to get
// Drupal's dev dependencies.
foreach ($composerLockData['packages'] as $package) {
// If there is no 'source' record, then this is a path repository
// or something else that we do not want to include.
if (isset($package['source']) && !in_array($package['name'], $remove_list)) {
$composer['require'][$package['name']] = $package['version'];
}
}
return $composer;
}
/**
* Returns the initial package metadata that describes the metapackage.
*
* @return array
*/
protected function initialPackageMetadata() {
return [
"name" => "drupal/core-recommended",
"type" => "metapackage",
"description" => "Locked core dependencies; require this project INSTEAD OF drupal/core.",
"license" => "GPL-2.0-or-later",
"conflict" => [
"webflo/drupal-core-strict" => "*",
],
"require" => [
"drupal/core" => "self.version",
],
];
}
}
<?php
namespace Drupal\Composer\Generator\Builder;
/**
* Builder to produce metapackage for drupal/dev-dependencies.
*/
class DrupalDevDependenciesBuilder extends DrupalPackageBuilder {
/**
* {@inheritdoc}
*/
public function getPath() {
return 'DevDependencies';
}
/**
* {@inheritdoc}
*/
public function getPackage() {
$composer = $this->initialPackageMetadata();
// Put everything from Drupal's "require-dev" into our "require" section.
$composer['require'] = $this->drupalCoreInfo->getRequireDev();
// If the require-dev is bringing in a dev version of behat/mink, convert
// the requirement to a more flexible set of versions.
// @todo: remove when https://www.drupal.org/node/3078671 is fixed.
if (isset($composer['require']['behat/mink']) && ($composer['require']['behat/mink'] == '1.7.x-dev')) {
$composer['require']['behat/mink'] = '1.8.0 | 1.7.1.1 | 1.7.x-dev';
}
// Do the same sort of conversion for behat/mink-selenium2-driver.
if (isset($composer['require']['behat/mink-selenium2-driver']) && ($composer['require']['behat/mink-selenium2-driver'] == '1.3.x-dev')) {
$composer['require']['behat/mink-selenium2-driver'] = '1.4.0 | 1.3.1.1 | 1.3.x-dev';
}
// Sort our required packages by key.
ksort($composer['require']);
return $composer;
}
/**
* Returns the initial package metadata that describes the metapackage.
*
* @return array
*/
protected function initialPackageMetadata() {
return [
"name" => "drupal/dev-dependencies",
"type" => "metapackage",
"description" => "require-dev dependencies from drupal/drupal; use in addition to drupal/core-recommended to run tests from drupal/core.",
"license" => "GPL-2.0-or-later",
"conflict" => [
"webflo/drupal-core-require-dev" => "*",
],
];
}
}
<?php
namespace Drupal\Composer\Generator\Builder;
use Drupal\Composer\Generator\BuilderInterface;
use Drupal\Composer\Generator\Util\DrupalCoreComposer;
/**
* Base class that includes helpful utility routine for Drupal builder classes.
*/
abstract class DrupalPackageBuilder implements BuilderInterface {
/**
* Information about composer.json, composer.lock etc. in current release.
*
* @var \Drupal\Composer\Generator\Util\DrupalCoreComposer
*/
protected $drupalCoreInfo;
/**
* DrupalPackageBuilder constructor.
*
* @param \Drupal\Composer\Generator\Util\DrupalCoreComposer $drupalCoreInfo
* Information about composer.json and composer.lock from current release.
*/
public function __construct(DrupalCoreComposer $drupalCoreInfo) {
$this->drupalCoreInfo = $drupalCoreInfo;
}
}
<?php
namespace Drupal\Composer\Generator\Builder;
/**
* Builder to produce metapackage for drupal/pinned-dev-dependencies.
*/
class DrupalPinnedDevDependenciesBuilder extends DrupalPackageBuilder {
/**
* {@inheritdoc}
*/
public function getPath() {
return 'PinnedDevDependencies';
}
/**
* {@inheritdoc}
*/
public function getPackage() {
$composer = $this->initialPackageMetadata();
// Pull the exact versions of the dependencies from the composer.lock
// file and use it to build our 'require' section.
$composerLockData = $this->drupalCoreInfo->composerLock();
if (isset($composerLockData['packages-dev'])) {
foreach ($composerLockData['packages-dev'] as $package) {
$composer['require'][$package['name']] = $package['version'];
// If the require-dev is bringing in a dev version of behat/mink,
// convert the requirement to a more flexible set of versions.
// @todo: remove when https://www.drupal.org/node/3078671 is fixed.
if (($package['name'] == 'behat/mink') && (($package['version'] == 'dev-master') || ($package['version'] == '1.7.x-dev'))) {
$composer['require']['behat/mink'] = '1.8.0 | 1.7.1.1 | 1.7.x-dev';
}
// Do the same sort of conversion for behat/mink-selenium2-driver.
if (($package['name'] == 'behat/mink-selenium2-driver') && (($package['version'] == 'dev-master') || ($package['version'] == '1.3.x-dev'))) {
$composer['require']['behat/mink-selenium2-driver'] = '1.4.0 | 1.3.1.1 | 1.3.x-dev';
}
}
}
return $composer;
}
/**
* Returns the initial package metadata that describes the metapackage.
*
* @return array
*/
protected function initialPackageMetadata() {
return [
"name" => "drupal/pinned-dev-dependencies",
"type" => "metapackage",
"description" => "Pinned require-dev dependencies from drupal/drupal; use in addition to drupal/core-recommended to run tests from drupal/core.",
"license" => "GPL-2.0-or-later",
"conflict" => [
"webflo/drupal-core-require-dev" => "*",
],
"require" => [
"drupal/core" => "self.version",
],
];
}
}
<?php
namespace Drupal\Composer\Generator;
use Drupal\Composer\Generator\Util\DrupalCoreComposer;
/**
* Produce the output for a metapackage.
*
* BuilderInterface provides an interface for builder classes which are
* called by the PackageGenerator in order to produce a derived metapackage from
* the provided source package.
*
* See the README.txt file in composer/Metapackage for a description of what
* a metapackage is, and an explanation of the metapackages produced by the
* generator.
*/
interface BuilderInterface {
/**
* BuilderInterface constructor.
*
* @param \Drupal\Composer\Generator\Util\DrupalCoreComposer $drupalCoreInfo
* Information about the composer.json, composer.lock, and repository path.
*/
public function __construct(DrupalCoreComposer $drupalCoreInfo);
/**
* Return the path to where the metapackage should be written.
*
* @return string
* Path to the metapackage.
*/
public function getPath();
/**
* Generate the Composer.json data for the current tag or branch.
*
* @return array
* Composer json data.
*/
public function getPackage();
}
<?php
namespace Drupal\Composer\Generator;
use Drupal\Composer\Generator\Builder\DrupalCoreRecommendedBuilder;
use Drupal\Composer\Generator\Builder\DrupalDevDependenciesBuilder;
use Drupal\Composer\Generator\Builder\DrupalPinnedDevDependenciesBuilder;
use Drupal\Composer\Generator\Util\DrupalCoreComposer;
use Composer\Util\Filesystem;
use Composer\IO\IOInterface;
/**
* Generates metapackages.
*/
class PackageGenerator {
/**
* Base directory where generated projects are written.
*
* @var string
*/
protected $generatedProjectBaseDir;
/**
* PackageGenerator constructor.
*/
public function __construct() {
$this->generatedProjectBaseDir = dirname(__DIR__) . '/Metapackage';
}
/**
* Generate Drupal's metapackages whenever composer.lock is updated
*
* @param \Composer\IO\IOInterface $io
* Composer IO object for interacting with the user.
* @param string $base_dir
* Directory where drupal/drupal repository is located.
*/
public function generate(IOInterface $io, $base_dir) {
// General information from drupal/drupal and drupal/core composer.json
// and composer.lock files.
$drupalCoreInfo = DrupalCoreComposer::createFromPath($base_dir);
// Exit early if there is no composer.lock file.
if (empty($drupalCoreInfo->composerLock())) {
return;
}
// Run all of our available builders.
$builders = $this->builders();
$changed = FALSE;
foreach ($builders as $builder_class) {
$builder = new $builder_class($drupalCoreInfo);
$changed |= $this->generateMetapackage($io, $builder);
}
// 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.");
}
}
/**
* Returns a list of metapackage builders.
*
* @return BuilderInterface[]
*/
protected function builders() {
return [
DrupalCoreRecommendedBuilder::class,
DrupalDevDependenciesBuilder::class,
DrupalPinnedDevDependenciesBuilder::class,
];
}
/**
* Generate one metapackage.
*
* @param \Composer\IO\IOInterface $io
* Composer IO object for interacting with the user.
* @param BuilderInterface $builder
* An object that can build a metapackage.
*
* @return bool
* TRUE if the generated metapackage is different than what is on disk.
*/
protected function generateMetapackage(IOInterface $io, BuilderInterface $builder) {
// Load the existing composer.json file for drupal/core-recommended
$relative_path = $builder->getPath() . '/composer.json';
$composer_json_path = $this->generatedProjectBaseDir . '/' . $relative_path;
$original_composer_json = file_exists($composer_json_path) ? file_get_contents($composer_json_path) : '';
// Get the composer.json file from the builder.
$composer_json_data = $builder->getPackage();
$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 metapackage file has been updated..
$io->write("Updated metapackage file <info>composer/Metapackage/$relative_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;
}
/**
* Utility function to encode metapackage 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($composer_json_data) {
return json_encode($composer_json_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n";
}
}
HOW-TO: Test this Drupal composer script
In order to test this script, you'll need to get the entire Drupal repo and
run the tests there.
You'll find the tests under core/tests/Drupal/Tests/Composer/Generator.
You can get the full Drupal repo here:
https://www.drupal.org/project/drupal/git-instructions
You can find more information about running PHPUnit tests with Drupal here:
https://www.drupal.org/node/2116263
Each component in the Drupal\Composer\Plugin namespace has its own annotated test
group. You can use this group to run only the tests for this component. Like
this:
$ ./vendor/bin/phpunit -c core --group Metapackage
<?php
namespace Drupal\Composer\Generator\Util;
/**
* Utilities for accessing composer.json data from drupal/drupal and drupal/core.
*
* Some data is stored in the root composer.json file, while others is found
* in the core/composer.json file.
*/
class DrupalCoreComposer {
/**
* Cached composer.json data.
*
* @var array
*/
protected $composerJson = [];
/**
* Cached composer.lock data.
*
* @var array
*/
protected $composerLock = [];
/**
* DrupalCoreComposer constructor.
*
* @param array $composerJson
* The composer.json data.
* @param array $composerLock
* The composer.lock data.
*/
public function __construct(array $composerJson, array $composerLock) {
$this->composerJson = $composerJson;
$this->composerLock = $composerLock;
}
/**
* DrupalCoreComposer factory.
*
* @param string $repositoryPath
* Path to a directory containing a composer.json and composer.lock files.
*
* @return static
* New DrupalCoreComposer object containing composer.json and lock data.
*/
public static function createFromPath(string $repositoryPath) {
$composerJson = static::loadJsonFromPath("$repositoryPath/composer.json");
$composerLock = static::loadJsonFromPath("$repositoryPath/composer.lock");
return new self($composerJson, $composerLock);
}
/**
* Fetch the composer data from the root drupal/drupal project.
*
* @return array
* Composer json data
*/
public function rootComposerJson() {
return $this->composerJson;
}
/**
* Fetch the composer lock data.
*
* @return array
* Composer lock data
*/
public function composerLock() {
return $this->composerLock;
}
/**
* Return the "require-dev" section from root or core composer.json file.
*
* The require-dev constraints moved from core/composer.json (8.7.x and
* earlier) to the root composer.json file (8.8.x and later).
*
* @return array
* The contents of the "require-dev" section.
*/
public function getRequireDev() {
$composerJsonData = $this->rootComposerJson();
return isset($composerJsonData['require-dev']) ? $composerJsonData['require-dev'] : [];
}
/**
* Look up the info for one package in the composer.lock file.
*
* @param string $packageName
* Name of package to find, e.g. 'behat/mink-selenium2-driver'.
* @param bool $dev