😥

GitHub Actions で PHPUnit カバレッジレポートがアップロードできなくて無駄にハマった

に公開

はじめに

PHPUnit のカバレッジレポートを GitHub Actions で生成・アップロードしようとした時に無駄にハマってしまったので、似たような事態に遭遇した人のフォローとなるようにこの記事を書きました。

先に結論

  • GitHub Actions のワークフローで PHPUnit によるカバレッジレポートを生成・アップロードする際に「ファイルが見つからない」という警告に遭遇しました。
  • 原因は、defaults.run.working-directory の設定と actions/upload-artifact アクションの path パラメータの解釈の違いでした。
    • actions/upload-artifactpath はデフォルトで ${{ github.workspace }} (ワークスペースのルート)からの相対パスとして解釈される。

本題

私が所属するプロジェクトでは Laravel & PHPUnit を利用しているので、 PHPUnit でテストカバレッジを計測し、その結果を CI/CD プロセスの一環として GitHub Actions 経由で保存・確認できるようにしたいと考えていました。しかし、ワークフローを組んで実行してみたところ、カバレッジレポートのアップロードステップで「ファイルが見つからない」という警告が出てしまい、アーティファクトが期待通りにアップロードされませんでした。

ログを詳細に追っていくと、カバレッジレポート自体は正しく生成されているように見えるのに、アップロードステップだけが失敗している状況でした。

想定されるディレクトリ構成例

以下に、今回の問題が発生した状況を想定したディレクトリ構成の例を示します。

your-repository/
├── .github/
│   └── workflows/
│       └── coverage_report.yml
└── your-laravel-project/    # working-directory で指定するディレクトリ
    ├── coverage/
    │   └── coverage_report/ # PHPUnit が生成するカバレッジレポート
    │       ├── index.html
    │       └── ... # (その他のレポートファイル)
    ├── app/
    │   └── ...
    ├── vendor/
    │   └── ...
    └── phpunit.xml

問題のあったワークフローの例

以下に、問題が発生していた GitHub Actions ワークフローファイル (coverage_report.yml) の例を示します
※例なのでこのままでは動きません。

name: coverage report

on: push

jobs:
  test:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: ./your-laravel-project # 各ステップのデフォルト作業ディレクトリ

    steps:
      - name: Checkout code
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
          php-extensions: # 必要な extension を指定

      - name: Install Dependencies
        run: composer install --prefer-dist --no-progress --no-suggest

      - name: Generate PHPUnit coverage report
        run: |
          vendor/bin/phpunit --coverage-html coverage/coverage_report tests --filter "test"
          ls -l coverage/coverage_report/

      - name: Upload coverage reports
        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
        with:
          name: phpunit-coverage-report-${{ github.sha }}
          path: coverage/coverage_report/
          retention-days: 30

調査:パスの謎を解き明かす

問題が発生していたのは、 PHPUnit でカバレッジレポートを生成した後、actions/upload-artifact を使ってそのレポートをアップロードするステップでした。

当初の Generate PHPUnit coverage report ステップのログでは、ls -l coverage/coverage_report/ の結果から、カバレッジレポートの HTML ファイル群が期待通り生成されていることが確認できました。

PHPUnit 10.5.30 by Sebastian Bergmann and contributors.
Runtime:       PHP 8.2.28 with Xdebug 3.4.2
Configuration: /home/runner/work/your-laravel-project/your-laravel-project/phpunit.xml
# (中略)
OK (122 tests, 668 assertions)
Generating code coverage report in HTML format ... done [00:10.338]
total 4812
# (中略)
-rw-r--r--  1 runner docker 4712880 Jun  3 14:22 dashboard.html
-rw-r--r--  1 runner docker   42979 Jun  3 14:22 helpers.php.html
-rw-r--r--  1 runner docker   42567 Jun  3 14:22 index.html

しかし、続く Upload coverage reports ステップでは、以下のような警告が出ていました。

Warning: No files were found with the provided path: coverage/coverage_report/. No artifacts will be uploaded.

ここで重要なのは、ワークフロー全体に defaults.run.working-directory: ./your-laravel-project という設定がされていた点です。これにより、run で実行されるコマンド(PHPUnit の実行や ls コマンドなど)は、リポジトリルート直下の your-laravel-project ディレクトリをカレントディレクトリとして実行されます。したがって、カバレッジレポートは ${{ github.workspace }}/your-laravel-project/coverage/coverage_report/ に生成されていました。

一方、actions/upload-artifact アクションの path パラメータは、デフォルトで ${{ github.workspace }} (ワークスペースのルート)からの相対パスとして解釈されます。working-directory の設定がこのアクションのパス解釈に直接影響するわけではなかったため、path: coverage/coverage_report/${{ github.workspace }}/coverage/coverage_report/ を指そうとしており、実際のファイルパスと異なっていたのが原因でした。

具体的な解決策:ワークフローファイルの修正

この問題を解決するために、actions/upload-artifactpath 指定を、ワークスペースルートからの正しい相対パスに変更しました。

修正前(問題があったコード):

    - name: Upload coverage reports
      uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
      with:
        name: phpunit-coverage-report-${{ github.sha }}
        path: coverage/coverage_report/ # 間違い:your-laravel-project ディレクトリが考慮されていない
        retention-days: 30

修正後(解決したコード):

    - name: Upload coverage reports
      uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
      with:
        name: phpunit-coverage-report-${{ github.sha }}
        path: your-laravel-project/coverage/coverage_report/ # 正しいパス指定
        retention-days: 30

この変更により、upload-artifact アクションは正しくカバレッジレポートファイル群を見つけ、無事にアップロードできるようになりました。

まとめ

GitHub Actions のワークフローを構築する際には、各ステップがどのディレクトリを基準に実行されるのか、そして使用するアクションがパスをどのように解釈するのかを正確に把握することが重要だと改めて認識しました。一見単純に見えるファイルパスの問題も、working-directory のような設定が絡むと少し複雑になることがあります。

新しいアクションを導入する際や、ワークフローの挙動が期待通りでない場合には、まず公式ドキュメントでパスに関する仕様を確認し、必要に応じてデバッグステップを挟んで確認する習慣が必要ですね💧

Discussion