diff --git a/action.yml b/action.yml index 6bcff61..e366e4e 100644 --- a/action.yml +++ b/action.yml @@ -136,6 +136,23 @@ runs: echo "cat __inputs.yml" cat __inputs.yml + - name: Fetch branches + id: fetch-branches + shell: bash + run: | + # Gets all branches from GitHub API + # https://docs.github.com/en/rest/branches/branches?apiVersion=2022-11-28#list-branches + RESP_CODE=$(curl -w %{http_code} -s -o __installer_branches.json \ + -X GET "https://api.github.com/repos/silverstripe/silverstripe-installer/branches?per_page=100" \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${{ github.token }}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + ) + if [[ $RESP_CODE != "200" ]]; then + echo "Unable to read list of branches - HTTP response code was $RESP_CODE" + exit 1 + fi + - name: Run php script id: php-script shell: bash @@ -143,3 +160,14 @@ runs: MATRIX_JSON=$(php ${{ github.action_path }}/action.php) echo "MATRIX_JSON: $MATRIX_JSON" echo "matrix=${MATRIX_JSON}" >> "$GITHUB_OUTPUT" + + - name: Delete temporary files + shell: bash + if: always() + run: | + if [[ -f __installer_branches.json ]]; then + rm __installer_branches.json + fi + if [[ -f __inputs.yml ]]; then + rm __inputs.yml + fi diff --git a/consts.php b/consts.php index 3ba697c..33e4306 100644 --- a/consts.php +++ b/consts.php @@ -41,6 +41,11 @@ '8.2', '8.3', ], + '6' => [ + '8.1', + '8.2', + '8.3', + ], ]; const DB_MYSQL_57 = 'mysql57'; @@ -162,6 +167,45 @@ 'silverstripe-versioned-admin' => '2', 'vendor-plugin' => '2', ], + '6' => [ + 'MinkFacebookWebDriver' => '3', + 'recipe-authoring-tools' => '3', + 'recipe-blog' => '3', + 'recipe-ccl' => '4', + 'recipe-cms' => '6', + 'recipe-collaboration' => '3', + 'recipe-content-blocks' => '4', + 'recipe-core' => '6', + 'recipe-form-building' => '3', + 'recipe-kitchen-sink' => '6', + 'recipe-plugin' => '3', + 'recipe-reporting-tools' => '3', + 'recipe-services' => '3', + 'recipe-testing' => '4', + 'silverstripe-installer' => '6', + 'silverstripe-admin' => '3', + 'silverstripe-asset-admin' => '3', + 'silverstripe-assets' => '3', + 'silverstripe-behat-extension' => '6', + 'silverstripe-campaign-admin' => '3', + 'silverstripe-cms' => '6', + 'silverstripe-errorpage' => '3', + 'silverstripe-event-dispatcher' => '2', + 'silverstripe-framework' => '6', + 'silverstripe-frameworktest' => '2', + 'silverstripe-graphql' => '6', + 'silverstripe-login-forms' => '6', + 'silverstripe-mimevalidator' => '4', + 'silverstripe-registry' => '4', + 'silverstripe-reports' => '6', + 'silverstripe-serve' => '4', + 'silverstripe-session-manager' => '3', + 'silverstripe-siteconfig' => '6', + 'silverstripe-testsession' => '4', + 'silverstripe-versioned' => '3', + 'silverstripe-versioned-admin' => '3', + 'vendor-plugin' => '3', + ], ]; // use hardcoded.php to bulk update update this after creating a .cow.pat.json diff --git a/job_creator.php b/job_creator.php index 5cbfbb0..3b36913 100644 --- a/job_creator.php +++ b/job_creator.php @@ -21,7 +21,10 @@ class JobCreator /** * Get the correct version of silverstripe/installer to include for the given repository and branch */ - public function getInstallerVersion(): string + public function getInstallerVersion( + // the following is only used for unit testing + string $installerBranchesJson = '' + ): string { $this->repoName = explode('/', $this->githubRepository)[1]; // repo should not use installer @@ -93,18 +96,44 @@ public function getInstallerVersion(): string // fallback to use the next-minor or latest-minor version of installer $installerVersions = array_keys(INSTALLER_TO_PHP_VERSIONS); $installerVersions = array_filter($installerVersions, fn($version) => substr($version, 0, 1) === $cmsMajor); + if (preg_match('#^[1-9]+[0-9]*$#', $branch)) { // next-minor e.g. 4 return $cmsMajor . '.x-dev'; } else { // current-minor e.g. 4.11 // remove major versions - $installerVersions = array_diff($installerVersions, ['4', '5', '6']); + $installerVersions = array_diff($installerVersions, ['4', '5', '6', '7', '8', '9']); // get the minor portions of the verisons e.g. [9, 10, 11] $minorPortions = array_map(fn($portions) => (int) explode('.', $portions)[1], $installerVersions); + if (count($minorPortions) === 0) { + return $cmsMajor . '.x-dev'; + } sort($minorPortions); $minorPortion = $minorPortions[count($minorPortions) - 1]; $installerVersion = $cmsMajor . '.' . $minorPortion; + + // It's normal for new major versions branches to exist a year or more before the first release + // The corresponding minor version branch will not exist at this time + // Check that the minor version of the installer branches exists, if not, fallback to using the major + if ($installerBranchesJson) { + // this if for unit testing + $json = json_decode($installerBranchesJson); + } else { + // this file is created in action.yml + if (!file_exists('__installer_branches.json')) { + throw new Exception('__installer_branches.json was not found'); + } + $json = json_decode(file_get_contents('__installer_branches.json')); + } + $branches = array_column($json, 'name'); + // using array_filter() instead of in_array() to ensure we get a strict equality check + // e.g. '6' and '6.0' are not equal + $branchExists = count(array_filter($branches, fn($branch) => $branch === $installerVersion)); + if (!$branchExists) { + return $cmsMajor . '.x-dev'; + } + if ($isReleaseBranch) { return 'dev-' . $installerVersion . '-release'; } @@ -394,7 +423,7 @@ private function createPhpunitJobs( 'phpunit' => true, 'phpunit_suite' => $suite, ]); - } elseif ($this->getCmsMajor() === '5') { + } else { // phpunit tests for cms 5 are run on php 8.1, 8.2 or 8.3 and mysql 8.0 or mariadb $phpToDB = $this->generatePhpToDBMap(); foreach ($phpToDB as $php => $db) { diff --git a/tests/JobCreatorTest.php b/tests/JobCreatorTest.php index f1c9a50..e660dc1 100644 --- a/tests/JobCreatorTest.php +++ b/tests/JobCreatorTest.php @@ -62,6 +62,9 @@ public function provideCreateJob(): array ['myaccount/silverstripe-installer', '5', 99, [], [ 'php' => max(INSTALLER_TO_PHP_VERSIONS['5']) ]], + ['myaccount/silverstripe-installer', '6', 99, [], [ + 'php' => max(INSTALLER_TO_PHP_VERSIONS['6']) + ]], ]; } @@ -71,14 +74,48 @@ public function provideCreateJob(): array public function testGetInstallerVersion( string $githubRepository, string $branch, - string $expected + string $expected, + array $customInstallerBranches = [], + array $customComposerDeps = [] ): void { - $creator = new JobCreator(); - $creator->githubRepository = $githubRepository; - $creator->repoName = explode('/', $githubRepository)[1]; - $creator->branch = $branch; - $actual = $creator->getInstallerVersion(); - $this->assertSame($expected, $actual); + try { + $creator = new JobCreator(); + $creator->githubRepository = $githubRepository; + $creator->repoName = explode('/', $githubRepository)[1]; + $creator->branch = $branch; + $installerBranchesJson = json_encode($this->getInstallerBranchesJson()); + if ($customInstallerBranches) { + $installerBranchesJson = json_encode($customInstallerBranches); + } + if ($customComposerDeps) { + $this->writeComposerJson($customComposerDeps, 'silverstripe-module'); + $creator->composerJsonPath = '__composer.json'; + } + $actual = $creator->getInstallerVersion($installerBranchesJson); + $this->assertSame($expected, $actual); + } finally { + if (file_exists('__composer.json')) { + unlink('__composer.json'); + } + } + } + + private function getInstallerBranchesJson(): array + { + return [ + ['name' => '4'], + ['name' => '4-release'], + ['name' => '4.10'], + ['name' => '4.10-release'], + ['name' => '4.11'], + ['name' => '4.12'], + ['name' => '4.13'], + ['name' => '5'], + ['name' => '5.1'], + ['name' => '5.2'], + ['name' => '6'], + ['name' => '6.0'], + ]; } private function getCurrentMinorInstallerVersion(string $cmsMajor): string @@ -92,9 +129,9 @@ private function getCurrentMinorInstallerVersion(string $cmsMajor): string public function provideGetInstallerVersion(): array { - $nextMinor = '4.x-dev'; - $nextMinorRelease = 'dev-' . $this->getCurrentMinorInstallerVersion('4') . '-release'; - $currentMinor = $this->getCurrentMinorInstallerVersion('4') . '.x-dev'; + $nextMinorCms4 = '4.x-dev'; + $nextMinorCms4Release = 'dev-' . $this->getCurrentMinorInstallerVersion('4') . '-release'; + $currentMinorCms4 = $this->getCurrentMinorInstallerVersion('4') . '.x-dev'; return [ // no-installer repo ['myaccount/recipe-cms', '4', ''], @@ -105,50 +142,62 @@ public function provideGetInstallerVersion(): array ['myaccount/recipe-cms', 'pulls/burger/myfeature', ''], ['myaccount/recipe-cms', '4-release', ''], ['myaccount/recipe-cms', '4.10-release', ''], + ['myaccount/recipe-cms', '5', ''], + ['myaccount/recipe-cms', '5.1', ''], + ['myaccount/recipe-cms', '6', ''], + ['myaccount/recipe-cms', '6.0', ''], // lockstepped repo with 4.* naming ['myaccount/silverstripe-framework', '4', '4.x-dev'], ['myaccount/silverstripe-framework', '4.10', '4.10.x-dev'], - ['myaccount/silverstripe-framework', 'burger', $currentMinor], + ['myaccount/silverstripe-framework', 'burger', $currentMinorCms4], ['myaccount/silverstripe-framework', 'pulls/4/mybugfix', '4.x-dev'], ['myaccount/silverstripe-framework', 'pulls/4.10/mybugfix', '4.10.x-dev'], - ['myaccount/silverstripe-framework', 'pulls/burger/myfeature', $currentMinor], + ['myaccount/silverstripe-framework', 'pulls/burger/myfeature', $currentMinorCms4], ['myaccount/silverstripe-framework', '4-release', 'dev-4-release'], ['myaccount/silverstripe-framework', '4.10-release', 'dev-4.10-release'], ['myaccount/silverstripe-framework', 'pulls/4.10-release/some-change', 'dev-4.10-release'], + ['myaccount/silverstripe-framework', '5', '5.x-dev'], + ['myaccount/silverstripe-framework', '5.1', '5.1.x-dev'], + ['myaccount/silverstripe-framework', '6', '6.x-dev'], // lockstepped repo with 1.* naming ['myaccount/silverstripe-admin', '1', '4.x-dev'], ['myaccount/silverstripe-admin', '1.10', '4.10.x-dev'], - ['myaccount/silverstripe-admin', 'burger', $currentMinor], + ['myaccount/silverstripe-admin', 'burger', $currentMinorCms4], ['myaccount/silverstripe-admin', 'pulls/1/mybugfix', '4.x-dev'], ['myaccount/silverstripe-admin', 'pulls/1.10/mybugfix', '4.10.x-dev'], - ['myaccount/silverstripe-admin', 'pulls/burger/myfeature', $currentMinor], + ['myaccount/silverstripe-admin', 'pulls/burger/myfeature', $currentMinorCms4], ['myaccount/silverstripe-admin', '1-release', 'dev-4-release'], ['myaccount/silverstripe-admin', '1.10-release', 'dev-4.10-release'], ['myaccount/silverstripe-admin', 'pulls/1.10-release/some-change', 'dev-4.10-release'], + ['myaccount/silverstripe-admin', '2', '5.x-dev'], + ['myaccount/silverstripe-admin', '2.1', '5.1.x-dev'], + ['myaccount/silverstripe-admin', '3', '6.x-dev'], // non-lockedstepped repo - ['myaccount/silverstripe-tagfield', '2', $nextMinor], - ['myaccount/silverstripe-tagfield', '2.9', $currentMinor], - ['myaccount/silverstripe-tagfield', 'burger', $currentMinor], - ['myaccount/silverstripe-tagfield', 'pulls/2/mybugfix', $nextMinor], - ['myaccount/silverstripe-tagfield', 'pulls/2.9/mybugfix', $currentMinor], - ['myaccount/silverstripe-tagfield', 'pulls/burger/myfeature', $currentMinor], + ['myaccount/silverstripe-tagfield', '2', $nextMinorCms4], + ['myaccount/silverstripe-tagfield', '2.9', $currentMinorCms4], + ['myaccount/silverstripe-tagfield', 'burger', $currentMinorCms4], + ['myaccount/silverstripe-tagfield', 'pulls/2/mybugfix', $nextMinorCms4], + ['myaccount/silverstripe-tagfield', 'pulls/2.9/mybugfix', $currentMinorCms4], + ['myaccount/silverstripe-tagfield', 'pulls/burger/myfeature', $currentMinorCms4], ['myaccount/silverstripe-tagfield', '2-release', 'dev-' . $this->getCurrentMinorInstallerVersion('4') . '-release'], - ['myaccount/silverstripe-tagfield', '2.9-release', $nextMinorRelease], - ['myaccount/silverstripe-tagfield', 'pulls/2.9-release/some-change', $nextMinorRelease], + ['myaccount/silverstripe-tagfield', '2.9-release', $nextMinorCms4Release], + ['myaccount/silverstripe-tagfield', 'pulls/2.9-release/some-change', $nextMinorCms4Release], + // non-lockstepped repo, fallback to major version of installer (is missing 6.0 installer branch) + ['myaccount/silverstripe-tagfield', '4.0', '6.x-dev', [['name' => '6']], ['silverstripe/framework' => '^6']], // hardcoded repo version - ['myaccount/silverstripe-session-manager', '1', $nextMinor], + ['myaccount/silverstripe-session-manager', '1', $nextMinorCms4], ['myaccount/silverstripe-session-manager', '1.2', '4.10.x-dev'], - ['myaccount/silverstripe-session-manager', 'burger', $currentMinor], + ['myaccount/silverstripe-session-manager', 'burger', $currentMinorCms4], ['myaccount/silverstripe-session-manager', '1.2-release', 'dev-4.10-release'], // hardcoded repo version using array - ['myaccount/silverstripe-html5', '2', $nextMinor], + ['myaccount/silverstripe-html5', '2', $nextMinorCms4], ['myaccount/silverstripe-html5', '2.2', '4.10.x-dev'], ['myaccount/silverstripe-html5', '2.3', '4.10.x-dev'], ['myaccount/silverstripe-html5', '2.4', '4.11.x-dev'], - ['myaccount/silverstripe-html5', 'burger', $currentMinor], + ['myaccount/silverstripe-html5', 'burger', $currentMinorCms4], // force installer unlockedstepped repo - ['myaccount/silverstripe-serve', '2', $nextMinor], - ['myaccount/silverstripe-behat-extension', '2', $nextMinor], + ['myaccount/silverstripe-serve', '2', $nextMinorCms4], + ['myaccount/silverstripe-behat-extension', '2', $nextMinorCms4], ]; } @@ -309,6 +358,73 @@ public function provideCreateJson(): array ], ] ], + // general test for v6 + [ + implode("\n", [ + $this->getGenericYml(), + << '6.x-dev', + 'php' => '8.1', + 'db' => DB_MYSQL_57, + 'composer_require_extra' => '', + 'composer_args' => '--prefer-lowest', + 'name_suffix' => '', + 'phpunit' => 'true', + 'phpunit_suite' => 'all', + 'phplinting' => 'false', + 'phpcoverage' => 'false', + 'endtoend' => 'false', + 'endtoend_suite' => 'root', + 'endtoend_config' => '', + 'js' => 'false', + 'needs_full_setup' => 'true', + 'name' => '8.1 prf-low mysql57 phpunit all', + ], + [ + 'installer_version' => '6.x-dev', + 'php' => '8.2', + 'db' => DB_MARIADB, + 'composer_require_extra' => '', + 'composer_args' => '', + 'name_suffix' => '', + 'phpunit' => 'true', + 'phpunit_suite' => 'all', + 'phplinting' => 'false', + 'phpcoverage' => 'false', + 'endtoend' => 'false', + 'endtoend_suite' => 'root', + 'endtoend_config' => '', + 'js' => 'false', + 'needs_full_setup' => 'true', + 'name' => '8.2 mariadb phpunit all', + ], + [ + 'installer_version' => '6.x-dev', + 'php' => '8.3', + 'db' => DB_MYSQL_80, + 'composer_require_extra' => '', + 'composer_args' => '', + 'name_suffix' => '', + 'phpunit' => 'true', + 'phpunit_suite' => 'all', + 'phplinting' => 'false', + 'phpcoverage' => 'false', + 'endtoend' => 'false', + 'endtoend_suite' => 'root', + 'endtoend_config' => '', + 'js' => 'false', + 'needs_full_setup' => 'true', + 'name' => '8.3 mysql80 phpunit all', + ], + ] + ], // general test for v5.1 [ implode("\n", [ @@ -773,10 +889,29 @@ public function provideGraphql3(): array ]; } + private function writeComposerJson(array $composerDeps, string $repoType = '', $filename = '__composer.json') + { + $composer = new stdClass(); + if ($repoType) { + $composer->type = $repoType; + } + $composer->require = new stdClass(); + foreach ($composerDeps as $dep => $version) { + $composer->require->{$dep} = $version; + } + file_put_contents($filename, json_encode($composer, JSON_UNESCAPED_SLASHES)); + } + + private function writeInstallerBranchesJson() + { + $installerBranchesJson = $this->getInstallerBranchesJson(); + file_put_contents('__installer_branches.json', json_encode($installerBranchesJson, JSON_UNESCAPED_SLASHES)); + } + /** - * @dataProvider provideGetInstallerVersionCMS5FromComposer + * @dataProvider provideGetInstallerVersionFromComposer */ - public function testGetInstallerVersionCMS5FromComposer( + public function testGetInstallerVersionFromComposer( string $githubRepository, string $branch, array $composerDeps, @@ -795,34 +930,31 @@ public function testGetInstallerVersionCMS5FromComposer( ]); try { $creator = new JobCreator(); + $this->writeComposerJson($composerDeps, $repoType); + $this->writeInstallerBranchesJson(); $creator->composerJsonPath = '__composer.json'; - $composer = new stdClass(); - if ($repoType) { - $composer->type = $repoType; - } - $composer->require = new stdClass(); - foreach ($composerDeps as $dep => $version) { - $composer->require->{$dep} = $version; - } - file_put_contents('__composer.json', json_encode($composer, JSON_PRETTY_PRINT + JSON_UNESCAPED_SLASHES)); $json = json_decode($creator->createJson($yml)); $this->assertSame($expected, $json->include[0]->installer_version); } finally { unlink('__composer.json'); + unlink('__installer_branches.json'); } } - public function provideGetInstallerVersionCMS5FromComposer(): array + public function provideGetInstallerVersionFromComposer(): array { - $currentMinor = $this->getCurrentMinorInstallerVersion('4') . '.x-dev'; + $currentMinorCms4 = $this->getCurrentMinorInstallerVersion('4') . '.x-dev'; return [ // priority given to branch name ['myaccount/silverstripe-framework', '4', [], 'silverstripe-module', '4.x-dev'], ['myaccount/silverstripe-framework', '4.10', [], 'silverstripe-vendormodule', '4.10.x-dev'], - ['myaccount/silverstripe-framework', 'burger', [], 'silverstripe-theme', $currentMinor], + ['myaccount/silverstripe-framework', 'burger', [], 'silverstripe-theme', $currentMinorCms4], ['myaccount/silverstripe-framework', '5', [], 'silverstripe-recipe', '5.x-dev'], ['myaccount/silverstripe-framework', '5.10', [], 'silverstripe-vendormodule', '5.10.x-dev'], + ['myaccount/silverstripe-framework', '6', [], 'silverstripe-recipe', '6.x-dev'], + ['myaccount/silverstripe-framework', '6.10', [], 'silverstripe-vendormodule', '6.10.x-dev'], // fallback to looking at deps in composer.json, use current minor of installer .x-dev + // CMS 5 ['myaccount/silverstripe-admin', 'mybranch', ['silverstripe/framework' => '5.x-dev'], 'silverstripe-module', '5.x-dev'], ['myaccount/silverstripe-admin', 'mybranch', ['silverstripe/framework' => '5.0.x-dev'], 'silverstripe-vendormodule', '5.0.x-dev'], ['myaccount/silverstripe-admin', 'mybranch', ['silverstripe/framework' => '^5'], 'silverstripe-theme', '5.2.x-dev'], @@ -832,6 +964,17 @@ public function provideGetInstallerVersionCMS5FromComposer(): array ['myaccount/silverstripe-somemodule', '3', ['silverstripe/framework' => '^5'], 'package', ''], ['myaccount/silverstripe-somemodule', '3', ['silverstripe/framework' => '^5'], '', ''], ['myaccount/silverstripe-somemodule', '3', [], '', ''], + // CMS 6 - note some of the 6.x-dev $expected will need to change once once + // the `6.0` branches are created - currently only `6` branches exist + ['myaccount/silverstripe-admin', 'mybranch', ['silverstripe/framework' => '6.x-dev'], 'silverstripe-module', '6.x-dev'], + ['myaccount/silverstripe-admin', 'mybranch', ['silverstripe/framework' => '6.0.x-dev'], 'silverstripe-vendormodule', '6.0.x-dev'], + ['myaccount/silverstripe-admin', 'mybranch', ['silverstripe/framework' => '^6'], 'silverstripe-theme', '6.x-dev'], + ['myaccount/silverstripe-somemodule', 'mybranch', ['silverstripe/cms' => '^6'], 'silverstripe-recipe', '6.x-dev'], + ['myaccount/silverstripe-somemodule', 'mybranch', ['silverstripe/admin' => '^3'], 'silverstripe-vendormodule', '6.x-dev'], + ['myaccount/silverstripe-somemodule', '4', ['silverstripe/framework' => '^6'], 'silverstripe-vendormodule', '6.x-dev'], + ['myaccount/silverstripe-somemodule', '4', ['silverstripe/framework' => '^6'], 'package', ''], + ['myaccount/silverstripe-somemodule', '4', ['silverstripe/framework' => '^6'], '', ''], + ['myaccount/silverstripe-somemodule', '4', [], '', ''], ]; } @@ -898,6 +1041,15 @@ public function provideComposerInstall(): array '8.1 mysql57 phpunit all' ] ], + 'composerinstall_nophpversion_framework6' => [ + 'true', + '', + '6.x-dev', + 'silverstripe-vendormodule', + [ + '8.1 mysql57 phpunit all' + ] + ], 'composerinstall_definedphpversion_framework5' => [ 'true', '21.99', @@ -938,6 +1090,17 @@ public function provideComposerInstall(): array '8.3 mysql80 phpunit all', ] ], + 'composerupgrade_nophpversion_framework6' => [ + 'false', + '', + '6.x-dev', + 'silverstripe-vendormodule', + [ + '8.1 prf-low mysql57 phpunit all', + '8.2 mariadb phpunit all', + '8.3 mysql80 phpunit all', + ] + ], 'composerupgrade_definedphpversion_framework5' => [ 'false', '21.99', @@ -960,6 +1123,17 @@ public function provideComposerInstall(): array '8.3 mysql80 phpunit all', ] ], + 'composerupgrade_invalidphpversion_framework6' => [ + 'false', + 'fish', + '6.x-dev', + 'silverstripe-theme', + [ + '8.1 prf-low mysql57 phpunit all', + '8.2 mariadb phpunit all', + '8.3 mysql80 phpunit all', + ] + ], 'composerupgrade_nophpversion_framework51' => [ 'false', '',