diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..4e47222 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,27 @@ +name: CI + +on: + push: + pull_request: + workflow_dispatch: + +jobs: + ci: + name: CI + runs-on: ubuntu-latest + steps: + + - name: Checkout code + uses: actions/checkout@7884fcad6b5d53d10323aee724dc68d8b9096a2e # v2.4.2 + + - name: Install PHP + uses: shivammathur/setup-php@3eda58347216592f618bb1dff277810b6698e4ca # v2.19.1 + with: + php-version: 8.1 + extensions: yaml + + - name: Install PHPUnit + run: wget https://phar.phpunit.de/phpunit-9.5.phar + + - name: PHPUnit + run: php phpunit-9.5.phar --verbose --colors=always diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..165765a --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.phpunit.result.cache diff --git a/action.php b/action.php index 52ac606..dabbdbb 100644 --- a/action.php +++ b/action.php @@ -4,10 +4,6 @@ include 'job_creator.php'; // Reads inputs.yml and creates a new json matrix -$inputs = yaml_parse(file_get_contents('__inputs.yml')); -if ($inputs === false) { - echo 'Unable to parse __inputs.yml'; - exit(1); -} +$yml = file_get_contents('__inputs.yml'); $jobCreator = new JobCreator(); -echo $jobCreator->createJson($inputs); +echo $jobCreator->createJson($yml); diff --git a/job_creator.php b/job_creator.php index 53a0264..d7e6e94 100644 --- a/job_creator.php +++ b/job_creator.php @@ -13,23 +13,29 @@ public function getInstallerVersion(string $githubRepository, string $branch): s if (in_array($repo, NO_INSTALLER_REPOS)) { return ''; } - // e.g. ['4', '11'] - $portions = explode('.', $branch); - if (count($portions) == 1) { - return '4.x-dev'; + // e.g. pulls/4.10/some-bugfix or pulls/4/some-feature + if (preg_match('#^pulls/([0-9\.]+)/#', $branch, $matches)) { + $branch = $matches[1]; } - if (in_array($repo, LOCKSTEPED_REPOS)) { - return '4.' . $portions[1] . '.x-dev'; - } else { - // use the latest minor version of installer - $installerVersions = array_keys(INSTALLER_TO_PHP_VERSIONS); - // remove '4' version - $installerVersions = array_diff($installerVersions, ['4']); - // get the minor portions of the verisons e.g. [9, 10, 11] - $minorPortions = array_map(fn($portions) => (int) explode('.', $portions)[1], $installerVersions); - sort($minorPortions); - return '4.' . $minorPortions[count($minorPortions) - 1] . '.x-dev'; + // e.g. 4.10-release + $branch = preg_replace('#^([0-9\.]+)-release$#', '$1', $branch); + if (in_array($repo, LOCKSTEPED_REPOS) && is_numeric($branch)) { + // e.g. ['4', '11'] + $portions = explode('.', $branch); + if (count($portions) == 1) { + return '4.x-dev'; + } else { + return '4.' . $portions[1] . '.x-dev'; + } } + // use the latest minor version of installer + $installerVersions = array_keys(INSTALLER_TO_PHP_VERSIONS); + // remove '4' version + $installerVersions = array_diff($installerVersions, ['4']); + // get the minor portions of the verisons e.g. [9, 10, 11] + $minorPortions = array_map(fn($portions) => (int) explode('.', $portions)[1], $installerVersions); + sort($minorPortions); + return '4.' . $minorPortions[count($minorPortions) - 1] . '.x-dev'; } public function createJob(int $phpIndex, array $opts): array @@ -180,8 +186,28 @@ private function buildDynamicMatrix( return $matrix; } - public function createJson(array $inputs): string + public function getInputs(string $yml): array + { + $message = 'Failed to parse yml'; + try { + $inputs = yaml_parse($yml); + } catch (Exception $e) { + throw new Exception($message); + } + if (!$inputs) { + throw new Exception($message); + } + if (array_key_exists('github_my_ref', $inputs)) { + if (!preg_match("#github_my_ref: *'#", $yml)) { + throw new Exception('github_my_ref needs to be surrounded by single-quotes'); + } + } + return $inputs; + } + + public function createJson(string $yml): string { + $inputs = $this->getInputs($yml); // $myRef will either be a branch for push (i.e cron) and pull-request (target branch), or a semver tag $myRef = $inputs['github_my_ref']; $isTag = preg_match('#^[0-9]+\.[0-9]+\.[0-9]+$#', $myRef, $m); diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..f5e111b --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,8 @@ + + + + + tests + + + diff --git a/tests/JobCreatorTest.php b/tests/JobCreatorTest.php new file mode 100644 index 0000000..d2e1b2f --- /dev/null +++ b/tests/JobCreatorTest.php @@ -0,0 +1,149 @@ +getInstallerVersion($githubRepository, $branch); + $this->assertSame($expected, $actual); + } + + private function getLatestInstallerVersion(): string + { + $versions = array_keys(INSTALLER_TO_PHP_VERSIONS); + natsort($versions); + $versions = array_reverse($versions); + return $versions[0]; + } + + public function provideGetInstallerVersion(): array + { + $latest = $this->getLatestInstallerVersion() . '.x-dev'; + return [ + // no-installer repo + ['myaccount/recipe-cms', '4', ''], + ['myaccount/recipe-cms', '4.10', ''], + ['myaccount/recipe-cms', 'burger', ''], + ['myaccount/recipe-cms', 'pulls/4/myfeature', ''], + ['myaccount/recipe-cms', 'pulls/4.10/myfeature', ''], + ['myaccount/recipe-cms', 'pulls/burger/myfeature', ''], + ['myaccount/recipe-cms', '4-release', ''], + ['myaccount/recipe-cms', '4.10-release', ''], + // 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', $latest], + ['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', $latest], + ['myaccount/silverstripe-framework', '4-release', '4.x-dev'], + ['myaccount/silverstripe-framework', '4.10-release', '4.10.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', $latest], + ['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', $latest], + ['myaccount/silverstripe-admin', '1-release', '4.x-dev'], + ['myaccount/silverstripe-admin', '1.10-release', '4.10.x-dev'], + // non-lockedstepped repo + ['myaccount/silverstripe-tagfield', '2', $latest], + ['myaccount/silverstripe-tagfield', '2.9', $latest], + ['myaccount/silverstripe-tagfield', 'burger', $latest], + ['myaccount/silverstripe-tagfield', 'pulls/2/mybugfix', $latest], + ['myaccount/silverstripe-tagfield', 'pulls/2.9/mybugfix', $latest], + ['myaccount/silverstripe-tagfield', 'pulls/burger/myfeature', $latest], + ['myaccount/silverstripe-tagfield', '2-release', $latest], + ['myaccount/silverstripe-tagfield', '2.9-release', $latest], + ]; + } + + /** + * @dataProvider provideGetInputsValid + */ + public function testGetInputsValid(string $yml, array $expected) + { + if (!function_exists('yaml_parse')) { + $this->markTestSkipped('yaml extension is not installed'); + } + $creator = new JobCreator(); + $actual = $creator->getInputs($yml); + $this->assertSame($expected, $actual); + } + + public function provideGetInputsValid(): array + { + return [ + [ + << true, + 'js' => true, + 'phpcoverage' => false, + 'phpcoverage_force_off' => false, + 'phplinting' => true, + 'phpunit' => true, + 'simple_matrix' => false, + 'github_repository' => 'myaccount/silverstripe-versioned', + 'github_my_ref'=> 'pulls/1.10/module-standards' + ] + ], + ]; + } + + /** + * @dataProvider provideGetInputsInvalid + */ + public function testGetInputsInvalid(string $yml, string $expectedMessage) + { + if (!function_exists('yaml_parse')) { + $this->markTestSkipped('yaml extension is not installed'); + } + $this->expectException(Exception::class); + $this->expectExceptionMessage($expectedMessage); + $creator = new JobCreator(); + $creator->getInputs($yml); + } + + public function provideGetInputsInvalid(): array + { + return [ + // missing quotes around github_my_ref (would turn into an int, so 1.10 becomes 1.1) + [ + <<