mcpField
D
dotnet-gha-build-test

dotnet-gha-build-test

github.com/wshaddixクラウド・DevOps8676 回インストール35 週間前に更新
リポジトリ

dotnet-gha-build-test について

GitHub Actions の .NET ビルド・テストパイプラインを構成するスキルです。SDK 設定、NuGet のキャッシュと認証、結果・カバレッジ報告、複数 TFM のテスト、テスト分割を扱います。

主な機能

  1. 01.NET SDK 設定と NuGet 認証
  2. 02NuGet 復元キャッシュ
  3. 03テスト結果の公開
  4. 04カバレッジレポートのアップロード
  5. 05複数 TFM のマトリックステスト
  6. 06テスト分割の手法

活用シーン

  1. 01.NET GitHub Actions パイプラインの構成
  2. 02CI 実行間での復元結果のキャッシュ
  3. 03テストとカバレッジ結果の公開
  4. 04大規模テストスイートのジョブ分割

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/wshaddix/dotnet-skills --skill dotnet-gha-build-test

インストール後、Claude Code で/dotnet-gha-build-testを入力して読み込まれたか確認します。

詳細

dotnet-gha-build-test

GitHub Actions 向けの .NET ビルドおよびテストのワークフローパターン:複数バージョンのインストールと NuGet 認証を含む actions/setup-dotnet@v4 の設定、高速な CI を実現する NuGet 復元キャッシュ、dorny/test-reporter による結果公開を伴う dotnet test、Codecov と Coveralls へのコードカバレッジのアップロード、net8.0 と net9.0 を対象とする複数 TFM のマトリックステスト、大規模プロジェクト向けのテスト分割戦略です。

バージョンの前提: .NET 8/9/10 のサポートには actions/setup-dotnet@v4 を使用します。テスト結果の可視化には dorny/test-reporter@v1 を使用します。カバレッジレポートには Codecov と Coveralls の GitHub Apps を使用します。

スコープの境界: このスキルは、GitHub Actions 向けの .NET ビルドおよびテストパイプラインの設定を担当します。スターター CI テンプレート(基本的なビルド/テスト/パック)は [skill:dotnet-add-ci] が担当します。再利用可能なワークフロー、マトリックス戦略、キャッシュなどの組み合わせ可能なワークフローパターンは [skill:dotnet-gha-patterns] が担当します。何をテストするか、テストアーキテクチャ、品質ゲートなどのテスト戦略に関するガイダンスは [skill:dotnet-testing-strategy] が担当します。ベンチマークの CI ワークフローは [skill:dotnet-ci-benchmarking] が担当します。

対象外: スターター CI テンプレートについては [skill:dotnet-add-ci] を参照してください。テストアーキテクチャと戦略については [skill:dotnet-testing-strategy] を参照してください。CI でのベンチマーク回帰検出については [skill:dotnet-ci-benchmarking] を参照してください。公開とデプロイについては [skill:dotnet-gha-publish] および [skill:dotnet-gha-deploy] を参照してください。Azure DevOps のビルド/テストパイプラインについては [skill:dotnet-ado-build-test] を参照してください。

相互参照:[skill:dotnet-add-ci] はスターター向けのビルド/テストテンプレート、[skill:dotnet-testing-strategy] はテストアーキテクチャに関するガイダンス、[skill:dotnet-ci-benchmarking] はベンチマークの CI 統合、[skill:dotnet-artifacts-output] はビルド出力を一元化するレイアウトを使用する場合のアーティファクトアップロードパスの調整について参照してください。


actions/setup-dotnet@v4 の設定

基本設定
steps:
  - uses: actions/checkout@v4

  - name: Setup .NET
    uses: actions/setup-dotnet@v4
    with:
      dotnet-version: '8.0.x'
複数バージョンのインストール

1 つのジョブ内で複数の TFM をビルドするため、複数の SDK バージョンをインストールします:

- name: Setup .NET SDKs
  uses: actions/setup-dotnet@v4
  with:
    dotnet-version: |
      8.0.x
      9.0.x

最初に記載したバージョンが、PATH 上の既定の dotnet になります。インストールしたすべてのバージョンを --framework の指定で使用できます。

プライベートフィード向けの NuGet 認証

actions/setup-dotnet@v4 を使用して NuGet ソースの認証を設定します:

- name: Setup .NET with NuGet auth
  uses: actions/setup-dotnet@v4
  with:
    dotnet-version: '8.0.x'
    source-url: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json
  env:
    NUGET_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

複数のプライベートフィードがある場合は、セットアップ後に追加のソースを設定します:

- name: Setup .NET
  uses: actions/setup-dotnet@v4
  with:
    dotnet-version: '8.0.x'

- name: Add private NuGet feed
  run: |
    set -euo pipefail
    dotnet nuget add source https://pkgs.dev.azure.com/myorg/_packaging/myfeed/nuget/v3/index.json \
      --name AzureArtifacts \
      --username az \
      --password ${{ secrets.AZURE_ARTIFACTS_PAT }} \
      --store-password-in-clear-text

