Commit 9c1bbba3 authored by catch's avatar catch

Issue #3072702 by alexpott, Wim Leers, mikelutz, xjm, catch, Berdir, tedbow,...

Issue #3072702 by alexpott, Wim Leers, mikelutz, xjm, catch, Berdir, tedbow, shaal, webchick, Mixologic, heddn: Core extensions should not need to specify the new core_version_requirement in *.info.yml files so that 9.0.x can be installed

(cherry picked from commit 3834d60f)
parent d8ce1e2d
...@@ -1629,6 +1629,7 @@ services: ...@@ -1629,6 +1629,7 @@ services:
arguments: ['@library.discovery', '@library.dependency_resolver', '@module_handler', '@theme.manager', '@language_manager', '@cache.data'] arguments: ['@library.discovery', '@library.dependency_resolver', '@module_handler', '@theme.manager', '@language_manager', '@cache.data']
info_parser: info_parser:
class: Drupal\Core\Extension\InfoParser class: Drupal\Core\Extension\InfoParser
arguments: ['@app.root']
twig: twig:
class: Drupal\Core\Template\TwigEnvironment class: Drupal\Core\Template\TwigEnvironment
arguments: ['@app.root', '@cache.default', '%twig_extension_hash%', '@state', '@twig.loader', '%twig.config%'] arguments: ['@app.root', '@cache.default', '%twig_extension_hash%', '@state', '@twig.loader', '%twig.config%']
......
...@@ -314,7 +314,7 @@ protected function getProfiles($include_hidden = FALSE, $auto_select_distributio ...@@ -314,7 +314,7 @@ protected function getProfiles($include_hidden = FALSE, $auto_select_distributio
$listing = new ExtensionDiscovery(getcwd(), FALSE); $listing = new ExtensionDiscovery(getcwd(), FALSE);
$listing->setProfileDirectories([]); $listing->setProfileDirectories([]);
$profiles = []; $profiles = [];
$info_parser = new InfoParserDynamic(); $info_parser = new InfoParserDynamic(getcwd());
foreach ($listing->scan('profile') as $profile) { foreach ($listing->scan('profile') as $profile) {
$details = $info_parser->parse($profile->getPathname()); $details = $info_parser->parse($profile->getPathname());
// Don't show hidden profiles. // Don't show hidden profiles.
......
...@@ -11,11 +11,33 @@ ...@@ -11,11 +11,33 @@
*/ */
class InfoParserDynamic implements InfoParserInterface { class InfoParserDynamic implements InfoParserInterface {
/**
* The root directory of the Drupal installation.
*
* @var string
*/
protected $root;
/** /**
* The earliest Drupal version that supports the 'core_version_requirement'. * The earliest Drupal version that supports the 'core_version_requirement'.
*/ */
const FIRST_CORE_VERSION_REQUIREMENT_SUPPORTED_VERSION = '8.7.7'; const FIRST_CORE_VERSION_REQUIREMENT_SUPPORTED_VERSION = '8.7.7';
/**
* InfoParserDynamic constructor.
*
* @param string|null $app_root
* The root directory of the Drupal installation.
*/
public function __construct(string $app_root = NULL) {
if ($app_root === NULL) {
// @todo https://www.drupal.org/project/drupal/issues/3087975 Require
// $app_root argument.
$app_root = \Drupal::hasService('app.root') ? (string) \Drupal::service('app.root') : DRUPAL_ROOT;
}
$this->root = $app_root;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
...@@ -34,13 +56,17 @@ public function parse($filename) { ...@@ -34,13 +56,17 @@ public function parse($filename) {
if (!empty($missing_keys)) { if (!empty($missing_keys)) {
throw new InfoParserException('Missing required keys (' . implode(', ', $missing_keys) . ') in ' . $filename); throw new InfoParserException('Missing required keys (' . implode(', ', $missing_keys) . ') in ' . $filename);
} }
if ($parsed_info['type'] === 'profile' && isset($parsed_info['core_version_requirement'])) {
// @todo Support the 'core_version_requirement' key in profiles in
// https://www.drupal.org/node/3070401.
throw new InfoParserException("The 'core_version_requirement' key is not supported in profiles in $filename");
}
if (!isset($parsed_info['core']) && !isset($parsed_info['core_version_requirement'])) { if (!isset($parsed_info['core']) && !isset($parsed_info['core_version_requirement'])) {
throw new InfoParserException("The 'core' or the 'core_version_requirement' key must be present in " . $filename); if (strpos($filename, 'core/') === 0 || strpos($filename, $this->root . '/core/') === 0) {
// Core extensions do not need to specify core compatibility: they are
// by definition compatible so a sensible default is used. Core
// modules are allowed to provide these for testing purposes.
$parsed_info['core_version_requirement'] = \Drupal::VERSION;
}
else {
// Non-core extensions must specify core compatibility.
throw new InfoParserException("The 'core' or the 'core_version_requirement' key must be present in " . $filename);
}
} }
if (isset($parsed_info['core']) && !preg_match("/^\d\.x$/", $parsed_info['core'])) { if (isset($parsed_info['core']) && !preg_match("/^\d\.x$/", $parsed_info['core'])) {
throw new InfoParserException("Invalid 'core' value \"{$parsed_info['core']}\" in " . $filename); throw new InfoParserException("Invalid 'core' value \"{$parsed_info['core']}\" in " . $filename);
......
...@@ -67,9 +67,7 @@ protected function setUp() { ...@@ -67,9 +67,7 @@ protected function setUp() {
$this->themeHandler = $this->container->get('theme_handler'); $this->themeHandler = $this->container->get('theme_handler');
$this->themeHandler->refreshInfo(); $this->themeHandler->refreshInfo();
$vfs_root = vfsStream::setup('root'); $vfs_root = vfsStream::setup('core');
$vfs_root->addChild(vfsStream::newDirectory($this->siteDirectory));
$site_dir = $vfs_root->getChild($this->siteDirectory);
vfsStream::create([ vfsStream::create([
'themes' => [ 'themes' => [
'test_stable' => [ 'test_stable' => [
...@@ -81,7 +79,7 @@ protected function setUp() { ...@@ -81,7 +79,7 @@ protected function setUp() {
'stable.theme' => file_get_contents(DRUPAL_ROOT . '/core/themes/stable/stable.theme'), 'stable.theme' => file_get_contents(DRUPAL_ROOT . '/core/themes/stable/stable.theme'),
], ],
], ],
], $site_dir); ], $vfs_root);
} }
/** /**
...@@ -116,8 +114,8 @@ class VfsThemeExtensionList extends ThemeExtensionList { ...@@ -116,8 +114,8 @@ class VfsThemeExtensionList extends ThemeExtensionList {
*/ */
public function __construct(string $root, string $type, CacheBackendInterface $cache, InfoParserInterface $info_parser, ModuleHandlerInterface $module_handler, StateInterface $state, ConfigFactoryInterface $config_factory, ThemeEngineExtensionList $engine_list, $install_profile) { public function __construct(string $root, string $type, CacheBackendInterface $cache, InfoParserInterface $info_parser, ModuleHandlerInterface $module_handler, StateInterface $state, ConfigFactoryInterface $config_factory, ThemeEngineExtensionList $engine_list, $install_profile) {
parent::__construct($root, $type, $cache, $info_parser, $module_handler, $state, $config_factory, $engine_list, $install_profile); parent::__construct($root, $type, $cache, $info_parser, $module_handler, $state, $config_factory, $engine_list, $install_profile);
$this->extensionDiscovery = new ExtensionDiscovery('vfs://root'); $this->extensionDiscovery = new ExtensionDiscovery('vfs://core');
$this->infoParser = new VfsInfoParser(); $this->infoParser = new VfsInfoParser('vfs:/');
} }
/** /**
...@@ -135,7 +133,7 @@ class VfsInfoParser extends InfoParser { ...@@ -135,7 +133,7 @@ class VfsInfoParser extends InfoParser {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function parse($filename) { public function parse($filename) {
return parent::parse("vfs://root/$filename"); return parent::parse("vfs://core/$filename");
} }
} }
...@@ -99,7 +99,7 @@ public function testUploadModule() { ...@@ -99,7 +99,7 @@ public function testUploadModule() {
// child site has access to, standard module API functions won't find it // child site has access to, standard module API functions won't find it
// when called here. To get the version, the info file must be parsed // when called here. To get the version, the info file must be parsed
// directly instead. // directly instead.
$info_parser = new InfoParserDynamic(); $info_parser = new InfoParserDynamic(DRUPAL_ROOT);
$info = $info_parser->parse($installedInfoFilePath); $info = $info_parser->parse($installedInfoFilePath);
$this->assertEqual($info['version'], '8.x-1.0'); $this->assertEqual($info['version'], '8.x-1.0');
......
...@@ -2,7 +2,9 @@ name: Testing config overrides ...@@ -2,7 +2,9 @@ name: Testing config overrides
type: profile type: profile
description: 'Minimal profile for running tests with config overrides in a profile.' description: 'Minimal profile for running tests with config overrides in a profile.'
version: VERSION version: VERSION
core: 8.x # This profile is copied to a non-core location by
# \Drupal\Tests\config\Functional\ConfigInstallProfileUnmetDependenciesTest.
core_version_requirement: '*'
hidden: true hidden: true
install: install:
- action - action
......
...@@ -29,7 +29,7 @@ protected function prepareEnvironment() { ...@@ -29,7 +29,7 @@ protected function prepareEnvironment() {
parent::prepareEnvironment(); parent::prepareEnvironment();
$this->info = [ $this->info = [
'type' => 'profile', 'type' => 'profile',
'core' => \Drupal::CORE_COMPATIBILITY, 'core_version_requirement' => '*',
'name' => 'Distribution profile', 'name' => 'Distribution profile',
'distribution' => [ 'distribution' => [
'name' => 'My Distribution', 'name' => 'My Distribution',
......
...@@ -22,7 +22,7 @@ protected function prepareEnvironment() { ...@@ -22,7 +22,7 @@ protected function prepareEnvironment() {
parent::prepareEnvironment(); parent::prepareEnvironment();
$this->info = [ $this->info = [
'type' => 'profile', 'type' => 'profile',
'core' => \Drupal::CORE_COMPATIBILITY, 'core_version_requirement' => '*',
'name' => 'Distribution profile', 'name' => 'Distribution profile',
'distribution' => [ 'distribution' => [
'name' => 'My Distribution', 'name' => 'My Distribution',
......
...@@ -32,7 +32,7 @@ protected function prepareEnvironment() { ...@@ -32,7 +32,7 @@ protected function prepareEnvironment() {
parent::prepareEnvironment(); parent::prepareEnvironment();
$this->info = [ $this->info = [
'type' => 'profile', 'type' => 'profile',
'core' => \Drupal::CORE_COMPATIBILITY, 'core_version_requirement' => '*',
'name' => 'Distribution profile', 'name' => 'Distribution profile',
'distribution' => [ 'distribution' => [
'name' => 'My Distribution', 'name' => 'My Distribution',
......
...@@ -30,9 +30,11 @@ class DistributionProfileTranslationTest extends InstallerTestBase { ...@@ -30,9 +30,11 @@ class DistributionProfileTranslationTest extends InstallerTestBase {
*/ */
protected function prepareEnvironment() { protected function prepareEnvironment() {
parent::prepareEnvironment(); parent::prepareEnvironment();
// We set core_version_requirement to '*' for the test so that it does not
// need to be updated between major versions.
$this->info = [ $this->info = [
'type' => 'profile', 'type' => 'profile',
'core' => \Drupal::CORE_COMPATIBILITY, 'core_version_requirement' => '*',
'name' => 'Distribution profile', 'name' => 'Distribution profile',
'distribution' => [ 'distribution' => [
'name' => 'My Distribution', 'name' => 'My Distribution',
......
...@@ -33,10 +33,11 @@ protected function prepareEnvironment() { ...@@ -33,10 +33,11 @@ protected function prepareEnvironment() {
$this->profile = $core_extension['profile']; $this->profile = $core_extension['profile'];
} }
// Create a profile for testing. // Create a profile for testing. We set core_version_requirement to '*' for
// the test so that it does not need to be updated between major versions.
$info = [ $info = [
'type' => 'profile', 'type' => 'profile',
'core' => \Drupal::CORE_COMPATIBILITY, 'core_version_requirement' => '*',
'name' => 'Configuration installation test profile (' . $this->profile . ')', 'name' => 'Configuration installation test profile (' . $this->profile . ')',
]; ];
......
...@@ -30,10 +30,12 @@ protected function prepareEnvironment() { ...@@ -30,10 +30,12 @@ protected function prepareEnvironment() {
parent::prepareEnvironment(); parent::prepareEnvironment();
// Create a self::PROFILE testing profile that depends on the 'language' // Create a self::PROFILE testing profile that depends on the 'language'
// module but not on 'locale' module. // module but not on 'locale' module. We set core_version_requirement to '*'
// for the test so that it does not need to be updated between major
// versions.
$profile_info = [ $profile_info = [
'type' => 'profile', 'type' => 'profile',
'core' => \Drupal::CORE_COMPATIBILITY, 'core_version_requirement' => '*',
'name' => 'Test with language but without locale', 'name' => 'Test with language but without locale',
'install' => ['language'], 'install' => ['language'],
]; ];
......
...@@ -27,7 +27,7 @@ protected function prepareEnvironment() { ...@@ -27,7 +27,7 @@ protected function prepareEnvironment() {
foreach (['distribution_one', 'distribution_two'] as $name) { foreach (['distribution_one', 'distribution_two'] as $name) {
$info = [ $info = [
'type' => 'profile', 'type' => 'profile',
'core' => \Drupal::CORE_COMPATIBILITY, 'core_version_requirement' => '*',
'name' => $name . ' profile', 'name' => $name . ' profile',
'distribution' => [ 'distribution' => [
'name' => $name, 'name' => $name,
......
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
use Drupal\Core\Extension\InfoParser; use Drupal\Core\Extension\InfoParser;
use Drupal\Core\Extension\InfoParserInterface; use Drupal\Core\Extension\InfoParserInterface;
use Drupal\Core\Extension\ThemeExtensionList; use Drupal\Core\Extension\ThemeExtensionList;
use Drupal\Core\Test\TestDatabase;
use Drupal\KernelTests\KernelTestBase; use Drupal\KernelTests\KernelTestBase;
use org\bovigo\vfs\vfsStream; use org\bovigo\vfs\vfsStream;
...@@ -63,10 +62,7 @@ public function register(ContainerBuilder $container) { ...@@ -63,10 +62,7 @@ public function register(ContainerBuilder $container) {
protected function setUpFilesystem() { protected function setUpFilesystem() {
parent::setUpFilesystem(); parent::setUpFilesystem();
$test_db = new TestDatabase($this->databasePrefix); $vfs_root = vfsStream::setup('core');
$test_site_path = $test_db->getTestSitePath();
$test_site_dir = $this->vfsRoot->getChild($test_site_path);
vfsStream::create([ vfsStream::create([
'themes' => [ 'themes' => [
'test_stable' => [ 'test_stable' => [
...@@ -78,17 +74,7 @@ protected function setUpFilesystem() { ...@@ -78,17 +74,7 @@ protected function setUpFilesystem() {
'stable.theme' => file_get_contents(DRUPAL_ROOT . '/core/themes/stable/stable.theme'), 'stable.theme' => file_get_contents(DRUPAL_ROOT . '/core/themes/stable/stable.theme'),
], ],
], ],
], $test_site_dir); ], $vfs_root);
// The origin site search directory used by the extension discovery service
// relies on the \Drupal::service('site.path') service to determine which
// directories to scan. It then prepends the root to each of those
// directories. But ::bootKernel() sets the origin site to
// $this->siteDirectory, which was in turns updated by ::setUpFileSystem()
// to include the virtual file system root. We must undo this if we want to
// use extension discovery on a virtual system.
// @see \Drupal\Core\Extension\ExtensionDiscovery::ORIGIN_SITE
$this->siteDirectory = $test_site_path;
} }
/** /**
...@@ -99,8 +85,8 @@ protected function setUpFilesystem() { ...@@ -99,8 +85,8 @@ protected function setUpFilesystem() {
*/ */
public function testStableIsDefault() { public function testStableIsDefault() {
$this->container->get('extension.list.theme') $this->container->get('extension.list.theme')
->setExtensionDiscovery(new ExtensionDiscovery('vfs://root')) ->setExtensionDiscovery(new ExtensionDiscovery('vfs://core'))
->setInfoParser(new VfsInfoParser()); ->setInfoParser(new VfsInfoParser('vfs:/'));
$this->themeInstaller->install(['test_stable']); $this->themeInstaller->install(['test_stable']);
$this->config('system.theme')->set('default', 'test_stable')->save(); $this->config('system.theme')->set('default', 'test_stable')->save();
...@@ -165,7 +151,7 @@ class VfsInfoParser extends InfoParser { ...@@ -165,7 +151,7 @@ class VfsInfoParser extends InfoParser {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function parse($filename) { public function parse($filename) {
return parent::parse("vfs://root/$filename"); return parent::parse("vfs://core/$filename");
} }
} }
...@@ -32,8 +32,8 @@ class InfoParserUnitTest extends UnitTestCase { ...@@ -32,8 +32,8 @@ class InfoParserUnitTest extends UnitTestCase {
*/ */
protected function setUp() { protected function setUp() {
parent::setUp(); parent::setUp();
// Use a fake DRUPAL_ROOT.
$this->infoParser = new InfoParser(); $this->infoParser = new InfoParser('vfs:/');
} }
/** /**
...@@ -349,7 +349,7 @@ public function testInfoParserMissingKey() { ...@@ -349,7 +349,7 @@ public function testInfoParserMissingKey() {
*/ */
public function testInfoParserCommonInfo() { public function testInfoParserCommonInfo() {
$common = <<<COMMONTEST $common = <<<COMMONTEST
core: 8.x core_version_requirement: '*'
name: common_test name: common_test
type: module type: module
description: 'testing info file parsing' description: 'testing info file parsing'
...@@ -371,12 +371,37 @@ public function testInfoParserCommonInfo() { ...@@ -371,12 +371,37 @@ public function testInfoParserCommonInfo() {
$this->assertEquals($info_values['simple_string'], 'A simple string', 'Simple string value was parsed correctly.'); $this->assertEquals($info_values['simple_string'], 'A simple string', 'Simple string value was parsed correctly.');
$this->assertEquals($info_values['version'], \Drupal::VERSION, 'Constant value was parsed correctly.'); $this->assertEquals($info_values['version'], \Drupal::VERSION, 'Constant value was parsed correctly.');
$this->assertEquals($info_values['double_colon'], 'dummyClassName::method', 'Value containing double-colon was parsed correctly.'); $this->assertEquals($info_values['double_colon'], 'dummyClassName::method', 'Value containing double-colon was parsed correctly.');
$this->assertSame('8.x', $info_values['core']);
$this->assertFalse(isset($info_values['core_version_requirement']));
$this->assertFalse($info_values['core_incompatible']); $this->assertFalse($info_values['core_incompatible']);
} }
} }
/**
* Tests common info file.
*
* @covers ::parse
*/
public function testInfoParserCoreInfo() {
$common = <<<CORETEST
name: core_test
type: module
version: "VERSION"
description: 'testing info file parsing'
CORETEST;
vfsStream::setup('core');
$filename = "core_test.info.txt";
vfsStream::create([
'fixtures' => [
$filename => $common,
],
]);
$info_values = $this->infoParser->parse(vfsStream::url("core/fixtures/$filename"));
$this->assertEquals($info_values['version'], \Drupal::VERSION, 'Constant value was parsed correctly.');
$this->assertFalse($info_values['core_incompatible']);
$this->assertEquals(\Drupal::VERSION, $info_values['core_version_requirement']);
}
/** /**
* @covers ::parse * @covers ::parse
* *
...@@ -439,12 +464,11 @@ public function providerCoreIncompatibility() { ...@@ -439,12 +464,11 @@ public function providerCoreIncompatibility() {
} }
/** /**
* Test a profile info file with the 'core_version_requirement' key. * Test a profile info file.
*/ */
public function testInvalidProfile() { public function testProfile() {
$profile = <<<PROFILE_TEST $profile = <<<PROFILE_TEST
core: 8.x core_version_requirement: '*'
core_version_requirement: ^8
name: The Perfect Profile name: The Perfect Profile
type: profile type: profile
description: 'This profile makes Drupal perfect. You should have no complaints.' description: 'This profile makes Drupal perfect. You should have no complaints.'
...@@ -456,9 +480,8 @@ public function testInvalidProfile() { ...@@ -456,9 +480,8 @@ public function testInvalidProfile() {
'invalid_profile.info.txt' => $profile, 'invalid_profile.info.txt' => $profile,
], ],
]); ]);
$this->expectException('\Drupal\Core\Extension\InfoParserException'); $info = $this->infoParser->parse(vfsStream::url('profiles/fixtures/invalid_profile.info.txt'));
$this->expectExceptionMessage("The 'core_version_requirement' key is not supported in profiles in vfs://profiles/fixtures/invalid_profile.info.txt"); $this->assertFalse($info['core_incompatible']);
$this->infoParser->parse(vfsStream::url('profiles/fixtures/invalid_profile.info.txt'));
} }
/** /**
......
...@@ -53,7 +53,7 @@ public function testRebuildThemeDataWithThemeParents() { ...@@ -53,7 +53,7 @@ public function testRebuildThemeDataWithThemeParents() {
$info_parser->parse(Argument::that($argument_condition)) $info_parser->parse(Argument::that($argument_condition))
->shouldBeCalled() ->shouldBeCalled()
->will(function ($file) use ($root) { ->will(function ($file) use ($root) {
$info_parser = new InfoParser(); $info_parser = new InfoParser($root);
return $info_parser->parse($root . '/' . $file[0]); return $info_parser->parse($root . '/' . $file[0]);
}); });
...@@ -124,7 +124,7 @@ public function testGetBaseThemes(array $themes, $theme, array $expected) { ...@@ -124,7 +124,7 @@ public function testGetBaseThemes(array $themes, $theme, array $expected) {
$state = new State(new KeyValueMemoryFactory(), new MemoryBackend(), new NullLockBackend()); $state = new State(new KeyValueMemoryFactory(), new MemoryBackend(), new NullLockBackend());
$config_factory = $this->getConfigFactoryStub([]); $config_factory = $this->getConfigFactoryStub([]);
$theme_engine_list = $this->prophesize(ThemeEngineExtensionList::class); $theme_engine_list = $this->prophesize(ThemeEngineExtensionList::class);
$theme_listing = new ThemeExtensionList($this->root, 'theme', new NullBackend('test'), new InfoParser(), $module_handler->reveal(), $state, $config_factory, $theme_engine_list->reveal(), 'test'); $theme_listing = new ThemeExtensionList($this->root, 'theme', new NullBackend('test'), new InfoParser($this->root), $module_handler->reveal(), $state, $config_factory, $theme_engine_list->reveal(), 'test');
$base_themes = $theme_listing->getBaseThemes($themes, $theme); $base_themes = $theme_listing->getBaseThemes($themes, $theme);
......
...@@ -314,13 +314,13 @@ class FunctionalExampleTest {} ...@@ -314,13 +314,13 @@ class FunctionalExampleTest {}
$test_profile_info = <<<EOF $test_profile_info = <<<EOF
name: Testing name: Testing
type: profile type: profile
core: 8.x core_version_requirement: '*'
EOF; EOF;
$test_module_info = <<<EOF $test_module_info = <<<EOF
name: Testing name: Testing
type: module type: module
core: 8.x core_version_requirement: '*'
EOF; EOF;
vfsStream::create([ vfsStream::create([
......
...@@ -37,13 +37,13 @@ protected function setupBasicModules() { ...@@ -37,13 +37,13 @@ protected function setupBasicModules() {
$info_a = <<<'EOS' $info_a = <<<'EOS'
type: module type: module
name: Module A name: Module A
core: 8.x core_version_requirement: '*'
EOS; EOS;
$info_b = <<<'EOS' $info_b = <<<'EOS'
type: module type: module
name: Module B name: Module B
core: 8.x core_version_requirement: '*'
EOS; EOS;
$module_a = <<<'EOS' $module_a = <<<'EOS'
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment