From 84534e45bc8275e0ba8183a9248a6daff39e6dae Mon Sep 17 00:00:00 2001 From: Steve Boyd Date: Tue, 31 May 2022 13:29:46 +1200 Subject: [PATCH 1/2] NEW Create workflow --- .github/workflows/ci.yml | 453 +++++++++++++++++++++++++++++++++++++++ README.md | 77 ++++++- 2 files changed, 529 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..33f8ab4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,453 @@ +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: '' + # simple matrix will only run a single php 7.4 mysql 5.7 job instead of a full matrix + # TODO: change to matrix_options - 'simple disabled' space separated, allows for future config + # disabled would have no auto generated and only output extra_jobs + simple_matrix: + type: boolean + default: false + endtoend: + type: boolean + default: true + phpcoverage: + type: boolean + # modules on silverstripe account will ignore this and always run codecov + default: false + phplinting: + type: boolean + default: true + phpunit: + type: boolean + default: true + js: + type: boolean + default: true + +jobs: + + 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_HEAD_REF: ${{ github.head_ref }} + 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 + echo "github.ref_name: ${{ github.ref_name }}" + # githbub.repository - The owner and repository name. For example, Codertocat/Hello-World + # TODO: ensure this is the target account/repo in a pull-request + echo "$github.repository: ${{ github.repository }}" + + genmatrix: + name: Generate matrix + runs-on: ubuntu-latest + 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 }} + + tests: + needs: genmatrix + + strategy: + # set fail-fast to false prevent one job from cancelling other jobs + fail-fast: false + matrix: ${{fromJson(needs.genmatrix.outputs.matrix)}} + + runs-on: ubuntu-latest + + services: + 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 + # Takes around 9 seconds per extra database added, so this is something we could optimise later + 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: >- + PHP ${{ matrix.php }} + ${{ matrix.composer_args == '--prefer-lowest' && 'prf-low' || '' }} + ${{ matrix.db }} + ${{ matrix.phpunit == 'true' && 'phpunit' || '' }} + ${{ matrix.phpunit == 'true' && matrix.phpunit_suite || '' }} + ${{ matrix.endtoend == 'true' && 'endtoend' || '' }} + ${{ matrix.endtoend == 'true' && matrix.endtoend_suite || '' }} + ${{ matrix.js == 'true' && 'js' || '' }} + ${{ matrix.phpcoverage == 'true' && 'phpcoverage' || '' }} + ${{ matrix.phplinting == 'true' && 'phplinting' || '' }} + ${{ matrix.name_suffix }} + + name: >- + PHP ${{ matrix.php }} + ${{ matrix.composer_args == '--prefer-lowest' && 'prf-low' || '' }} + ${{ matrix.db }} + ${{ matrix.phpunit == 'true' && 'phpunit' || '' }} + ${{ matrix.phpunit == 'true' && matrix.phpunit_suite || '' }} + ${{ matrix.endtoend == 'true' && 'endtoend' || '' }} + ${{ matrix.endtoend == 'true' && matrix.endtoend_suite || '' }} + ${{ matrix.js == 'true' && 'js' || '' }} + ${{ matrix.phpcoverage == 'true' && 'phpcoverage' || '' }} + ${{ matrix.phplinting == 'true' && 'phplinting' || '' }} + ${{ matrix.name_suffix }} + + steps: + - name: Checkout code + uses: actions/checkout@7884fcad6b5d53d10323aee724dc68d8b9096a2e # @v2 + + - name: Install PHP + # SHA will need to be updated to support new php version when they are released + uses: shivammathur/setup-php@6cba851606e69e00775ebccba68bc1d418b6fa44 # @v2 + 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=6144M + # Assign less memory for behat tests so that chrome has plenty of memory available + if [ "${{ matrix.endtoend }}" == "true" ]; then + PHP_MEMORY_LIMIT=4096M + 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 + 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 + cat << EOF > __dir.conf + + DirectoryIndex index.php index.html index.cgi index.pl index.xhtml index.htm + + EOF + sudo cp __dir.conf /etc/apache2/mods-enabled/dir.conf + rm __dir.conf + # this script will create a 000-default.conf file with the pwd as the DocumentRoot + cat << EOF > __000-default.conf + + ServerAdmin webmaster@localhost + DocumentRoot PWD + + AllowOverride All + Require all granted + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + + EOF + cat << EOF > __apache2.php + > /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 + with: + path: ~/.cache/composer + key: shared-composer-cache + + - name: Composer + env: + INPUTS_COMPOSER_REQUIRE_EXTRA: ${{ inputs.composer_require_extra }} + MATRIX_COMPOSER_REQUIRE_EXTRA: ${{ matrix.composer_require_extra }} + run: | + # Update composer.json and install dependencies + # github.base_ref is only available on pull-requests events is the target branch - is is NOT prefixed with refs/heads/ + # 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 << TODO: confirm + # https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability + + # Note: $BRANCH will be tag for tag events + BRANCH=$(php -r "echo preg_replace('#^pulls/([0-9\.]+)/.+\$#', '\$1', '${{ github.base_ref }}'?:'${{ github.ref_name }}');") + if [[ "$BRANCH" =~ ^[1-9]$ ]] || [[ "$BRANCH" =~ ^[1-9]\.[0-9]+$ ]]; then export COMPOSER_ROOT_VERSION="${BRANCH}.x-dev"; elif [[ "$BRANCH" =~ ^[1-9]\.[0-9]+\.[0-9]+ ]]; then export COMPOSER_ROOT_VERSION="${BRANCH}"; else export COMPOSER_ROOT_VERSION="dev-${BRANCH}"; fi + echo "BRANCH is $BRANCH" + echo "COMPOSER_ROOT_VERSION is $COMPOSER_ROOT_VERSION" + + # Detect if using phpunit9 + # If so, then ensure sminnee phpunit5 modules do not get installed + PHPUNIT9=$(php -r '$rd=json_decode(file_get_contents("composer.json"))->{"require-dev"};$pu=$rd->{"phpunit/phpunit"}??"";$rt=$rd->{"silverstripe/recipe-testing"}??"";echo ($pu=="^9"||$pu=="^9.5"||$rt=="^2")?1:0;') + echo "PHPUNIT9 is $PHPUNIT9" + if [ "$PHPUNIT9" == "1" ]; then + php -r "\$j=json_decode(file_get_contents('composer.json'));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));" + fi + + # 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 assets unit tests as well as recipes that run 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 + # TODO: test that requiring multiple modules seperated with a space here works + composer require $INPUTS_COMPOSER_REQUIRE_EXTRA --no-update + fi + if [ "$MATRIX_COMPOSER_REQUIRE_EXTRA" != "" ]; then + # TODO: test that requiring multiple modules seperated with a space here works + composer require $MATRIX_COMPOSER_REQUIRE_EXTRA --no-update + fi + + # Ensure composer.json has prefer-stable true and minimum-stability dev + php -r '$j=json_decode(file_get_contents("composer.json"));$j->{"prefer-stable"}=true;$j->{"minimum-stability"}="dev";file_put_contents("composer.json",json_encode($j,192));' + + # 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 + + # Installing using --prefer-source for recipes + some modules that run other unit-tests in other modules + # matrix.composer_args is sometimes --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 --prefer-source --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 'foreach (["silverstripe", "cwp", "symbiote", "dnadesign", "tractorcow", "bringyourownideas", "colymba"] 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 + 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 + mkdir artifacts + + # run dev/build flush to help debug any issues (though it's not strictly required here) + # travis-shared has no reference to sake, so it should be safe to omit dev/build flush=1 + # 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 }} + endtoend: ${{ matrix.endtoend }} + endtoend_suite: ${{ matrix.endtoend_suite }} + endtoend_config: ${{ matrix.endtoend_config }} + js: ${{ matrix.js }} + phplinting: ${{ matrix.phplinting }} + phpcoverage: ${{ matrix.phpcoverage }} + + - 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 + + - name: Upload artifacts + uses: actions/upload-artifact@82c141cc518b40d92cc801eee768e7aafc9c2fa2 # @v2 + if: always() + with: + name: ${{ env.artifacts_name }} + path: artifacts diff --git a/README.md b/README.md index cf01c3a..8d65046 100644 --- a/README.md +++ b/README.md @@ -1 +1,76 @@ -# gha-ci \ No newline at end of file +# GitHub Actions - CI + +CI used by Silverstripe modules + +Will use feature detection based on files in the root folder such as phpunit.xml.dist to build dynmaic matrix of tests to run + +It's highly recommended that you use a tagged version (e.g. v0.2) 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. + +This repository is currently in development and code on the `main` branch could change at any time, including taking on a whole new direction. It's expected that new functionality will be added. + +Note: Unlike other silverstripe/gha-* repositiories, 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`. + +### CI Usage + +Create the following file in your module + +(subsitute the tagged version for the most recent tag from this module) + +**.github/workflows/ci.yml** +```yml +name: CI + +on: + push: + pull_request: + workflow_dispatch: + +jobs: + ci: + uses: silverstripe/ghi-ci/.github/workflows/ci.yml@main +``` + +Use the following if your module does not have a `phpcs.xml.dist` file + +(or better still, copy paste this [sample phpcs.xml.dist](https://raw.githubusercontent.com/silverstripe/silverstripe-elemental/4/phpcs.xml.dist) file in to your module) + +```yml +jobs: + ci: + uses: silverstripe/github-actions-ci-cd/.github/workflows/ci.yml@v0.2 + with: + phplinting: false +``` + +#### Some other "with" options + +Extra composer requirements +You do not need to quote the string for multiple requirements +`composer_require_extra: silverstripe/widgets:^2 silverstripe/comments:^3` + +Simple matrix - php 7.4 with mysql 5.7 only +`simple_matrix: true` + +Enable php coverage (codecov - no feature detection) +Modules on the silverstripe account will automaticaly have this enabled +`phpcoverage: true` + +Disable end-to-end tests (behat.yml): +`endtoend: false` + +Disable JS tests (package.json - yarn lint, test and build diff): +`js: false` + +Disable phpunit tests (phpunit.xml.dist / phpunit.xml) +`phpunit: false` + +Disable php linting (phpcs.xml.dist, phpstan.neon.dist) +`phplinting: false` + +Extra jobs +Define php version and/or db +```yml +extra_jobs: | + - php: '8.0' + endtoend: true +``` From 45899cbb3f336a26d654a1282b178a6747d717c0 Mon Sep 17 00:00:00 2001 From: Steve Boyd Date: Tue, 31 May 2022 16:07:19 +1200 Subject: [PATCH 2/2] MNT Add auto-tag workflow --- .github/workflows/auto-tag.yml | 12 ++ .github/workflows/ci.yml | 282 +++++++++++++++++++-------------- README.md | 94 +++++++---- 3 files changed, 239 insertions(+), 149 deletions(-) create mode 100644 .github/workflows/auto-tag.yml 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 index 33f8ab4..7b2f47a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,9 +1,8 @@ -name: ci - +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 must be multi-line string, as there's no support for `type: array` for inputs extra_jobs: type: string required: false @@ -12,9 +11,9 @@ on: type: string required: false default: '' - # simple matrix will only run a single php 7.4 mysql 5.7 job instead of a full matrix - # TODO: change to matrix_options - 'simple disabled' space separated, allows for future config - # disabled would have no auto generated and only output extra_jobs + dynamic_matrix: + type: boolean + default: true simple_matrix: type: boolean default: false @@ -23,7 +22,6 @@ on: default: true phpcoverage: type: boolean - # modules on silverstripe account will ignore this and always run codecov default: false phplinting: type: boolean @@ -37,32 +35,39 @@ on: 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 }}" + 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. + # 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 - echo "github.ref_name: ${{ github.ref_name }}" - # githbub.repository - The owner and repository name. For example, Codertocat/Hello-World - # TODO: ensure this is the target account/repo in a pull-request - echo "$github.repository: ${{ github.repository }}" + 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 }} @@ -79,17 +84,21 @@ jobs: 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 job from cancelling other jobs + # 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: @@ -99,7 +108,6 @@ jobs: ports: - 3357:3306 options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 - # Takes around 9 seconds per extra database added, so this is something we could optimise later mysql80: image: mysql:8.0 env: @@ -118,39 +126,18 @@ jobs: - 5432:5432 env: - artifacts_name: >- - PHP ${{ matrix.php }} - ${{ matrix.composer_args == '--prefer-lowest' && 'prf-low' || '' }} - ${{ matrix.db }} - ${{ matrix.phpunit == 'true' && 'phpunit' || '' }} - ${{ matrix.phpunit == 'true' && matrix.phpunit_suite || '' }} - ${{ matrix.endtoend == 'true' && 'endtoend' || '' }} - ${{ matrix.endtoend == 'true' && matrix.endtoend_suite || '' }} - ${{ matrix.js == 'true' && 'js' || '' }} - ${{ matrix.phpcoverage == 'true' && 'phpcoverage' || '' }} - ${{ matrix.phplinting == 'true' && 'phplinting' || '' }} - ${{ matrix.name_suffix }} - - name: >- - PHP ${{ matrix.php }} - ${{ matrix.composer_args == '--prefer-lowest' && 'prf-low' || '' }} - ${{ matrix.db }} - ${{ matrix.phpunit == 'true' && 'phpunit' || '' }} - ${{ matrix.phpunit == 'true' && matrix.phpunit_suite || '' }} - ${{ matrix.endtoend == 'true' && 'endtoend' || '' }} - ${{ matrix.endtoend == 'true' && matrix.endtoend_suite || '' }} - ${{ matrix.js == 'true' && 'js' || '' }} - ${{ matrix.phpcoverage == 'true' && 'phpcoverage' || '' }} - ${{ matrix.phplinting == 'true' && 'phplinting' || '' }} - ${{ matrix.name_suffix }} + artifacts_name: ${{ matrix.name }} + + name: ${{ matrix.name }} steps: + - name: Checkout code - uses: actions/checkout@7884fcad6b5d53d10323aee724dc68d8b9096a2e # @v2 + 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@6cba851606e69e00775ebccba68bc1d418b6fa44 # @v2 + 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 @@ -166,36 +153,38 @@ jobs: - name: Configure PHP run: | # Set memory limit and disable xdebug if not running phpcoverage - if [ -z $(which php) ]; then + 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=6144M + 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=4096M + 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 + 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 + 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 + 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 @@ -203,7 +192,7 @@ jobs: 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 + 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 @@ -212,36 +201,26 @@ jobs: run: | # apache2 is installed and running by default in ubuntu # update dir.conf to use index.php as the primary index doc - cat << EOF > __dir.conf + DIR_CONF=$(cat << EOF - DirectoryIndex index.php index.html index.cgi index.pl index.xhtml index.htm + DirectoryIndex index.php index.html index.cgi index.pl index.xhtml index.htm - EOF - sudo cp __dir.conf /etc/apache2/mods-enabled/dir.conf - rm __dir.conf - # this script will create a 000-default.conf file with the pwd as the DocumentRoot - cat << EOF > __000-default.conf + 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 - + DocumentRoot $(pwd) + AllowOverride All Require all granted ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined - EOF - cat << EOF > __apache2.php - /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" @@ -253,79 +232,117 @@ jobs: # 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 + 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 }} - run: | - # Update composer.json and install dependencies + # 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 << TODO: confirm - # https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability - - # Note: $BRANCH will be tag for tag events - BRANCH=$(php -r "echo preg_replace('#^pulls/([0-9\.]+)/.+\$#', '\$1', '${{ github.base_ref }}'?:'${{ github.ref_name }}');") - if [[ "$BRANCH" =~ ^[1-9]$ ]] || [[ "$BRANCH" =~ ^[1-9]\.[0-9]+$ ]]; then export COMPOSER_ROOT_VERSION="${BRANCH}.x-dev"; elif [[ "$BRANCH" =~ ^[1-9]\.[0-9]+\.[0-9]+ ]]; then export COMPOSER_ROOT_VERSION="${BRANCH}"; else export COMPOSER_ROOT_VERSION="dev-${BRANCH}"; fi - echo "BRANCH is $BRANCH" + # 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" - # Detect if using phpunit9 - # If so, then ensure sminnee phpunit5 modules do not get installed - PHPUNIT9=$(php -r '$rd=json_decode(file_get_contents("composer.json"))->{"require-dev"};$pu=$rd->{"phpunit/phpunit"}??"";$rt=$rd->{"silverstripe/recipe-testing"}??"";echo ($pu=="^9"||$pu=="^9.5"||$rt=="^2")?1:0;') - echo "PHPUNIT9 is $PHPUNIT9" - if [ "$PHPUNIT9" == "1" ]; then - php -r "\$j=json_decode(file_get_contents('composer.json'));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));" - fi + # 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 + if [[ "${{ matrix.installer_version }}" != "" ]]; then composer require silverstripe/installer:${{ matrix.installer_version }} --no-update fi - # Required for assets unit tests as well as recipes that run assets unit tests + # 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 + if [[ "${{ matrix.db }}" == "pgsql" ]] && ! [[ $GITHUB_REPOSITORY =~ /postgresql ]]; then composer require silverstripe/postgresql:^2 --no-update fi - if [ "${{ matrix.endtoend }}" == "true" ]; then + if [[ "${{ matrix.endtoend }}" == "true" ]]; then composer require silverstripe/recipe-testing:^2 --dev --no-update fi - if [ "${{ matrix.phplinting }}" == "true" ]; then + if [[ "${{ matrix.phplinting }}" == "true" ]]; then composer require silverstripe/cow:dev-master --dev --no-update fi - if [ "$INPUTS_COMPOSER_REQUIRE_EXTRA" != "" ]; then - # TODO: test that requiring multiple modules seperated with a space here works + 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 - # TODO: test that requiring multiple modules seperated with a space here works + 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 - php -r '$j=json_decode(file_get_contents("composer.json"));$j->{"prefer-stable"}=true;$j->{"minimum-stability"}="dev";file_put_contents("composer.json",json_encode($j,192));' + # 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 - # Installing using --prefer-source for recipes + some modules that run other unit-tests in other modules - # matrix.composer_args is sometimes --prefer-lowest which is only supported by `composer update`, not `composer install` + # 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 --prefer-source --no-interaction --no-progress ${{ matrix.composer_args }} + composer update --no-interaction --no-progress ${{ matrix.composer_args }} # Useful to see what was installed composer show @@ -334,18 +351,42 @@ jobs: # 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 + 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 'foreach (["silverstripe", "cwp", "symbiote", "dnadesign", "tractorcow", "bringyourownideas", "colymba"] 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); } }' + 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 + 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 + 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 + if [[ -f vendor/silverstripe/graphql/tests/Middleware/MiddlewareProcessTestBase.php ]]; then rm vendor/silverstripe/graphql/tests/Middleware/MiddlewareProcessTestBase.php fi # Rebuild composer classloader @@ -355,8 +396,10 @@ jobs: - 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 + if [[ "${{ matrix.db }}" == "mysql57pdo" ]]; then cat << EOF > .env SS_DATABASE_CLASS="MySQLPDODatabase" SS_DATABASE_PORT="3357" @@ -396,16 +439,17 @@ jobs: 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) - # travis-shared has no reference to sake, so it should be safe to omit dev/build flush=1 # normal module - if [ -f vendor/bin/sake ]; then + if [[ -f vendor/bin/sake ]]; then vendor/bin/sake dev/build flush=1 fi # framework module - if [ -f sake ]; then + if [[ -f sake ]]; then ./sake dev/build flush=1 fi @@ -424,29 +468,31 @@ jobs: 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 }} - phplinting: ${{ matrix.phplinting }} - phpcoverage: ${{ matrix.phpcoverage }} - name: Copy artifacts if: always() run: | # Copy selected files to the artifacts dir - if [ -f composer.json ]; then + if [[ -f composer.json ]]; then cp composer.json artifacts fi - if [ -f composer.lock ]; then + if [[ -f composer.lock ]]; then cp composer.lock artifacts fi - if [ "${{ matrix.endtoend }}" == "true" ] && [ -f __behat.yml ]; then + 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 + uses: actions/upload-artifact@82c141cc518b40d92cc801eee768e7aafc9c2fa2 # @v2.3.1 if: always() with: name: ${{ env.artifacts_name }} diff --git a/README.md b/README.md index 8d65046..4823a8a 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,16 @@ # GitHub Actions - CI -CI used by Silverstripe modules +CI for Silverstripe modules -Will use feature detection based on files in the root folder such as phpunit.xml.dist to build dynmaic matrix of tests to run +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. v0.2) 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. +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. -This repository is currently in development and code on the `main` branch could change at any time, including taking on a whole new direction. It's expected that new functionality will be added. +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`. -Note: Unlike other silverstripe/gha-* repositiories, 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 -### CI Usage - -Create the following file in your module - -(subsitute the tagged version for the most recent tag from this module) +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 @@ -27,50 +23,86 @@ on: jobs: ci: - uses: silverstripe/ghi-ci/.github/workflows/ci.yml@main + uses: silverstripe/gha-ci/.github/workflows/ci.yml@main ``` -Use the following if your module does not have a `phpcs.xml.dist` file - -(or better still, copy paste this [sample phpcs.xml.dist](https://raw.githubusercontent.com/silverstripe/silverstripe-elemental/4/phpcs.xml.dist) file in to your module) +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/github-actions-ci-cd/.github/workflows/ci.yml@v0.2 + uses: silverstripe/gha-ci/.github/workflows/ci.yml@main with: phplinting: false ``` -#### Some other "with" options +#### Inputs: -Extra composer requirements -You do not need to quote the string for multiple requirements +##### 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 - php 7.4 with mysql 5.7 only +##### 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` -Enable php coverage (codecov - no feature detection) -Modules on the silverstripe account will automaticaly have this enabled +##### 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` -Disable end-to-end tests (behat.yml): +##### End-to-end tests +Runs behat tests if `behat.yml` is available. Default is true, disable with: `endtoend: false` -Disable JS tests (package.json - yarn lint, test and build diff): +##### 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` -Disable phpunit tests (phpunit.xml.dist / phpunit.xml) -`phpunit: false` - -Disable php linting (phpcs.xml.dist, phpstan.neon.dist) -`phplinting: false` - -Extra jobs -Define php version and/or db +##### 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