From c965eb0c2e88cb77a7ae9e8cd660c22b52df332f Mon Sep 17 00:00:00 2001 From: UndefinedOffset Date: Wed, 16 Feb 2022 15:19:42 -0400 Subject: [PATCH] Attempt at getting GitHub actions working --- .github/matchers/phpcs.json | 23 ++ .github/workflows/ci.yml | 312 ++++++++++++++++++ .../GitHubActionsAnnotatorPrinter.php | 112 +++++++ 3 files changed, 447 insertions(+) create mode 100644 .github/matchers/phpcs.json create mode 100644 .github/workflows/ci.yml create mode 100644 tests/Bootstrap/GitHubActionsAnnotatorPrinter.php diff --git a/.github/matchers/phpcs.json b/.github/matchers/phpcs.json new file mode 100644 index 0000000..786faa7 --- /dev/null +++ b/.github/matchers/phpcs.json @@ -0,0 +1,23 @@ +{ + "problemMatcher": [ + { + "owner": "phpcs", + "severity": "error", + "pattern": [ + { + "regexp": "^$", + "file": 1 + }, + { + "regexp": "+)$", + "line": 1, + "column": 2, + "severity": 3, + "message": 4, + "code": 5, + "loop": true + } + ] + } + ] +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..06d4e45 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,312 @@ +name: CI + +on: + push: + branches: [ '**' ] + paths-ignore: + - 'docs/**' + - '*.md' + pull_request: + branches: [ '**' ] + paths-ignore: + - 'docs/**' + - '*.md' + + + +jobs: + silverstripe_410_php74: + name: "Silverstripe 4.10 | PHP 7.4" + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')" + env: + php_version: 7.4 + php_extensions: ctype, dom, fileinfo, hash, intl, mbstring, session, simplexml, tokenizer, xml, pdo, mysqli, gd, zip + services: + mysql: + image: mysql:5.7 + env: + MYSQL_ALLOW_EMPTY_PASSWORD: false + MYSQL_ROOT_PASSWORD: testpassword + MYSQL_DATABASE: test_db + ports: + - 3306/tcp + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP Extension Cache + id: cache-env + uses: shivammathur/cache-extensions@v1 + with: + php-version: ${{ env.php_version }} + extensions: ${{ env.php_extensions }} + key: php74-ext-cache-${{ hashFiles('.github/workflows/ci.yml') }} + + - name: Cache PHP Extensions + uses: actions/cache@v2 + with: + path: ${{ steps.cache-env.outputs.dir }} + key: ${{ steps.cache-env.outputs.key }} + restore-keys: ${{ steps.cache-env.outputs.key }} + + - name: Setup PHP, with composer and extensions + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ env.php_version }} + extensions: ${{ env.php_extensions }} + ini-values: log_errors=On, error_log="${{github.workspace}}/artifacts/php_errors.log" + coverage: none + tools: composer:v1 + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-ss410-php74-${{ hashFiles('**/composer.json') }}-${{ hashFiles('.github/workflows/ci.yml') }} + restore-keys: ${{ runner.os }}-composer-ss410-php74- + + - name: Install Composer dependencies + env: + GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + composer self-update --ansi || true + if [[ $GITHUB_ACCESS_TOKEN ]]; then composer config --ansi -g github-oauth.github.com $GITHUB_ACCESS_TOKEN; fi + composer require silverstripe/admin 1.10.* --no-update + composer require silverstripe/versioned 1.10.* + composer install --verbose --no-interaction --no-progress --no-suggest --optimize-autoloader --ansi + + - name: Configure Environment + run: | + cp tests/utils/actions.env.template .env + mkdir artifacts + + - name: Perform PHPUnit Tests + env: + SS_DATABASE_PORT: ${{ job.services.mysql.ports['3306'] }} + run: vendor/bin/phpunit --colors=always --printer UndefinedOffset\\SortableGridField\\Tests\\Bootstrap\\GitHubActionsAnnotatorPrinter + + silverstripe_410_php80: + name: "Silverstripe 4.10 | PHP 8.0" + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')" + env: + php_version: 8.0 + php_extensions: ctype, dom, fileinfo, hash, intl, mbstring, session, simplexml, tokenizer, xml, pdo, mysqli, gd, zip + services: + mysql: + image: mysql:5.7 + env: + MYSQL_ALLOW_EMPTY_PASSWORD: false + MYSQL_ROOT_PASSWORD: testpassword + MYSQL_DATABASE: test_db + ports: + - 3306/tcp + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP Extension Cache + id: cache-env + uses: shivammathur/cache-extensions@v1 + with: + php-version: ${{ env.php_version }} + extensions: ${{ env.php_extensions }} + key: php80-ext-cache-${{ hashFiles('.github/workflows/ci.yml') }} + + - name: Cache PHP Extensions + uses: actions/cache@v2 + with: + path: ${{ steps.cache-env.outputs.dir }} + key: ${{ steps.cache-env.outputs.key }} + restore-keys: ${{ steps.cache-env.outputs.key }} + + - name: Setup PHP, with composer and extensions + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ env.php_version }} + extensions: ${{ env.php_extensions }} + ini-values: log_errors=On, error_log="${{github.workspace}}/artifacts/php_errors.log" + coverage: none + tools: composer:v2 + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-ss410-php80-${{ hashFiles('**/composer.json') }}-${{ hashFiles('.github/workflows/ci.yml') }} + restore-keys: ${{ runner.os }}-composer-ss410-php80- + + - name: Install Composer dependencies + env: + GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + composer self-update --ansi || true + if [[ $GITHUB_ACCESS_TOKEN ]]; then composer config --ansi -g github-oauth.github.com $GITHUB_ACCESS_TOKEN; fi + composer require silverstripe/admin 1.10.* --no-update + composer require silverstripe/versioned 1.10.* + composer install --verbose --no-interaction --no-progress --no-suggest --optimize-autoloader --ansi + + - name: Configure Environment + run: | + cp tests/utils/actions.env.template .env + mkdir artifacts + + - name: Perform PHPUnit Tests + env: + SS_DATABASE_PORT: ${{ job.services.mysql.ports['3306'] }} + run: vendor/bin/phpunit --colors=always --printer UndefinedOffset\\SortableGridField\\Tests\\Bootstrap\\GitHubActionsAnnotatorPrinter + + silverstripe_49_php74: + name: "Silverstripe 4.9 | PHP 7.4" + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')" + env: + php_version: 7.4 + php_extensions: ctype, dom, fileinfo, hash, intl, mbstring, session, simplexml, tokenizer, xml, pdo, mysqli, gd, zip + services: + mysql: + image: mysql:5.7 + env: + MYSQL_ALLOW_EMPTY_PASSWORD: false + MYSQL_ROOT_PASSWORD: testpassword + MYSQL_DATABASE: test_db + ports: + - 3306/tcp + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP Extension Cache + id: cache-env + uses: shivammathur/cache-extensions@v1 + with: + php-version: ${{ env.php_version }} + extensions: ${{ env.php_extensions }} + key: php74-ext-cache-${{ hashFiles('.github/workflows/ci.yml') }} + + - name: Cache PHP Extensions + uses: actions/cache@v2 + with: + path: ${{ steps.cache-env.outputs.dir }} + key: ${{ steps.cache-env.outputs.key }} + restore-keys: ${{ steps.cache-env.outputs.key }} + + - name: Setup PHP, with composer and extensions + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ env.php_version }} + extensions: ${{ env.php_extensions }} + ini-values: log_errors=On, error_log="${{github.workspace}}/artifacts/php_errors.log" + coverage: none + tools: composer:v1 + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-ss49-php74-${{ hashFiles('**/composer.json') }}-${{ hashFiles('.github/workflows/ci.yml') }} + restore-keys: ${{ runner.os }}-composer-ss49-php74- + + - name: Install Composer dependencies + env: + GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + composer self-update --ansi || true + if [[ $GITHUB_ACCESS_TOKEN ]]; then composer config --ansi -g github-oauth.github.com $GITHUB_ACCESS_TOKEN; fi + composer require silverstripe/admin 1.9.* --no-update + composer require silverstripe/versioned 1.9.* + composer install --verbose --no-interaction --no-progress --no-suggest --optimize-autoloader --ansi + + - name: Configure Environment + run: | + cp tests/utils/actions.env.template .env + mkdir artifacts + + - name: Perform PHPUnit Tests + env: + SS_DATABASE_PORT: ${{ job.services.mysql.ports['3306'] }} + run: vendor/bin/phpunit --colors=always --printer UndefinedOffset\\SortableGridField\\Tests\\Bootstrap\\GitHubActionsAnnotatorPrinter + + phpcs: + name: "PHP_CodeSniffer" + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')" + env: + php_version: 7.4 + php_extensions: ctype, dom, fileinfo, hash, intl, mbstring, session, simplexml, tokenizer, xml, pdo, mysqli, gd, zip + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP Extension Cache + id: cache-env + uses: shivammathur/cache-extensions@v1 + with: + php-version: ${{ env.php_version }} + extensions: ${{ env.php_extensions }} + key: php74-ext-cache-${{ hashFiles('.github/workflows/ci.yml') }} + + - name: Cache PHP Extensions + uses: actions/cache@v2 + with: + path: ${{ steps.cache-env.outputs.dir }} + key: ${{ steps.cache-env.outputs.key }} + restore-keys: ${{ steps.cache-env.outputs.key }} + + - name: Setup PHP, with composer and extensions + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ env.php_version }} + extensions: ${{ env.php_extensions }} + ini-values: log_errors=On, error_log="${{github.workspace}}/artifacts/php_errors.log" + coverage: none + tools: composer:v2 + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache composer dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-phpcs-${{ hashFiles('.github/workflows/ci.yml') }} + restore-keys: ${{ runner.os }}-composer-phpcs- + + - name: Install Composer dependencies + env: + GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + rm composer.json + composer self-update --ansi || true + if [[ $GITHUB_ACCESS_TOKEN ]]; then composer config --ansi -g github-oauth.github.com $GITHUB_ACCESS_TOKEN; fi + composer require squizlabs/php_codesniffer ~3.4 + composer install --verbose --no-interaction --no-progress --no-suggest --optimize-autoloader --ansi + mkdir artifacts + + - name: Validate Code Style + run: vendor/bin/phpcs --colors --report=full --report-checkstyle=artifacts/phpcs.xml + + - name: Annotate Code Style Issues + if: failure() + run: (test -f artifacts/phpcs.xml && echo "::add-matcher::.github/matchers/phpcs.json" && cat artifacts/phpcs.xml && echo "::remove-matcher owner=phpcs::") diff --git a/tests/Bootstrap/GitHubActionsAnnotatorPrinter.php b/tests/Bootstrap/GitHubActionsAnnotatorPrinter.php new file mode 100644 index 0000000..3d26291 --- /dev/null +++ b/tests/Bootstrap/GitHubActionsAnnotatorPrinter.php @@ -0,0 +1,112 @@ +currentType = $type; + + parent::printDefects($defects, $type); + } + + /** + * Handles printing of a single defect + * @param TestFailure $defect Test Failure Object + * @param int $count Current position + */ + protected function printDefect(TestFailure $defect, int $count): void + { + parent::printDefect($defect, $count); + + $this->printGitHubAnnotation($defect); + } + + + /** + * Prints a GitHub Annotation Command + * @param TestFailure $defect Defect to print + */ + protected function printGitHubAnnotation(TestFailure $defect) + { + $e = $defect->thrownException(); + + $errorLines = array_filter( + explode("\n", (string) $e), + function ($l) { + return $l; + } + ); + + $error = end($errorLines); + $lineIndex = strrpos($error, ":"); + $path = substr($error, 0, $lineIndex); + $line = substr($error, $lineIndex + 1); + + if (!$path) { + list($path, $line) = $this->getReflectionFromTest( + $defect->getTestName() + ); + } + + $message = explode("\n", trim($defect->getTestName() . "\n\n" . (string) $e)); + $message = implode('%0A', $message); + + $type = $this->getCurrentType(); + $file = "file={$this->relativePath($path)}"; + $line = "line={$line}"; + $this->write("::{$type} $file,$line::{$message}\n"); + } + + /** + * Gets the current type of the defect + * @return string + */ + protected function getCurrentType() + { + if (in_array($this->currentType, ['error', 'failure'])) { + return 'error'; + } + + return 'warning'; + } + + /** + * Gets the relative path to the file + * @param string $path Path to make relative + * @return string + */ + protected function relativePath($path) + { + $relative = str_replace(getcwd() . DIRECTORY_SEPARATOR, '', $path); + + //Normalize + $relative = str_replace('\\', '/', $relative); + + return $relative; + } + + /** + * Gets the file name and start line for the test + * @param string $name Name of the test + * @return array + */ + protected function getReflectionFromTest($name) + { + list($klass, $method) = explode('::', $name); + $c = new \ReflectionClass($klass); + $m = $c->getMethod($method); + + return [$m->getFileName(), $m->getStartLine()]; + } +}