diff --git a/.github/workflows/auto-tag.yml b/.github/workflows/auto-tag.yml new file mode 100644 index 0000000..33446ca --- /dev/null +++ b/.github/workflows/auto-tag.yml @@ -0,0 +1,12 @@ +name: Auto-tag +on: + push: + tags: + - '*.*.*' +jobs: + auto-tag: + name: Auto-tag + runs-on: ubuntu-latest + steps: + - name: Auto-tag + uses: silverstripe/gha-auto-tag@main diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..7b2f47a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,499 @@ +name: CI +on: + workflow_call: + inputs: + # extra jobs must be multi-line string, as there's no support for `type: array` for inputs + extra_jobs: + type: string + required: false + default: '' + composer_require_extra: + type: string + required: false + default: '' + dynamic_matrix: + type: boolean + default: true + simple_matrix: + type: boolean + default: false + endtoend: + type: boolean + default: true + phpcoverage: + type: boolean + default: false + phplinting: + type: boolean + default: true + phpunit: + type: boolean + default: true + js: + type: boolean + default: true + +jobs: + + # Writes various github variables to the console for debugging purposes + context: + name: Context + runs-on: ubuntu-latest + env: + # Put this through an env variable to prevent possible string substitution injection via pull-request branch name + GITHUB_BASE_REF: ${{ github.base_ref }} + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_REF: ${{ github.ref }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_REPOSITORY: ${{ github.repository }} + steps: + - name: Context + run: | + # https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability + # github.base_ref - The target branch of the pull request in a workflow run. + echo "github.base_ref: $GITHUB_BASE_REF" + # github.head_ref - The source branch of the pull request in a workflow run. + echo "github.head_ref: $GITHUB_HEAD_REF" + # github.ref - The branch or tag ref that triggered the workflow run. + # For branches this is the format refs/heads/, and for tags it is refs/tags/ + echo "github.ref: $GITHUB_REF" + # github.ref_name - The branch or tag name that triggered the workflow run - same as github.ref though without the leading refs/[heads|tags]/ + echo "github.ref_name: $GITHUB_REF_NAME" + # gitbub.repository - The owner and repository name. For example, Codertocat/Hello-World + echo "$github.repository: $GITHUB_REPOSITORY" + + # Generates a dynamic matrix of jobs to run tests on based on the inputs provided + genmatrix: + name: Generate matrix + runs-on: ubuntu-latest + + # gha-generate-matrix script.php will sanitise matrix outputs so they're safe to use within bash + outputs: + matrix: ${{ steps.generate-matrix.outputs.matrix }} + + steps: + - name: Generate matrix + id: generate-matrix + uses: emteknetnz/gha-generate-matrix@main + with: + extra_jobs: ${{ inputs.extra_jobs }} + simple_matrix: ${{ inputs.simple_matrix }} + endtoend: ${{ inputs.endtoend }} + phpcoverage: ${{ inputs.phpcoverage }} + phplinting: ${{ inputs.phplinting }} + phpunit: ${{ inputs.phpunit }} + js: ${{ inputs.js }} + + # For each job in the matrix, setup an environment and run the tests + tests: + needs: genmatrix + + strategy: + # set fail-fast to false prevent one matrix job from cancelling other matrix jobs + # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategyfail-fast + fail-fast: false + matrix: ${{fromJson(needs.genmatrix.outputs.matrix)}} + + runs-on: ubuntu-latest + + services: + # It takes around 9 seconds per extra database added, so this is something that could be optimised later + # to only add the database that's required for the job + mysql57: + image: mysql:5.7 + env: + MYSQL_HOST: 127.0.0.1 + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: SS_mysite + ports: + - 3357:3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + mysql80: + image: mysql:8.0 + env: + MYSQL_HOST: 127.0.0.1 + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: SS_mysite + ports: + - 3380:3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + postgres: + image: postgres + env: + POSTGRES_PASSWORD: postgres + options: --health-cmd="pg_isready" --health-interval=10s --health-timeout=5s --health-retries=5 + ports: + - 5432:5432 + + env: + artifacts_name: ${{ matrix.name }} + + name: ${{ matrix.name }} + + steps: + + - name: Checkout code + uses: actions/checkout@7884fcad6b5d53d10323aee724dc68d8b9096a2e # v2.4.2 + + - name: Install PHP + # SHA will need to be updated to support new php version when they are released + uses: shivammathur/setup-php@3eda58347216592f618bb1dff277810b6698e4ca # v2.19.1 + with: + php-version: ${{ matrix.php }} + extensions: curl, dom, gd, intl, json, ldap, mbstring, mysql, tidy, xdebug, zip + tools: composer:v2 + coverage: xdebug + # While this should be the correct way to allow forks in composer.json repositories + # in practice there are still many sporadic "Could not authenticate against github.com" errors + # there's 1,000 requests per hour limit when using this token, likely it get exceeded + # fairly easily when using a fork with multiple jobs in a matrix + #env: + # COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Configure PHP + run: | + # Set memory limit and disable xdebug if not running phpcoverage + if [[ -z $(which php) ]]; then + echo "PHP not installed, skipping" && exit 0 + fi + + # github linux runners have 7GB of RAM + # https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners + # Set a high memory limit, particularly for php coverage tests + PHP_MEMORY_LIMIT=6G + # Assign less memory for behat tests so that chrome has plenty of memory available + if [[ "${{ matrix.endtoend }}" == "true" ]]; then + PHP_MEMORY_LIMIT=4G + fi + echo "PHP_MEMORY_LIMIT is $PHP_MEMORY_LIMIT" + + sudo sh -c "echo 'memory_limit = $PHP_MEMORY_LIMIT' >> /etc/php/${{ matrix.php }}/cli/php.ini" + if [[ -f /etc/php/${{ matrix.php }}/apache2/php.ini ]]; then + sudo sh -c "echo 'memory_limit = $PHP_MEMORY_LIMIT' >> /etc/php/${{ matrix.php }}/apache2/php.ini" + fi + + # Disable xdebug which greatly slow down unit testing + # Note: omitting xdebug from shivammathur/setup-php still results in xdebug being installed and enabled + if [[ "${{ matrix.phpcoverage }}" == "true" ]]; then + sudo sh -c "echo ';zend_extension=xdebug.so' > /etc/php/${{ matrix.php }}/mods-available/xdebug.ini" + fi + echo "PHP has been configured" + + - name: Apt install additional requirements + env: + GITHUB_REPOSITORY: ${{ github.repository }} + run: | + # apt install extra requirements as required + if [[ "${{ matrix.endtoend }}" == "true" ]]; then + sudo apt install -y software-properties-common + sudo add-apt-repository -y ppa:ondrej/php + sudo add-apt-repository -y ppa:ondrej/apache2 + sudo apt update + sudo apt install -y libapache2-mod-php${{ matrix.php }} + # ubuntu-latest comes with a current version of google-chrome-stable and chromedriver + fi + if [[ $GITHUB_REPOSITORY =~ /(spellcheck|recipe-authoring-tools)$ ]] || [[ "${{ matrix.phpunit_suite }}" == "recipe-authoring-tools" ]]; then + sudo apt install -y hunspell libhunspell-dev hunspell-en-us + fi + + - name: Configure apache - endtoend test + if: ${{ matrix.endtoend == 'true' }} + run: | + # apache2 is installed and running by default in ubuntu + # update dir.conf to use index.php as the primary index doc + DIR_CONF=$(cat << EOF + + DirectoryIndex index.php index.html index.cgi index.pl index.xhtml index.htm + + EOF) + sudo echo "$DIR_CONF" > /etc/apache2/mods-enabled/dir.conf + # create a 000-default.conf file with the pwd as the DocumentRoot + DEFAULT_CONF=$(cat << EOF + + ServerAdmin webmaster@localhost + DocumentRoot $(pwd) + + AllowOverride All + Require all granted + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + + EOF) + sudo echo "$DEFAULT_CONF" > /etc/apache2/sites-enabled/000-default.conf + sudo a2enmod rewrite + # run apache as 'runner:docker' instead of 'www-data:www-data' + sudo sh -c "echo 'export APACHE_RUN_USER=runner' >> /etc/apache2/envvars" + sudo sh -c "echo 'export APACHE_RUN_GROUP=docker' >> /etc/apache2/envvars" + sudo systemctl restart apache2 + echo "Apache has been configured" + + # This is shared between runs, not just jobs. It means the first time the repo runs the job it'll + # need to download requirements for the first time, after that it will be plenty quick + # https://docs.github.com/en/actions/advanced-guides/caching-dependencies-to-speed-up-workflows + - name: Enable shared composer cache + uses: actions/cache@937d24475381cd9c75ae6db12cb4e79714b926ed # @v2.1.7 + with: + path: ~/.cache/composer + key: shared-composer-cache + + # Update composer.json and install dependencies + - name: Composer + env: + INPUTS_COMPOSER_REQUIRE_EXTRA: ${{ inputs.composer_require_extra }} + MATRIX_COMPOSER_REQUIRE_EXTRA: ${{ matrix.composer_require_extra }} + # https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability + # github.base_ref is only available on pull-requests events is the target branch - is is NOT prefixed with refs/heads/ + GITHUB_BASE_REF: ${{ github.base_ref }} + # github.ref_name is used for regular branch builds on events push - it is NOT prefixed with refs/heads/ + # github.ref_name is also the tag on tag events - it is NOT prefixed with refs/tags/ + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_REPOSITORY: ${{ github.repository }} + run: | + BRANCH_OR_TAG=$GITHUB_REF_NAME + if [[ $GITHUB_BASE_REF != "" ]]; then + BRANCH_OR_TAG=$GITHUB_BASE_REF + fi + if [[ $BRANCH_OR_TAG =~ ^[1-9]$ ]] || [[ $BRANCH_OR_TAG =~ ^[0-9]\.[0-9]+$ ]]; then + export COMPOSER_ROOT_VERSION="${BRANCH_OR_TAG}.x-dev" + elif [[ $BRANCH_OR_TAG =~ ^[0-9]\.[0-9]+\.[0-9]+ ]]; then + export COMPOSER_ROOT_VERSION="${BRANCH_OR_TAG}" + else + export COMPOSER_ROOT_VERSION="dev-${BRANCH_OR_TAG}" + fi + echo "BRANCH_OR_TAG is $BRANCH_OR_TAG" + echo "COMPOSER_ROOT_VERSION is $COMPOSER_ROOT_VERSION" + + # If using phpunit9, ensure sminnee phpunit5 modules do not get installed + php -r ' + $j = json_decode(file_get_contents("composer.json")); + $pu = $j->{"require-dev"}->{"phpunit/phpunit"} ?? ""; + $rt = $j->{"require-dev"}->{"silverstripe/recipe-testing"} ?? ""; + if ($pu == "^9" || $pu == "^9.5" || $rt == "^2") { + if (!property_exists($j, "replace")) { + $j->replace = new stdClass(); + } + $j->replace->{"sminnee/phpunit"} = "*"; + $j->replace->{"sminnee/phpunit-mock-objects"} = "*"; + file_put_contents("composer.json", json_encode($j, JSON_PRETTY_PRINT + JSON_UNESCAPED_SLASHES)); + } + ' + + # Require silverstripe/installer for non-recipe and all but a few modules + if [[ "${{ matrix.installer_version }}" != "" ]]; then + composer require silverstripe/installer:${{ matrix.installer_version }} --no-update + fi + + # Required for any module/recipe that runs silverstripe/assets unit tests + # Should technically be defined as composer_require_extra on individual modules, though easier just doing here + # 1.6.10 is for --prefer-lowest and is the minimum version with php 8.1 support + composer require mikey179/vfsstream:^1.6.10 --dev --no-update + + if [[ "${{ matrix.db }}" == "pgsql" ]] && ! [[ $GITHUB_REPOSITORY =~ /postgresql ]]; then + composer require silverstripe/postgresql:^2 --no-update + fi + if [[ "${{ matrix.endtoend }}" == "true" ]]; then + composer require silverstripe/recipe-testing:^2 --dev --no-update + fi + if [[ "${{ matrix.phplinting }}" == "true" ]]; then + composer require silverstripe/cow:dev-master --dev --no-update + fi + if [[ $INPUTS_COMPOSER_REQUIRE_EXTRA != "" ]]; then + # $INPUTS_COMPOSER_REQUIRE_EXTRA is explicitly not wrapped in double quotes below + # so that multiple requirements separated by spaces will work + composer require $INPUTS_COMPOSER_REQUIRE_EXTRA --no-update + fi + if [[ $MATRIX_COMPOSER_REQUIRE_EXTRA != "" ]]; then + # $MATRIX_COMPOSER_REQUIRE_EXTRA is explicitly not wrapped in double quotes below + # so that multiple requirements separated by spaces will work + composer require $MATRIX_COMPOSER_REQUIRE_EXTRA --no-update + fi + + # Ensure composer.json has prefer-stable true and minimum-stability dev + # Update preferred-install to source for recipes and some modules that run + # other unit-tests in other modules + php -r ' + $j = json_decode(file_get_contents("composer.json")); + $j->{"prefer-stable"} = true; + $j->{"minimum-stability"} = "dev"; + if (empty($j->config)) { + $j->config = new stdClass(); + } + $j->config->{"preferred-install"} = new stdClass(); + $j->config->{"preferred-install"}->{"silverstripe/*"} = "source"; + $j->config->{"preferred-install"}->{"creative-commoners/*"} = "source"; + $j->config->{"preferred-install"}->{"symbiote/*"} = "source"; + $j->config->{"preferred-install"}->{"dnadesign/*"} = "source"; + $j->config->{"preferred-install"}->{"bringyourownideas/*"} = "source"; + $j->config->{"preferred-install"}->{"colymba/*"} = "source"; + $j->config->{"preferred-install"}->{"cwp/*"} = "source"; + $j->config->{"preferred-install"}->{"tractorcow/*"} = "source"; + $j->config->{"preferred-install"}->{"*"} = "dist"; + file_put_contents("composer.json", json_encode($j, JSON_PRETTY_PRINT + JSON_UNESCAPED_SLASHES)); + ' + + # Enable plugins + composer config allow-plugins.composer/installers true + composer config allow-plugins.silverstripe/recipe-plugin true + composer config allow-plugins.silverstripe/vendor-plugin true + + # Useful to see generated composer.json when diagnosing new bugs + cat composer.json + + # matrix.composer_args sometimes includes `--prefer-lowest` which is only supported by `composer update`, not `composer install` + # Modules do not have composer.lock files, so `composer update` is the same speed as `composer install` + composer update --no-interaction --no-progress ${{ matrix.composer_args }} + + # Useful to see what was installed + composer show + + # Remove vendor unit tests files that were installed because of the use of --prefer-source + # Some older silverstripe vendor modules may still have the phpunit5 setUp() signatures without :void which when loaded will throw fatal PHP errors. + # We cannot simply get rid of the 'tests' folders because behat requires vendor/silverstripe/[framework|cms]/tests/behat/serve-bootstrap.php + # Recipes are excluded because the unit tests they run are in the required modules, and there's an assumption they'll require a compatible minor branch of the module + if ! [[ $GITHUB_REPOSITORY =~ /(recipe|silverstripe-installer) ]]; then + echo "Repositiory is a not a recipe, removing unecessary vendor silverstripe unit tests" + # Make an exception for 'ExtendTest.php' which is a misnamed TestOnly non-test class + php -r ' + $a = [ + "silverstripe", + "cwp", + "symbiote", + "dnadesign", + "tractorcow", + "bringyourownideas", + "colymba" + ]; + foreach ($a as $v) { + $d = "vendor/$v"; + if (!is_dir($d)) { + continue; + } + foreach (explode("\n", shell_exec("find $d | grep \/tests\/ | grep [a-zA-Z0-9]Test.php")) as $f) { + if (preg_match("#ExtendTest\.php#", $f)) { + continue; + } + if (is_file($f)) { + unlink($f); + } + } + } + ' + # Also remove a few other file that don't match the *Test.php convention and extends SapphireTest + if [[ -f vendor/silverstripe/assets/tests/php/FilenameParsing/FileIDHelperTester.php ]]; then + rm vendor/silverstripe/assets/tests/php/FilenameParsing/FileIDHelperTester.php + fi + if [[ -f vendor/silverstripe/framework/tests/php/Forms/NullableFieldTests.php ]]; then + rm vendor/silverstripe/framework/tests/php/Forms/NullableFieldTests.php + fi + if [[ -f vendor/silverstripe/graphql/tests/Middleware/MiddlewareProcessTestBase.php ]]; then + rm vendor/silverstripe/graphql/tests/Middleware/MiddlewareProcessTestBase.php + fi + # Rebuild composer classloader + composer dumpautoload -o + fi + + - name: Final preparation + run: | + # Add .env file and create artifacts directory + # Note: the wonky indentation is intentional so there is no space at the start of + # each newline in the .env file + if [[ "${{ matrix.db }}" =~ mysql ]]; then + if [[ "${{ matrix.db }}" == "mysql57pdo" ]]; then + cat << EOF > .env + SS_DATABASE_CLASS="MySQLPDODatabase" + SS_DATABASE_PORT="3357" + EOF + else + cat << EOF > .env + SS_DATABASE_CLASS="MySQLDatabase" + SS_DATABASE_PORT="${{ matrix.db == 'mysql57' && '3357' || '3380' }}" + EOF + fi + cat << EOF >> .env + SS_DATABASE_SERVER="127.0.0.1" + SS_DATABASE_USERNAME="root" + SS_DATABASE_PASSWORD="root" + EOF + else + cat << EOF > .env + SS_DATABASE_CLASS="PostgreSQLDatabase" + SS_DATABASE_SERVER="localhost" + SS_DATABASE_PORT="5432" + SS_DATABASE_USERNAME="postgres" + SS_DATABASE_PASSWORD="postgres" + EOF + fi + cat << EOF >> .env + SS_ENVIRONMENT_TYPE="dev" + SS_DATABASE_NAME="SS_mysite" + SS_DEFAULT_ADMIN_USERNAME="admin" + SS_DEFAULT_ADMIN_PASSWORD="password" + SS_TRUSTED_PROXY_IPS="*" + SS_MFA_SECRET_KEY="1234567894175b99966561e1efe237e4" + SS_BASE_URL="http://localhost" + EOF + + # debug + echo ".env is" + cat .env + + # Artifacts directory must be created after composer install as it would remove the artifacts directory + # This seems a bit strange, if you need to add an artifact at an earlier stage then revalidate that + # composer install does actually remove the artifact directory + mkdir artifacts + + # run dev/build flush to help debug any issues (though it's not strictly required here) + # normal module + if [[ -f vendor/bin/sake ]]; then + vendor/bin/sake dev/build flush=1 + fi + # framework module + if [[ -f sake ]]; then + ./sake dev/build flush=1 + fi + + # Delete the silverstripe-cache dir - it will automatically recreate when needed + # There were issues with a unit test getting the following issue + # Identifier name 'SilverStripe_CampaignAdmin_Tests_AddToCampaignValidatorTest_TestObject' is too long + # Likely because the /tmp/silverstripe-cache-php7.4.xyz... dir being out of sync with TestOnly objects + rm -rf $(find /tmp -maxdepth 1 | grep silverstripe-cache) + + - name: Debug + run: | + echo "matrix.phpunit: ${{ matrix.phpunit }}" + + - name: Run tests + uses: emteknetnz/gha-run-tests@main + with: + phpunit: ${{ matrix.phpunit }} + phpunit_suite: ${{ matrix.phpunit_suite }} + phpunit_fail_on_warning: false + phplinting: ${{ matrix.phplinting }} + phpcoverage: ${{ matrix.phpcoverage }} + endtoend: ${{ matrix.endtoend }} + endtoend_suite: ${{ matrix.endtoend_suite }} + endtoend_config: ${{ matrix.endtoend_config }} + js: ${{ matrix.js }} + + - name: Copy artifacts + if: always() + run: | + # Copy selected files to the artifacts dir + if [[ -f composer.json ]]; then + cp composer.json artifacts + fi + if [[ -f composer.lock ]]; then + cp composer.lock artifacts + fi + if [[ "${{ matrix.endtoend }}" == "true" ]] && [[ -f __behat.yml ]]; then + cp __behat.yml artifacts + fi + + # https://docs.github.com/en/actions/using-workflows/storing-workflow-data-as-artifacts + - name: Upload artifacts + uses: actions/upload-artifact@82c141cc518b40d92cc801eee768e7aafc9c2fa2 # @v2.3.1 + if: always() + with: + name: ${{ env.artifacts_name }} + path: artifacts diff --git a/README.md b/README.md index cf01c3a..4823a8a 100644 --- a/README.md +++ b/README.md @@ -1 +1,108 @@ -# gha-ci \ No newline at end of file +# GitHub Actions - CI + +CI for Silverstripe modules + +Uses feature detection based on files in the root folder such as phpunit.xml.dist to build and run a dynamic matrix of jobs. + +It's highly recommended that you use a tagged version (e.g. v1) to ensure stability of your builds. If you have a relatively simple build that you have no intention of ever making more complex e.g. only phpunit tests using phpunit.xml.dist, then this is probably all you need for long term use. + +Note: Unlike other silverstripe/gha-* repositories, this one is a [reusable workflow](https://docs.github.com/en/actions/using-workflows/reusing-workflows) rather than an [action](https://docs.github.com/en/actions/creating-actions/creating-a-composite-action). A reusable workflow is required to create a `matrix`. + +### Usage + +Create the following file in your module, and substitute the tagged version for the most recent tag prefixed with a `v` e.g. `@v1` + +**.github/workflows/ci.yml** +```yml +name: CI + +on: + push: + pull_request: + workflow_dispatch: + +jobs: + ci: + uses: silverstripe/gha-ci/.github/workflows/ci.yml@main +``` + +Set config specific to your needs via "inputs" defined under the `with:` key. For instance, to disable PHP linting because your module does not yet have a `phpcs.xml.dist` file + +```yml +jobs: + ci: + uses: silverstripe/gha-ci/.github/workflows/ci.yml@main + with: + phplinting: false +``` + +#### Inputs: + +##### Extra composer requirements +Require additional modules through composer for matrix entries. You do not need to quote the string for multiple requirements +`composer_require_extra: silverstripe/widgets:^2 silverstripe/comments:^3` + +##### Simple matrix +Create a smaller matrix with only the lowest supported PHP and MySQL versions instead of a full matrix of multiple PHP and database versions. Default is false, enable with: +`simple_matrix: true` + +##### Dynamic matrix +Dynamically generate a matrix using feature detection. If disabled, jobs must be defined using the extra_jobs input. Default is true, disable with: +`dynamic_matrix: false` + +##### PHPUnit tests +Runs PHPunit if the `phpunit.xml` or `phpunit.xml.dist` config file is available. Default is true, disable PHPunit tests with: +`phpunit: false` + +##### PHP linting +Runs phpcs and phpstan if the `phpcs.xml.dist` or `phpstan.neon.dist` config files are available. Default is true, disable with: +`phplinting: false` + +##### PHP coverage +Run codecov, which does not require a configuration file. Default is false, though modules on the silverstripe account will always have this enabled. Enable with: +`phpcoverage: true` + +##### End-to-end tests +Runs behat tests if `behat.yml` is available. Default is true, disable with: +`endtoend: false` + +##### JS tests +Runs `yarn lint`, `yarn test` and `yarn build` + `git diff-files` + `git diff` if the package.json file is available. Default is true, disable JS tests with: +`js: false` + +##### Extra jobs +Define specific test combinations (e.g. php and db versions). All inputs are false by default so you only need to include config for the input(s) you want enabled. All inputs except `dynamic_matrix` and `simple_matrix` are available in this context. There are also some additional inputs specific to the extra_jobs input (see below). Use a multi-line yml string +```yml +extra_jobs: | + - php: '8.0' + db: pgsql + phpunit: true + phpunit_suite: api + - php: '8.1' + endtoend: true + endtoend_suite: admin + endtoend_config: vendor/silverstripe/admin/behat.yml +``` + +##### Extra jobs - additional inputs +Additional inputs that can be applied to individual jobs within the extra_jobs input + +###### php +The version of PHP to run e.g. 8.1 + +###### db +The database to run e.g. mysql80 + +For the list of supported databases see [the gha-generate-matrix script](https://github.com/silverstripe/gha-generate-matrix/blob/main/job_creator.php) + +###### name_suffix +Add a specific suffix to job and artifact names to make them easier to identify. This will be trucated if greater than 20 characters. Must match the regex `[a-zA-Z0-9_\- ]` + +###### phpunit_suite +The PHPUnit suite to run as defined in `phpunit.xml`. Translates to the `--testsuite` parameter passed to `phpunit`. Specify 'all' to run all PHPunit suites available in the context of this module/recipe + +###### endtoend_suite +The end-to-end suite as defined in `behat.yml`. Must be defined, specify 'root' to run the default suite + +###### endtoend_config +The `behat.yml` config file to use if not using the root `behat.yml`. Only used if running behat tests in a different module