diff --git a/.github/workflows/ci-cd.yaml b/.github/workflows/ci-cd.yaml index 214caa1..65c305d 100644 --- a/.github/workflows/ci-cd.yaml +++ b/.github/workflows/ci-cd.yaml @@ -1,13 +1,30 @@ --- name: PHP CI/CD -on: [push, pull_request] +# Pushing to a PR branch used to fire this workflow twice, once for `push` +# and once for `pull_request`, with both runs created within seconds of each +# other. Ten macOS jobs then invoked setup-php simultaneously and raced over +# the shared runner tool cache, which is why identical commits produced +# opposite results: one run's macOS jobs passed while the other's all failed +# with "composer: command not found" after setup-php reported success. +# +# Restricting `push` to master means PR branches get exactly one run. +on: + push: + branches: + - master + pull_request: + +# Belt and braces for rapid successive pushes to the same branch. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true permissions: {} jobs: Unit_tests: runs-on: ${{ matrix.os }} - timeout-minutes: 10 + timeout-minutes: 30 strategy: fail-fast: false matrix: @@ -23,7 +40,9 @@ jobs: - uses: shivammathur/setup-php@2.32.0 with: php-version: ${{ matrix.php-version }} - tools: composer:v2 + # composer 2.3 raised its floor to PHP 7.2.5, so the 5.x jobs need + # the 2.2 LTS line. composer:v2 resolves to the newest 2.x. + tools: ${{ startsWith(matrix.php-version, '5.') && 'composer:2.2' || 'composer:v2' }} - uses: actions/checkout@v3 - name: Validate composer.json and composer.lock run: composer validate --strict @@ -32,9 +51,15 @@ jobs: - name: Run test suite run: vendor/bin/phpunit Integration_tests: - if: github.event_name == 'push' + # Previously `push` only, which covered PR branches because `push` fired + # for them. Now that `push` is master-only, the same coverage means master + # pushes plus same-repo pull requests. Fork PRs are excluded because they + # do not receive TINIFY_KEY and would fail on the missing secret. + if: >- + github.event_name == 'push' || + github.event.pull_request.head.repo.full_name == github.repository runs-on: ${{ matrix.os }} - timeout-minutes: 10 + timeout-minutes: 30 needs: Unit_tests strategy: fail-fast: false diff --git a/composer.phpstan.json b/composer.phpstan.json index 223a3cf..be4c190 100644 --- a/composer.phpstan.json +++ b/composer.phpstan.json @@ -4,6 +4,6 @@ "vendor-dir": "vendor-phpstan" }, "require": { - "phpstan/phpstan": "^2.1" + "phpstan/phpstan": "2.3.4" } } diff --git a/phpstan.neon b/phpstan.neon index d6b47fe..cf575f1 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -3,3 +3,17 @@ parameters: level: 5 paths: - lib + ignoreErrors: + # Client::request retries by decrementing $retries and every path + # through the loop body returns, continues or throws. A continue only + # happens while $retries > 0, so at the condition check $retries is + # always 0 or 1 and the bound is never what ends the loop. Both of the + # findings below follow from that and are deliberate: the bound states + # the retry budget, and the trailing throw guards against a future edit + # introducing a path that falls out of the loop. + - + identifier: greaterOrEqual.alwaysTrue + path: lib/Tinify/Client.php + - + identifier: deadCode.unreachable + path: lib/Tinify/Client.php