DPAPI による暗号化を利用できない Linux ランナーでは、--store-password-in-clear-text フラグが必要です。

Global.json による SDK バージョンの固定

リポジトリのルートに global.json がある場合、actions/setup-dotnet@v4 はそのファイルを自動的に読み取れます:

- name: Setup .NET from global.json
  uses: actions/setup-dotnet@v4
  with:
    global-json-file: global.json

これにより、CI でローカル開発と同じ SDK バージョンが使用されます。


NuGet の復元キャッシュ

標準的なキャッシュ設定
- name: Cache NuGet packages
  uses: actions/cache@v4
  with:
    path: ~/.nuget/packages
    key: nuget-${{ runner.os }}-${{ hashFiles('**/*.csproj', '**/Directory.Packages.props') }}
    restore-keys: |
      nuget-${{ runner.os }}-

- name: Restore dependencies
  run: dotnet restore MySolution.sln
setup-dotnet に組み込まれたキャッシュ機能

actions/setup-dotnet@v4 には、packages.lock.json を使用するキャッシュ機能が組み込まれています:

- name: Setup .NET with caching
  uses: actions/setup-dotnet@v4
  with:
    dotnet-version: '8.0.x'
    cache: true
    cache-dependency-path: '**/packages.lock.json'

まずローカルでロックファイルを生成します:dotnet restore --use-lock-file。決定的な復元を行うため、packages.lock.json ファイルをコミットしてください。

キャッシュキーの戦略
キーの構成要素目的
runner.osOS 間のキャッシュ衝突を防止します
hashFiles('**/*.csproj')パッケージ参照が変更されたときにキャッシュを無効化します
hashFiles('**/Directory.Packages.props')集中管理されているバージョンが変更されたときにキャッシュを無効化します
restore-keys のプレフィックス部分一致による増分キャッシュの再利用を可能にします

テスト結果の公開

dorny/test-reporter

インラインの失敗詳細を含む GitHub Actions のチェック注釈として、dotnet test の結果を公開します:

- name: Test
  run: |
    set -euo pipefail
    dotnet test MySolution.sln \
      --configuration Release \
      --logger "trx;LogFileName=test-results.trx" \
      --results-directory ./test-results
  continue-on-error: true
  id: test

- name: Publish test results
  uses: dorny/test-reporter@v1
  if: always()
  with:
    name: '.NET Test Results'
    path: 'test-results/**/*.trx'
    reporter: dotnet-trx
    fail-on-error: true

主な判断事項:

  • テストのステップに continue-on-error: true を設定すると、テストが失敗してもレポーターのステップが必ず実行されます
  • レポーターのステップに if: always() を設定すると、テスト結果にかかわらず結果が公開されます
  • レポーターに fail-on-error: true を設定すると、テストが失敗した場合にチェックが失敗としてマークされます
代替案:EnricoMi/publish-unit-test-result-action

テスト件数を含む、より充実した PR コメント連携を行う場合:

- name: Publish test results
  uses: EnricoMi/publish-unit-test-result-action@v2
  if: always()
  with:
    files: 'test-results/**/*.trx'
    check_name: 'Test Results'

コードカバレッジのアップロード

Codecov
- name: Test with coverage
  run: |
    set -euo pipefail
    dotnet test MySolution.sln \
      --configuration Release \
      --collect:"XPlat Code Coverage" \
      --results-directory ./coverage

- name: Upload coverage to Codecov
  uses: codecov/codecov-action@v4
  with:
    directory: ./coverage
    fail_ci_if_error: false
    token: ${{ secrets.CODECOV_TOKEN }}
Coveralls
- name: Test with coverage
  run: |
    set -euo pipefail
    dotnet test MySolution.sln \
      --configuration Release \
      --collect:"XPlat Code Coverage" \
      --results-directory ./coverage

- name: Upload coverage to Coveralls
  uses: coverallsapp/github-action@v2
  with:
    file: coverage/**/coverage.cobertura.xml
    format: cobertura
    github-token: ${{ secrets.GITHUB_TOKEN }}
ReportGenerator を使用したカバレッジレポートの生成

CI へのアップロードと併せて、人間が読みやすい HTML カバレッジレポートを生成します。

- name: Generate coverage report
  run: |
    set -euo pipefail
    dotnet tool install -g dotnet-reportgenerator-globaltool
    reportgenerator \
      -reports:coverage/**/coverage.cobertura.xml \
      -targetdir:coverage-report \
      -reporttypes:HtmlInline_AzurePipelines\;Cobertura

- name: Upload coverage report
  uses: actions/upload-artifact@v4
  with:
    name: coverage-report
    path: coverage-report/
    retention-days: 30

マルチ TFM マトリックステスト

TFM のマトリックス戦略
jobs:
  test:
    strategy:
      fail-fast: false
      matrix:
        tfm: [net8.0, net9.0]
        os: [ubuntu-latest, windows-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: |
            8.0.x
            9.0.x

      - name: Cache NuGet
        uses: actions/cache@v4
        with:
          path: ~/.nuget/packages
          key: nuget-${{ runner.os }}-${{ hashFiles('**/*.csproj', '**/Directory.Packages.props') }}
          restore-keys: |
            nuget-${{ runner.os }}-

      - name: Test ${{ matrix.tfm }}
        run: |
          set -euo pipefail
          dotnet test MySolution.sln \
            --framework ${{ matrix.tfm }} \
            --configuration Release \
            --logger "trx;LogFileName=${{ matrix.tfm }}-results.trx" \
            --results-directory ./test-results

      - name: Publish test results
        uses: dorny/test-reporter@v1
        if: always()
        with:
          name: 'Tests (${{ matrix.os }} / ${{ matrix.tfm }})'
          path: 'test-results/**/*.trx'
          reporter: dotnet-trx
必要な SDK をすべてインストールする

マトリックスではなく単一のジョブでマルチ TFM テストを実行する場合は、必要な SDK をすべて事前にインストールします。

- name: Setup .NET SDKs
  uses: actions/setup-dotnet@v4
  with:
    dotnet-version: |
      8.0.x
      9.0.x

- name: Test all TFMs
  run: dotnet test MySolution.sln --configuration Release

対応する SDK がインストールされていないと、dotnet test はその TFM を対象にビルドできず、NETSDK1045 で失敗します。


大規模プロジェクトのテスト分割

複数の並列ジョブにテストを分割する

大規模なテストスイートでは、テストプロジェクトを複数の並列ランナーに分割し、CI の合計実行時間を短縮します。

jobs:
  discover:
    runs-on: ubuntu-latest
    outputs:
      projects: ${{ steps.find.outputs.projects }}
    steps:
      - uses: actions/checkout@v4
      - id: find
        shell: bash
        run: |
          set -euo pipefail
          PROJECTS=$(find tests -name '*.csproj' | jq -R . | jq -sc .)
          echo "projects=$PROJECTS" >> "$GITHUB_OUTPUT"

  test:
    needs: discover
    strategy:
      fail-fast: false
      matrix:
        project: ${{ fromJson(needs.discover.outputs.projects) }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '8.0.x'

      - name: Test ${{ matrix.project }}
        run: |
          set -euo pipefail
          dotnet test ${{ matrix.project }} \
            --configuration Release \
            --logger "trx;LogFileName=results.trx" \
            --results-directory ./test-results

      - name: Publish test results
        uses: dorny/test-reporter@v1
        if: always()
        with:
          name: 'Tests - ${{ matrix.project }}'
          path: 'test-results/**/*.trx'
          reporter: dotnet-trx
プロジェクト内でテストクラスごとに分割する

単一の大規模なテストプロジェクトでは、dotnet test --filter を使用して名前空間ごとに分割します。

jobs:
  test:
    strategy:
      fail-fast: false
      matrix:
        shard: ['Unit', 'Integration', 'EndToEnd']
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '8.0.x'

      - name: Test ${{ matrix.shard }}
        run: |
          set -euo pipefail
          dotnet test tests/MyApp.Tests.csproj \
            --configuration Release \
            --filter "FullyQualifiedName~${{ matrix.shard }}" \
            --logger "trx;LogFileName=${{ matrix.shard }}-results.trx" \
            --results-directory ./test-results

エージェントが注意すべき点

  1. 複数行の bash run ブロックでは、必ず set -euo pipefail を設定する -- pipefail がないと、パイプでつないだコマンドが失敗してもエラーが伝播せず、CI が誤って成功したように表示されます。
  2. continue-on-error: true はテストのステップに設定し、レポーターには設定しない -- レポーターが結果を公開できるよう、テストのステップでジョブが早期に失敗しないようにする必要があります。一方、テストが失敗した場合は、レポーターがチェックを失敗させる必要があります。
  3. NuGet キャッシュキーに runner.os を含める -- NuGet パッケージには OS 固有のネイティブアセットが含まれます。OS をまたいでキャッシュがヒットすると、復元に失敗します。
  4. マルチ TFM では必要な SDK バージョンをすべてインストールする -- 対応する SDK を指定せずに dotnet test を実行すると NETSDK1045 が発生します。必要なバージョンをすべて dotnet-version に列挙してください。
  5. ワークフローファイルに TFM 文字列をハードコードしない -- マトリックス変数を使用して、ワークフローファイルとプロジェクト設定の整合性を保ちます。CI に net8.0 をハードコードすると、プロジェクトが net9.0 に移行したときに問題が発生します。
  6. カバレッジの収集には --collect:"XPlat Code Coverage" が必要 -- 既定の dotnet test ではカバレッジファイルは生成されません。XPlat Code Coverage コレクターは .NET SDK に組み込まれています。
  7. TRX ロガーのパスはレポーターの glob パターンと一致させる -- ロガーが test-results/results.trx に書き込む場合、レポーターの path には、そのディレクトリを含む glob パターンを指定する必要があります。
  8. NuGet の認証情報をワークフローファイルにコミットしない -- すべての認証トークンには ${{ secrets.* }} の参照を使用します。NUGET_AUTH_TOKEN 環境変数が標準的な方法です。

コメント