From 0f8ca867fd201eeeca3576efe88fa258cfba8ee2 Mon Sep 17 00:00:00 2001 From: Steve Boyd <emteknetnz@gmail.com> Date: Fri, 1 Jul 2022 11:03:43 +1200 Subject: [PATCH] FIX Update getInstallerVersion --- .github/workflows/ci.yml | 27 +++++++ .gitignore | 1 + action.php | 8 +-- job_creator.php | 58 ++++++++++----- phpunit.xml | 8 +++ tests/JobCreatorTest.php | 149 +++++++++++++++++++++++++++++++++++++++ tests/bootstrap.php | 4 ++ 7 files changed, 233 insertions(+), 22 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 phpunit.xml create mode 100644 tests/JobCreatorTest.php create mode 100644 tests/bootstrap.php 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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<phpunit bootstrap="tests/bootstrap.php" colors="true"> + <testsuites> + <testsuite name="Default"> + <directory>tests</directory> + </testsuite> + </testsuites> +</phpunit> diff --git a/tests/JobCreatorTest.php b/tests/JobCreatorTest.php new file mode 100644 index 0000000..979db8a --- /dev/null +++ b/tests/JobCreatorTest.php @@ -0,0 +1,149 @@ +<?php + +use PHPUnit\Framework\TestCase; + +class JobCreatorTest extends TestCase +{ + /** + * @dataProvider provideGetInstallerVersion + */ + public function testGetInstallerVersion( + string $githubRepository, + string $branch, + string $expected + ): void { + $creator = new JobCreator(); + $actual = $creator->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 [ + [ + <<<EOT + endtoend: 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' + EOT, + [ + 'endtoend' => 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) + [ + <<<EOT + github_my_ref: 1.10 + EOT, + 'github_my_ref needs to be surronded by single-quotes' + ], + // invalid yml + [ + <<<EOT + this: -- + is: - total: ' nonsense + " + EOT, + 'Failed to parse yml' + ], + ]; + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..6832380 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,4 @@ +<?php +// working directory will be root +include 'consts.php'; +include 'job_creator.php';