diff --git a/composer/Template/LegacyProject/composer.json b/composer/Template/LegacyProject/composer.json index 995e12bbd060fc82c09660129550bf7943ab4982..c7f532dfe61c923ae76db82034ad34b87c25a8d3 100644 --- a/composer/Template/LegacyProject/composer.json +++ b/composer/Template/LegacyProject/composer.json @@ -16,13 +16,13 @@ ], "require": { "composer/installers": "^1.2", - "drupal/core-composer-scaffold": "^8.8", - "drupal/core-project-message": "^8.8", - "drupal/core-recommended": "^8.8", - "drupal/core-vendor-hardening": "^8.8" + "drupal/core-composer-scaffold": "^9", + "drupal/core-project-message": "^9", + "drupal/core-recommended": "^9", + "drupal/core-vendor-hardening": "^9" }, "require-dev": { - "drupal/core-dev": "^8.8" + "drupal/core-dev": "^9" }, "conflict": { "drupal/drupal": "*" diff --git a/composer/Template/RecommendedProject/composer.json b/composer/Template/RecommendedProject/composer.json index f6d2635af98c302e3281103de06020ebdd19adb9..56aa5cdb2ce49a695d37b629e9655eab64884394 100644 --- a/composer/Template/RecommendedProject/composer.json +++ b/composer/Template/RecommendedProject/composer.json @@ -16,12 +16,12 @@ ], "require": { "composer/installers": "^1.2", - "drupal/core-composer-scaffold": "^8.8", - "drupal/core-project-message": "^8.8", - "drupal/core-recommended": "^8.8" + "drupal/core-composer-scaffold": "^9", + "drupal/core-project-message": "^9", + "drupal/core-recommended": "^9" }, "require-dev": { - "drupal/core-dev": "^8.8" + "drupal/core-dev": "^9" }, "conflict": { "drupal/drupal": "*" diff --git a/core/drupalci.yml b/core/drupalci.yml index 9ce9b2b1c074230e930dce14f29580be0b616ce3..8cc5f1d4883efc069dc6a4db95809f679f7ac573 100644 --- a/core/drupalci.yml +++ b/core/drupalci.yml @@ -55,7 +55,3 @@ build: # Run nightwatch testing. # @see https://www.drupal.org/project/drupal/issues/2869825 nightwatchjs: -# container_command.drupal_project_templates: -# commands: -# - "sudo -u www-data ${SOURCE_DIR}/core/tests/scripts/test_composer_project_templates.sh" -# halt-on-fail: true diff --git a/core/tests/Drupal/BuildTests/Composer/Template/ComposerProjectTemplatesTest.php b/core/tests/Drupal/BuildTests/Composer/Template/ComposerProjectTemplatesTest.php new file mode 100644 index 0000000000000000000000000000000000000000..e9bf830116833f009c66d20a9ecd525d01fc0431 --- /dev/null +++ b/core/tests/Drupal/BuildTests/Composer/Template/ComposerProjectTemplatesTest.php @@ -0,0 +1,212 @@ +<?php + +namespace Drupal\BuildTests\Composer\Template; + +use Composer\Json\JsonFile; +use Drupal\BuildTests\Framework\BuildTestBase; +use Drupal\Composer\Composer; +use Symfony\Component\Finder\Finder; + +/** + * Demonstrate that Composer project templates are buildable as patched. + * + * We have to use the packages.json fixture so that Composer will use the + * in-codebase version of the project template. + * + * We also have to add path repositories to the in-codebase project template or + * else Composer will try to use packagist to resolve dependencies we'd prefer + * it to find locally. + * + * This is because Composer only uses the packages.json file to resolve the + * project template and not any other dependencies. + * + * @group #slow + * @group Template + * + * @requires externalCommand composer + */ +class ComposerProjectTemplatesTest extends BuildTestBase { + + /** + * Get Composer items that we want to be path repos, from within a directory. + * + * @param string $workspace_directory + * The full path to the workspace directory. + * @param string $subdir + * The subdirectory to search under composer/. + * + * @return string[] + * Array of paths, indexed by package name. + */ + public function getPathReposForType($workspace_directory, $subdir) { + // Find the Composer items that we want to be path repos. + $path_repos = Finder::create() + ->files() + ->name('composer.json') + ->in($workspace_directory . '/composer/' . $subdir); + + $data = []; + /* @var $path_repo \SplFileInfo */ + foreach ($path_repos as $path_repo) { + $json_file = new JsonFile($path_repo->getPathname()); + $json = $json_file->read(); + $data[$json['name']] = $path_repo->getPath(); + } + return $data; + } + + public function provideTemplateCreateProject() { + return [ + 'recommended-project' => [ + 'drupal/recommended-project', + 'composer/Template/RecommendedProject', + '/web', + ], + 'legacy-project' => [ + 'drupal/legacy-project', + 'composer/Template/LegacyProject', + '', + ], + ]; + } + + /** + * Make sure we've accounted for all the templates. + */ + public function testVerifyTemplateTestProviderIsAccurate() { + $root = $this->getDrupalRoot(); + $data = $this->provideTemplateCreateProject($root); + + // Find all the templates. + $template_files = Finder::create() + ->files() + ->name('composer.json') + ->in($root . '/composer/Template'); + + $this->assertEquals(count($template_files), count($data)); + + // We could have the same number of templates but different names. + $template_data = []; + foreach ($data as $data_name => $data_value) { + $template_data[$data_value[0]] = $data_name; + } + /* @var $file \SplFileInfo */ + foreach ($template_files as $file) { + $json_file = new JsonFile($file->getPathname()); + $json = $json_file->read(); + $this->assertArrayHasKey('name', $json); + // Assert that the template name is in the project created + // from the template. + $this->assertArrayHasKey($json['name'], $template_data); + } + } + + /** + * @dataProvider provideTemplateCreateProject + */ + public function testTemplateCreateProject($project, $package_dir, $docroot_dir) { + $this->copyCodebase(); + + // Get the Drupal core version branch. For instance, this should be + // 8.9.x-dev for the 8.9.x branch. + $core_version = Composer::drupalVersionBranch(); + + // Set up the template to use our path repos. Inclusion of metapackages is + // reported differently, so we load up a separate set for them. + $metapackage_path_repos = $this->getPathReposForType($this->getWorkspaceDirectory(), 'Metapackage'); + $path_repos = array_merge($metapackage_path_repos, $this->getPathReposForType($this->getWorkspaceDirectory(), 'Plugin')); + // Always add drupal/core as a path repo. + $path_repos['drupal/core'] = $this->getWorkspaceDirectory() . '/core'; + foreach ($path_repos as $name => $path) { + $this->executeCommand('composer config repositories.' . $name . ' path ' . $path, $package_dir); + $this->assertCommandSuccessful(); + } + + $repository_path = $this->getWorkspaceDirectory() . '/test_repository/packages.json'; + $this->makeTestPackage($repository_path, $core_version); + + $autoloader = $this->getWorkspaceDirectory() . '/testproject' . $docroot_dir . '/autoload.php'; + $this->assertFileNotExists($autoloader); + + $this->executeCommand("COMPOSER_CORE_VERSION=$core_version composer create-project $project testproject $core_version -s dev -vv --repository $repository_path"); + $this->assertCommandSuccessful(); + + // Ensure we used the project from our codebase. + $this->assertErrorOutputContains("Installing $project ($core_version): Symlinking from $package_dir"); + // Ensure that we used drupal/core from our codebase. This probably means + // that drupal/core-recommended was added successfully by the project. + $this->assertErrorOutputContains("Installing drupal/core ($core_version): Symlinking from"); + // Verify that there is an autoloader. + $this->assertFileExists($autoloader); + + // In order to verify that Composer used the path repos for our project, we + // have to get the requirements from the project composer.json so we can + // reconcile our expectations. + $template_json_file = $this->getWorkspaceDirectory() . '/' . $package_dir . '/composer.json'; + $this->assertFileExists($template_json_file); + $json_file = new JsonFile($template_json_file); + $template_json = $json_file->read(); + // Get the require and require-dev information, and ensure that our + // requirements are not erroneously empty. + $this->assertNotEmpty( + $require = array_merge($template_json['require'] ?? [], $template_json['require-dev'] ?? []) + ); + // Verify that path repo packages were installed. + $path_repos = array_keys($path_repos); + foreach (array_keys($require) as $package_name) { + if (in_array($package_name, $path_repos)) { + // Metapackages do not report that they were installed as symlinks, but + // we still must check that their installed version matches + // COMPOSER_CORE_VERSION. + if (array_key_exists($package_name, $metapackage_path_repos)) { + $this->assertErrorOutputContains("Installing $package_name ($core_version)"); + } + else { + $this->assertErrorOutputContains("Installing $package_name ($core_version): Symlinking from"); + } + } + } + } + + /** + * Creates a test package that points to the templates. + * + * @param string $repository_path + * The path where to create the test package. + * @param string $version + * The version under test. + */ + protected function makeTestPackage($repository_path, $version) { + $json = <<<JSON +{ + "packages": { + "drupal/recommended-project": { + "$version": { + "name": "drupal/recommended-project", + "dist": { + "type": "path", + "url": "composer/Template/RecommendedProject" + }, + "type": "project", + "version": "$version" + } + }, + "drupal/legacy-project": { + "$version": { + "name": "drupal/legacy-project", + "dist": { + "type": "path", + "url": "composer/Template/LegacyProject" + }, + "type": "project", + "version": "$version" + } + } + } +} +JSON; + mkdir(dirname($repository_path)); + file_put_contents($repository_path, $json); + } + +} diff --git a/core/tests/scripts/test_composer_project_templates.sh b/core/tests/scripts/test_composer_project_templates.sh deleted file mode 100755 index 620f6ad29dd39ab03ef4d0fbee9dcdd97a90e9ca..0000000000000000000000000000000000000000 --- a/core/tests/scripts/test_composer_project_templates.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -IFS=$'\n\t' - -# @todo: convert to a build test after #2984031 is in. - -#/ Usage: ./drupal_project_templates.sh -#/ Description: Container command to check default composer templates. -#/ Options: -#/ --help: Display this help message -usage() { grep '^#/' "$0" | cut -c4- ; exit 0 ; } -expr "$*" : ".*--help" > /dev/null && usage - -info() { echo "[INFO] $*" ; } -fatal() { echo "[FATAL] $*" ; exit 1 ; } - -assertScaffold() { - if [ -f $1/autoload.php ] - then - info "autoload.php file found." - else - fatal "No autoload.php file found." - fi - if [ -f $1/core/authorize.php ] - then - info "authorize.php file found." - else - fatal "No authorize.php file found." - fi -} - -info "Starting script" - -SOURCE_DIR=$(realpath $(dirname $0))/../../.. - -info "Installing recommended project composer template" -composer --working-dir="${SOURCE_DIR}/composer/Template/RecommendedProject" config repositories.scaffold path '../../Plugin/Scaffold' -composer --working-dir="${SOURCE_DIR}/composer/Template/RecommendedProject" install --no-suggest --no-progress --no-interaction -info "Recommended project composer template installed successfully." -assertScaffold "${SOURCE_DIR}/composer/Template/RecommendedProject/web" - -info "Installing legacy project composer template" -composer --working-dir="${SOURCE_DIR}/composer/Template/LegacyProject" config repositories.scaffold path '../../Plugin/Scaffold' -composer --working-dir="${SOURCE_DIR}/composer/Template/LegacyProject" install --no-suggest --no-progress --no-interaction -info "Legacy project composer template installed successfully." -assertScaffold "${SOURCE_DIR}/composer/Template/LegacyProject" - -info "Script complete!"