Skip to content
Snippets Groups Projects
Verified Commit b4011fbf authored by Alex Pott's avatar Alex Pott
Browse files

Issue #3206226 by Wim Leers, mglaman, lauriii, alexpott: Make updating changes...

Issue #3206226 by Wim Leers, mglaman, lauriii, alexpott: Make updating changes from starterkit themes to generated themes easier

(cherry picked from commit 8d3c6fd3)
parent ab035217
Branches
Tags
42 merge requests!85673265330-fix-missing-hyphens: Create patch to MR and fix remaining words,!8394[warning] array_flip(): Can only flip STRING and INTEGER values, when saving a non-revisionable custom content entity,!7780issue 3443822: fix for 'No route found for the specified format html. Supported formats: json, xml.',!7416Simplify the HTML of field.html.twig,!7150Revert "Issue #3137119 by munish.kumar, johnwebdev, Jaypan, jungle, xjm,...,!6445Issue #3034692: Renamed the getHandler function which return the configuration of a handler instance on given display,!5013Issue #3071143: Table Render Array Example Is Incorrect,!4848Issue #1566662: Update module should send notifications on Thursdays,!4792Issue #2230689: Remove redundant "Italic" style,!4782Issue #2662898: "Links" field not displaying on custom view modes,!4220Issue #3368223: Link field > Access to internal links is not checked on display.,!4173Issue #2123543: Add string context and location filters to the translate interface,!3884Issue #3356842,!3870Issue #3087868,!3812Draft: Issue #3339373 by alexpott, andypost, mondrake:...,!3736Issue #3294005: Refactor Claro's form--password-confirm stylesheet,!3686Issue #3219967 against 9.5.x,!3683Issue #2939397: Clearing AliasManager cache with root path raises warning,!3543Issue #3344259: Allow ajax dialog to have focus configurable,!3437Issue #3106205: Length of menu_tree.url and menu_tree.route_param_key are too short (255 characters),!3356Issue #3209129: Scrolling problems when adding a block via layout builder,!2982Issue #3301562: Translate the default settings for this plugin (TimestampAgoFormatter),!2921Issue #1383696: Allow a custom HTML element to be selected for a grouping field,!2920Issue #3260175: Saving media entity without an owner crashes,!2857Issue #3314541: Remove unnecessary fill from SVG icon for the "Media Library" CKEditor 5 button — enabling dark mode support in contrib,!2841Resolve #3296811 "Resourceresponsetrait needs a",!2803Issue #3041402: Add option absolute url in formatter URL to image,!2733Issue #3293855: Update the outdated user_help text for user.admin_permissions and the description of the select box on the role settings page,!2527Issue #3298714: Undefined #options and Count Warning in Radios.php,!2447Issue #3293135: shouldUpdateThumbnail does not update thumbnail is source field changed,!2428Issue #3032078: Multiple webheads can cause infinite growth of Twig cache,!2280Issue #3280415: Metapackage Generator Breaks Under Composer --no-dev,!2205Quote all names in the regions section.,!2050Issue #3272969: Remove UnqiueField constraint.,!1956Issue #3268872: hook_views_invalidate_cache not called when a view is deleted,!1893Issue #3217260: Add a way to make media captions not editable in CKEditor,!1690fixing include_source documentation at SubProcess.php,!1520Issue #2815221: Add ability to use Quick Edit to the latest_revision route,!1459Issue #3087632: menu_name max length is too long,!878Issue #3221534: throw an exception when IDs passed to loadMultiple() are badly formed,!866Issue #2845319: The highlighting of the 'Home' menu-link does not respect query strings and fragment identifiers,!204Issue #3040556: It is not possible to react to an entity being duplicated
......@@ -3,6 +3,7 @@
namespace Drupal\Core\Command;
use Composer\Autoload\ClassLoader;
use Composer\Semver\VersionParser;
use Drupal\Component\Serialization\Yaml;
use Drupal\Core\Extension\Extension;
use Drupal\Core\Extension\ExtensionDiscovery;
......@@ -14,7 +15,9 @@
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Process\Process;
use Twig\Util\TemplateDirIterator;
/**
......@@ -128,6 +131,43 @@ protected function execute(InputInterface $input, OutputInterface $output) {
$info['core_version_requirement'] = '^' . $this->getVersion();
if (!array_key_exists('version', $info)) {
$confirm_versionless_source_theme = new ConfirmationQuestion(sprintf('The source theme %s does not have a version specified. This makes tracking changes in the source theme difficult. Are you sure you want to continue?', $source_theme->getName()));
if (!$io->askQuestion($confirm_versionless_source_theme)) {
return 0;
}
}
$source_version = $info['version'] ?? 'unknown-version';
if ($source_version === 'VERSION') {
$source_version = \Drupal::VERSION;
}
// A version in the generator string like "9.4.0-dev" is not very helpful.
// When this occurs, generate a version string that points to a commit.
if (VersionParser::parseStability($source_version) === 'dev') {
$git_check = Process::fromShellCommandline('git --help');
$git_check->run();
if ($git_check->getExitCode()) {
$io->error(sprintf('The source theme %s has a development version number (%s). Determining a specific commit is not possible because git is not installed. Either install git or use a tagged release to generate a theme.', $source_theme->getName(), $source_version));
return 1;
}
// Get the git commit for the source theme.
$git_get_commit = Process::fromShellCommandline("git rev-list --max-count=1 --abbrev-commit HEAD -C $source");
$git_get_commit->run();
if ($git_get_commit->getOutput() === '') {
$confirm_packaged_dev_release = new ConfirmationQuestion(sprintf('The source theme %s has a development version number (%s). Because it is not a git checkout, a specific commit could not be identified. This makes tracking changes in the source theme difficult. Are you sure you want to continue?', $source_theme->getName(), $source_version));
if (!$io->askQuestion($confirm_packaged_dev_release)) {
return 0;
}
$source_version .= '#unknown-commit';
}
else {
$source_version .= '#' . trim($git_get_commit->getOutput());
}
}
$info['generator'] = "$source_theme_name:$source_version";
if ($description = $input->getOption('description')) {
$info['description'] = $description;
}
......
......@@ -40,9 +40,12 @@ public function setUp(): void {
}
/**
* Tests the generate-theme command.
* Generates PHP process to generate a theme from core's starterkit theme.
*
* @return \Symfony\Component\Process\Process
* The PHP process
*/
public function test() {
private function generateThemeFromStarterkit() : Process {
$install_command = [
$this->php,
'core/scripts/drupal',
......@@ -53,15 +56,48 @@ public function test() {
];
$process = new Process($install_command, NULL);
$process->setTimeout(60);
return $process;
}
/**
* Asserts the theme exists. Returns the parsed *.info.yml file.
*
* @param string $theme_path_relative
* The core-relative path to the theme.
*
* @return array
* The parsed *.info.yml file.
*/
private function assertThemeExists(string $theme_path_relative): array {
$theme_path_absolute = $this->getWorkspaceDirectory() . "/$theme_path_relative";
$theme_name = basename($theme_path_relative);
$info_yml_filename = "$theme_name.info.yml";
$this->assertFileExists($theme_path_absolute . '/' . $info_yml_filename);
$info = Yaml::decode(file_get_contents($theme_path_absolute . '/' . $info_yml_filename));
return $info;
}
/**
* Tests the generate-theme command.
*/
public function test() {
// Do not rely on \Drupal::VERSION: change the version to a concrete version
// number, to simulate using a tagged core release.
$starterkit_info_yml = $this->getWorkspaceDirectory() . '/core/themes/starterkit_theme/starterkit_theme.info.yml';
$info = Yaml::decode(file_get_contents($starterkit_info_yml));
$info['version'] = '9.4.0';
file_put_contents($starterkit_info_yml, Yaml::encode($info));
$process = $this->generateThemeFromStarterkit();
$result = $process->run();
$this->assertEquals('Theme generated successfully to themes/test_custom_theme', trim($process->getOutput()), $process->getErrorOutput());
$this->assertSame(0, $result);
$theme_path_relative = 'themes/test_custom_theme';
$theme_path_absolute = $this->getWorkspaceDirectory() . "/$theme_path_relative";
$this->assertFileExists($theme_path_absolute . '/test_custom_theme.info.yml');
$info = Yaml::decode(file_get_contents($theme_path_absolute . '/test_custom_theme.info.yml'));
$info = $this->assertThemeExists($theme_path_relative);
self::assertArrayNotHasKey('hidden', $info);
self::assertArrayHasKey('generator', $info);
self::assertEquals('starterkit_theme:9.4.0', $info['generator']);
// Ensure that the generated theme can be installed.
$this->installQuickStart('minimal');
......@@ -72,10 +108,12 @@ public function test() {
$this->getMink()->getSession()->getPage()->clickLink('Install "Test custom starterkit theme" theme');
$this->getMink()->assertSession()->pageTextContains('The "Test custom starterkit theme" theme has been installed.');
// Ensure that a new theme cannot be generated when the destination
// directory already exists.
$theme_path_absolute = $this->getWorkspaceDirectory() . "/$theme_path_relative";
$this->assertFileExists($theme_path_absolute . '/test_custom_theme.theme');
unlink($theme_path_absolute . '/test_custom_theme.theme');
$process = new Process($install_command, NULL);
$process->setTimeout(60);
$process = $this->generateThemeFromStarterkit();
$result = $process->run();
$this->assertStringContainsString('Theme could not be generated because the destination directory', $process->getErrorOutput());
$this->assertStringContainsString($theme_path_relative, $process->getErrorOutput());
......@@ -83,6 +121,145 @@ public function test() {
$this->assertFileDoesNotExist($theme_path_absolute . '/test_custom_theme.theme');
}
/**
* Tests the generate-theme command on a dev snapshot of Drupal core.
*/
public function testDevSnapshot() {
// Do not rely on \Drupal::VERSION: change the version to a development
// snapshot version number, to simulate using a branch snapshot of core.
$starterkit_info_yml = $this->getWorkspaceDirectory() . '/core/themes/starterkit_theme/starterkit_theme.info.yml';
$info = Yaml::decode(file_get_contents($starterkit_info_yml));
$info['version'] = '9.4.0-dev';
file_put_contents($starterkit_info_yml, Yaml::encode($info));
$process = $this->generateThemeFromStarterkit();
$result = $process->run();
$this->assertEquals('Theme generated successfully to themes/test_custom_theme', trim($process->getOutput()), $process->getErrorOutput());
$this->assertSame(0, $result);
$theme_path_relative = 'themes/test_custom_theme';
$info = $this->assertThemeExists($theme_path_relative);
self::assertArrayNotHasKey('hidden', $info);
self::assertArrayHasKey('generator', $info);
self::assertMatchesRegularExpression('/^starterkit_theme\:9.4.0-dev#[0-9a-f]+$/', $info['generator']);
}
/**
* Tests the generate-theme command on a theme with a release version number.
*/
public function testContribStarterkit(): void {
// Change the version to a concrete version number, to simulate using a
// contrib theme as the starterkit.
$starterkit_info_yml = $this->getWorkspaceDirectory() . '/core/themes/starterkit_theme/starterkit_theme.info.yml';
$info = Yaml::decode(file_get_contents($starterkit_info_yml));
$info['version'] = '1.20';
file_put_contents($starterkit_info_yml, Yaml::encode($info));
$process = $this->generateThemeFromStarterkit();
$result = $process->run();
$this->assertEquals('Theme generated successfully to themes/test_custom_theme', trim($process->getOutput()), $process->getErrorOutput());
$this->assertSame(0, $result);
$info = $this->assertThemeExists('themes/test_custom_theme');
self::assertArrayNotHasKey('hidden', $info);
self::assertArrayHasKey('generator', $info);
self::assertEquals('starterkit_theme:1.20', $info['generator']);
}
/**
* Tests the generate-theme command on a theme with a dev version number.
*/
public function testContribStarterkitDevSnapshot(): void {
// Change the version to a development snapshot version number, to simulate
// using a contrib theme as the starterkit.
$starterkit_info_yml = $this->getWorkspaceDirectory() . '/core/themes/starterkit_theme/starterkit_theme.info.yml';
$info = Yaml::decode(file_get_contents($starterkit_info_yml));
$info['core_version_requirement'] = '*';
$info['version'] = '7.x-dev';
file_put_contents($starterkit_info_yml, Yaml::encode($info));
// Avoid the core git commit from being considered the source theme's: move
// it out of core.
Process::fromShellCommandline('mv core/themes/starterkit_theme themes/', $this->getWorkspaceDirectory())->run();
$process = $this->generateThemeFromStarterkit();
$result = $process->run();
$this->assertEquals("The source theme starterkit_theme has a development version number (7.x-dev). Because it is not a git checkout, a specific commit could not be identified. This makes tracking changes in the source theme difficult. Are you sure you want to continue? (yes/no) [yes]:\n > Theme generated successfully to themes/test_custom_theme", trim($process->getOutput()), $process->getErrorOutput());
$this->assertSame(0, $result);
$info = $this->assertThemeExists('themes/test_custom_theme');
self::assertArrayNotHasKey('hidden', $info);
self::assertArrayHasKey('generator', $info);
self::assertEquals('starterkit_theme:7.x-dev#unknown-commit', $info['generator']);
}
/**
* Tests the generate-theme command on a theme with a dev version without git.
*/
public function testContribStarterkitDevSnapshotWithGitNotInstalled(): void {
// Change the version to a development snapshot version number, to simulate
// using a contrib theme as the starterkit.
$starterkit_info_yml = $this->getWorkspaceDirectory() . '/core/themes/starterkit_theme/starterkit_theme.info.yml';
$info = Yaml::decode(file_get_contents($starterkit_info_yml));
$info['core_version_requirement'] = '*';
$info['version'] = '7.x-dev';
file_put_contents($starterkit_info_yml, Yaml::encode($info));
// Avoid the core git commit from being considered the source theme's: move
// it out of core.
Process::fromShellCommandline('mv core/themes/starterkit_theme themes/', $this->getWorkspaceDirectory())->run();
// Confirm that 'git' is available.
$output = [];
exec('git --help', $output, $status);
$this->assertEquals(0, $status);
// Modify our $PATH so that it begins with a path that contains an
// executable script named 'git' that always exits with 127, as if git were
// not found. Note that we run our tests using process isolation, so we do
// not need to restore the PATH when we are done.
$unavailableGitPath = $this->getWorkspaceDirectory() . '/bin';
mkdir($unavailableGitPath);
$bash = <<<SH
#!/bin/bash
exit 127
SH;
file_put_contents($unavailableGitPath . '/git', $bash);
chmod($unavailableGitPath . '/git', 0755);
$oldPath = getenv('PATH');
putenv('PATH=' . $unavailableGitPath . ':' . getenv('PATH'));
// Confirm that 'git' is no longer available.
$output = [];
exec('git --help', $output, $status);
$this->assertEquals(127, $status);
$process = $this->generateThemeFromStarterkit();
$result = $process->run();
$this->assertEquals("[ERROR] The source theme starterkit_theme has a development version number \n (7.x-dev). Determining a specific commit is not possible because git is\n not installed. Either install git or use a tagged release to generate a\n theme.", trim($process->getOutput()), $process->getErrorOutput());
$this->assertSame(1, $result);
$this->assertFileDoesNotExist($this->getWorkspaceDirectory() . "/themes/test_custom_theme");
putenv('PATH=' . $oldPath . ':' . getenv('PATH'));
}
/**
* Tests the generate-theme command on a theme without a version number.
*/
public function testCustomStarterkit(): void {
// Omit the version, to simulate using a custom theme as the starterkit.
$starterkit_info_yml = $this->getWorkspaceDirectory() . '/core/themes/starterkit_theme/starterkit_theme.info.yml';
$info = Yaml::decode(file_get_contents($starterkit_info_yml));
unset($info['version']);
file_put_contents($starterkit_info_yml, Yaml::encode($info));
$process = $this->generateThemeFromStarterkit();
$result = $process->run();
$this->assertEquals("The source theme starterkit_theme does not have a version specified. This makes tracking changes in the source theme difficult. Are you sure you want to continue? (yes/no) [yes]:\n > Theme generated successfully to themes/test_custom_theme", trim($process->getOutput()), $process->getErrorOutput());
$this->assertSame(0, $result);
$info = $this->assertThemeExists('themes/test_custom_theme');
self::assertArrayNotHasKey('hidden', $info);
self::assertArrayHasKey('generator', $info);
self::assertEquals('starterkit_theme:unknown-version', $info['generator']);
}
/**
* Tests themes that do not exist return an error.
*/
......
......@@ -3,6 +3,7 @@ type: theme
'base theme': stable9
hidden: true
starterkit: true
version: VERSION
libraries:
- starterkit_theme/base
- starterkit_theme/messages
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment