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