ci: invert the gate's path list, matching the other five SDKs #165
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Tests | |
| on: | |
| push: | |
| paths: | |
| - '**.php' | |
| - '.github/workflows/run-tests.yml' | |
| - 'phpunit.xml.dist' | |
| - 'composer.json' | |
| - 'composer.lock' | |
| # Without this, the suite never runs on a pull request: it only fired after a | |
| # merge, so a PR could be merged with nothing having tested it, and no test | |
| # check existed for branch protection to require. | |
| # | |
| # And no `paths:` filter here, deliberately. Branch protection requires the | |
| # four `P8.x - prefer-stable - ubuntu-latest` contexts on every pull request. | |
| # A filter means a pull request touching no PHP file never runs this workflow, | |
| # so those contexts never report and the pull request stays BLOCKED forever | |
| # with nothing to fix. That happened to #91 and #92, which both needed an | |
| # admin merge. A required check that cannot run on some pull requests is a | |
| # contradiction: either it runs on all of them, or it is not required. | |
| pull_request: | |
| jobs: | |
| # A required status check can never be conditional: the list is static. And a | |
| # matrix job that GitHub skips does NOT expand its name -- it reports one check | |
| # called `P${{ matrix.php }} - ...`, and zero `P8.x` contexts. Measured on #95. | |
| # So the matrix contexts cannot be both required and skippable, which is why | |
| # `Tests passed` below is the context branch protection requires instead. | |
| # | |
| # With that in place this job is free to decide whether the suite applies, and a | |
| # documentation-only pull request costs one short job rather than seventeen. | |
| changes: | |
| name: Decide whether the PHP suite applies | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 2 | |
| outputs: | |
| php: ${{ steps.filter.outputs.php }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: Detect PHP-relevant changes | |
| id: filter | |
| env: | |
| BASE: ${{ github.event.pull_request.base.sha }} | |
| HEAD: ${{ github.event.pull_request.head.sha }} | |
| run: | | |
| # Only a pull request can be documentation-only here. A push reaches | |
| # this workflow through the paths filter above, so by then the change is | |
| # already PHP-relevant. | |
| if [ "${{ github.event_name }}" != "pull_request" ]; then | |
| echo "php=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| # Every path out of here that is not a confident "false" says true. | |
| # A gate that cannot see both ends of the diff must not conclude that | |
| # nothing PHP changed. | |
| for sha in "$BASE" "$HEAD"; do | |
| if ! git cat-file -e "$sha^{commit}" 2>/dev/null; then | |
| echo "php=true" >> "$GITHUB_OUTPUT" | |
| echo "Cannot reach $sha, so running the suite rather than guessing." | |
| exit 0 | |
| fi | |
| done | |
| # Three dots: the diff from the merge base, not from whatever main has | |
| # moved to since. Two dots would call every file main gained a change | |
| # of this pull request. | |
| files=$(git diff --name-only "$BASE...$HEAD") | |
| echo "changed files:" | |
| printf '%s\n' "$files" | sed 's/^/ /' | |
| # Inverted deliberately, and matching the other five SDKs. This lists | |
| # what DOCUMENTATION looks like and runs the suite for everything else. | |
| # The first version enumerated PHP-relevant paths instead, which meant | |
| # the suite skipped for any file nobody had listed -- a new config | |
| # file, a new source directory -- and skipping is the answer that | |
| # produces a green over untested code. | |
| if [ -n "$files" ] && ! printf '%s\n' "$files" | grep -qvE '(^docs/|\.md$)'; then | |
| echo "php=false" >> "$GITHUB_OUTPUT" | |
| echo "Documentation only: the suite skips, and Tests passed says so." | |
| else | |
| echo "php=true" >> "$GITHUB_OUTPUT" | |
| echo "Something other than documentation changed: running the suite." | |
| fi | |
| test: | |
| needs: changes | |
| # `!= 'false'` and not `== 'true'`, deliberately. If the gate job fails, its | |
| # output is empty, and the safe reading of empty is RUN THE SUITE. The other | |
| # polarity would turn a broken gate into a green check over untested code. | |
| # `Tests passed` enforces the same thing from the other side. | |
| if: needs.changes.outputs.php != 'false' | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 5 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, windows-latest] | |
| php: [8.5, 8.4, 8.3, 8.2] | |
| stability: [prefer-lowest, prefer-stable] | |
| name: P${{ matrix.php }} - ${{ matrix.stability }} - ${{ matrix.os }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v7 | |
| - name: Setup PHP | |
| uses: shivammathur/setup-php@v2 | |
| with: | |
| php-version: ${{ matrix.php }} | |
| extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo, xdebug | |
| coverage: xdebug | |
| - name: Setup problem matchers | |
| run: | | |
| echo "::add-matcher::${{ runner.tool_cache }}/php.json" | |
| echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" | |
| - name: Install dependencies | |
| run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction | |
| - name: List Installed Dependencies | |
| run: composer show -D | |
| - name: Execute tests | |
| run: vendor/bin/phpunit --testsuite Unit --coverage-clover coverage.xml | |
| - name: Upload coverage to Codecov | |
| # Only upload coverage from one canonical job to avoid merge issues | |
| # with platform-specific tests that skip on Windows vs Unix | |
| if: matrix.php == '8.4' && matrix.stability == 'prefer-stable' && matrix.os == 'ubuntu-latest' | |
| uses: codecov/codecov-action@v7 | |
| env: | |
| CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} | |
| integration: | |
| needs: changes | |
| if: github.event_name == 'pull_request' && needs.changes.outputs.php != 'false' | |
| name: Live API integration contracts | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v7 | |
| - name: Setup PHP | |
| uses: shivammathur/setup-php@v2 | |
| with: | |
| php-version: 8.4 | |
| extensions: dom, curl, libxml, mbstring | |
| coverage: none | |
| - name: Install dependencies | |
| run: composer install --prefer-dist --no-interaction | |
| - name: Execute authenticated integration contracts | |
| env: | |
| MARKETDATA_TOKEN: ${{ secrets.MARKETDATA_TOKEN }} | |
| run: vendor/bin/phpunit --no-coverage --testsuite Integration --group ci | |
| tests-passed: | |
| # The single context branch protection should require, replacing the four | |
| # `P8.x - prefer-stable - ubuntu-latest` entries. Those cannot do the job: a | |
| # skipped matrix job reports no per-combination context at all, so requiring | |
| # them means a pull request that touches no PHP file waits forever for checks | |
| # that will never arrive. #91 and #92 both needed an admin merge for that. | |
| # | |
| # This job always reports, so it is always a decision rather than a silence. | |
| name: Tests passed | |
| needs: [changes, test] | |
| if: always() | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 2 | |
| steps: | |
| - name: Require the suite to have passed, or to have been skipped for cause | |
| env: | |
| PHP_RELEVANT: ${{ needs.changes.outputs.php }} | |
| TEST_RESULT: ${{ needs.test.result }} | |
| run: | | |
| echo "gate said php=${PHP_RELEVANT:-<nothing: the gate itself failed>}" | |
| echo "matrix result: ${TEST_RESULT}" | |
| case "$TEST_RESULT" in | |
| success) | |
| echo "The suite ran and passed." | |
| ;; | |
| skipped) | |
| # A skip is acceptable ONLY because the gate said so out loud. If | |
| # the gate failed, its output is empty, the matrix skipped for the | |
| # wrong reason, and a green here would cover untested code. | |
| if [ "$PHP_RELEVANT" = "false" ]; then | |
| echo "No PHP-relevant file changed, so the suite did not apply." | |
| else | |
| echo "The suite was skipped and the gate did not ask for it." | |
| exit 1 | |
| fi | |
| ;; | |
| *) | |
| echo "The suite reported: ${TEST_RESULT}" | |
| exit 1 | |
| ;; | |
| esac |