mcpField
D
dotnet-ado-build-test

dotnet-ado-build-test

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

dotnet-ado-build-test について

dotnet-ado-build-test は、Azure DevOps の .NET ビルド・テストパイプライン設定パターンを提供する、CI を保守する開発者向けのスキルです。NuGet フィード認証、テスト・カバレッジ報告、net8.0 と net9.0 のマルチ TFM マトリックスを扱います。

主な機能

  1. 01.NET のビルド、テスト、パック設定
  2. 02Azure Artifacts の NuGet フィード認証
  3. 03TRX と JUnit のテスト結果公開
  4. 04Cobertura と JaCoCo のカバレッジ公開
  5. 05マルチ TFM マトリックスの設定

活用シーン

  1. 01Azure DevOps のビルド・テスト手順の作成
  2. 02Azure Artifacts からの NuGet パッケージ復元
  3. 03パイプラインでのテスト・カバレッジ報告
  4. 04net8.0 と net9.0 でのテスト実行

インストール

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

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

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

詳細

dotnet-ado-build-test

Azure DevOps 向けの .NET ビルドおよびテストパイプラインのパターン:ビルド、テスト、パック操作に使用する DotNetCoreCLI@2 タスク、NuGetAuthenticate@1 を使った Azure Artifacts フィードからの NuGet 復元、TRX および JUnit 形式での PublishTestResults@2 によるテスト結果の公開、Cobertura および JaCoCo 形式での PublishCodeCoverageResults@2 によるコードカバレッジ、さらに net8.0 と net9.0 を対象とする複数 TFM のマトリックス戦略を扱います。

バージョンの前提: DotNetCoreCLI@2 タスク(現行)。SDK のインストールには UseDotNet@2 を使用します。Azure Artifacts には NuGetAuthenticate@1 を使用します。レポートには PublishTestResults@2 と PublishCodeCoverageResults@2 を使用します。

対象範囲の境界: このスキルでは、Azure DevOps の .NET ビルドおよびテストパイプラインの構成を扱います。スターター CI テンプレート(基本的なビルド/テスト/パック)は [skill:dotnet-add-ci] が担当します。コンポーザブルなパイプラインパターン(テンプレート、複数ステージ、トリガー)は [skill:dotnet-ado-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-ado-publish] および [skill:dotnet-ado-unique] を参照してください。GitHub Actions のビルド/テストワークフローについては [skill:dotnet-gha-build-test] を参照してください。

相互参照:[skill:dotnet-add-ci] はスターターのビルド/テストテンプレート、[skill:dotnet-testing-strategy] はテストアーキテクチャのガイダンス、[skill:dotnet-ci-benchmarking] はベンチマークの CI 統合を扱います。


DotNetCoreCLI@2 タスク

ビルド
steps:
  - task: UseDotNet@2
    displayName: 'Install .NET SDK'
    inputs:
      packageType: 'sdk'
      version: '8.0.x'

  - task: DotNetCoreCLI@2
    displayName: 'Restore'
    inputs:
      command: 'restore'
      projects: 'MyApp.sln'

  - task: DotNetCoreCLI@2
    displayName: 'Build'
    inputs:
      command: 'build'
      projects: 'MyApp.sln'
      arguments: '-c Release --no-restore'
テスト
- task: DotNetCoreCLI@2
  displayName: 'Run tests'
  inputs:
    command: 'test'
    projects: '**/*Tests.csproj'
    arguments: >-
      -c Release
      --logger "trx;LogFileName=test-results.trx"
      --results-directory $(Build.ArtifactStagingDirectory)/test-results
パック
- task: DotNetCoreCLI@2
  displayName: 'Pack NuGet packages'
  inputs:
    command: 'pack'
    packagesToPack: 'src/**/*.csproj'
    configuration: 'Release'
    outputDir: '$(Build.ArtifactStagingDirectory)/nupkgs'
    nobuild: true
カスタムコマンド

タスクで直接サポートされていないコマンド(例:dotnet tool install)の場合:

- task: DotNetCoreCLI@2
  displayName: 'Install dotnet tools'
  inputs:
    command: 'custom'
    custom: 'tool'
    arguments: 'restore'
複数バージョンの SDK のインストール

複数 TFM のビルド用に、複数バージョンの SDK をインストールします。

- task: UseDotNet@2
  displayName: 'Install .NET 8'
  inputs:
    packageType: 'sdk'
    version: '8.0.x'

- task: UseDotNet@2
  displayName: 'Install .NET 9'
  inputs:
    packageType: 'sdk'
    version: '9.0.x'

UseDotNet@2 を呼び出すたびに、その SDK バージョンが PATH に追加されます。最後にインストールされたバージョンが既定になりますが、--framework を指定することで、すべてのバージョンを使用できます。


Azure Artifacts フィードからの NuGet 復元

フィード認証に使用する NuGetAuthenticate@1
steps:
  - task: NuGetAuthenticate@1
    displayName: 'Authenticate NuGet feeds'

  - task: DotNetCoreCLI@2
    displayName: 'Restore'
    inputs:
      command: 'restore'
      projects: 'MyApp.sln'
      feedsToUse: 'config'
      nugetConfigPath: 'nuget.config'

NuGetAuthenticate@1 タスクは、nuget.config で参照されているすべての Azure Artifacts フィードの認証情報を構成します。PAT や API キーを明示的に指定する必要はありません。このタスクではパイプラインの ID が使用されます。

フィードの直接選択

nuget.config を使用しないシンプルな構成では、復元タスクでフィードを直接選択します。

- task: DotNetCoreCLI@2
  displayName: 'Restore with Azure Artifacts'
  inputs:
    command: 'restore'
    projects: 'MyApp.sln'
    feedsToUse: 'select'
    vstsFeed: 'MyProject/MyFeed'
    includeNuGetOrg: true
アップストリームソース

Azure Artifacts フィードでは、アップストリームソースとして nuget.org をプロキシできます。設定すると、1 つのフィード参照でプライベートパッケージと公開 NuGet パッケージの両方にアクセスできます。


<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <clear />
    <add key="MyFeed" value="https://pkgs.dev.azure.com/myorg/_packaging/myfeed/nuget/v3/index.json" />
  </packageSources>
</configuration>

フィードでアップストリームソースを有効にすると、nuget.org のパッケージは Azure Artifacts フィードにキャッシュされ、すべてのパッケージに対して単一の認証済みソースを利用できます。

組織をまたぐフィードへのアクセス

別の Azure DevOps 組織にあるフィードには、サービス接続を使用します。

- task: NuGetAuthenticate@1
  displayName: 'Authenticate external feed'
  inputs:
    nuGetServiceConnections: 'ExternalOrgFeedConnection'

- task: DotNetCoreCLI@2
  displayName: 'Restore'
  inputs:
    command: 'restore'
    projects: 'MyApp.sln'
    feedsToUse: 'config'
    nugetConfigPath: 'nuget.config'

テスト結果の公開

TRX 形式での PublishTestResults@2
- task: DotNetCoreCLI@2
  displayName: 'Run tests'
  inputs:
    command: 'test'
    projects: '**/*Tests.csproj'
    arguments: >-
      -c Release
      --logger "trx;LogFileName=results.trx"
      --results-directory $(Common.TestResultsDirectory)
  continueOnError: true

- task: PublishTestResults@2
  displayName: 'Publish test results'
  condition: always()
  inputs:
    testResultsFormat: 'VSTest'
    testResultsFiles: '$(Common.TestResultsDirectory)/**/*.trx'
    mergeTestResults: true
    testRunTitle: '.NET Unit Tests'

主な判断事項:

  • テストタスクに continueOnError: true を指定すると、テストが失敗した場合でも公開ステップが必ず実行されます
  • 公開タスクに condition: always() を指定すると、前のステップの結果にかかわらず実行されます
  • mergeTestResults: true を指定すると、複数のテストプロジェクトの結果が 1 つのテスト実行にまとめられます
  • testRunTitle を指定すると、Azure DevOps の「テスト」タブにわかりやすい名前が表示されます
JUnit 形式

一部のサードパーティ製テストフレームワークは JUnit XML を出力します。JUnit 形式を使用します。

- task: PublishTestResults@2
  displayName: 'Publish JUnit results'
  condition: always()
  inputs:
    testResultsFormat: 'JUnit'
    testResultsFiles: '**/junit-results.xml'
    mergeTestResults: true
添付ファイル付きテスト結果

失敗したテストのデバッグ用に、スクリーンショットやログをテスト結果に添付します。

- task: DotNetCoreCLI@2
  displayName: 'Run tests with attachments'
  inputs:
    command: 'test'
    projects: '**/*Tests.csproj'
    arguments: >-
      -c Release
      --logger "trx;LogFileName=results.trx"
      --results-directory $(Common.TestResultsDirectory)
      --collect:"XPlat Code Coverage"
  continueOnError: true

- task: PublishTestResults@2
  displayName: 'Publish test results'
  condition: always()
  inputs:
    testResultsFormat: 'VSTest'
    testResultsFiles: '$(Common.TestResultsDirectory)/**/*.trx'
    mergeTestResults: true
    testRunTitle: '.NET Tests'
    publishRunAttachments: true

コードカバレッジ

Cobertura を使用した PublishCodeCoverageResults@2
- task: DotNetCoreCLI@2
  displayName: 'Test with coverage'
  inputs:
    command: 'test'
    projects: '**/*Tests.csproj'
    arguments: >-
      -c Release
      --collect:"XPlat Code Coverage"
      --results-directory $(Agent.TempDirectory)/coverage

- task: PublishCodeCoverageResults@2
  displayName: 'Publish code coverage'
  inputs:
    summaryFileLocation: '$(Agent.TempDirectory)/coverage/**/coverage.cobertura.xml'

PublishCodeCoverageResults@2 タスク(v2)は、reportgenerator を使用しなくても、Azure DevOps のビルド概要タブに HTML カバレッジレポートを自動生成します。

ReportGenerator を使用した詳細なカバレッジレポート

組み込みの表示機能では対応できない、カスタムカバレッジレポートを作成するには、次のようにします。

- task: DotNetCoreCLI@2
  displayName: 'Test with coverage'
  inputs:
    command: 'test'
    projects: '**/*Tests.csproj'
    arguments: >-
      -c Release
      --collect:"XPlat Code Coverage"
      --results-directory $(Agent.TempDirectory)/coverage

- script: |
    set -euo pipefail
    dotnet tool install -g dotnet-reportgenerator-globaltool
    reportgenerator \
      -reports:$(Agent.TempDirectory)/coverage/**/coverage.cobertura.xml \
      -targetdir:$(Build.ArtifactStagingDirectory)/coverage-report \
      -reporttypes:HtmlInline_AzurePipelines\;Cobertura
  displayName: 'Generate coverage report'

- task: PublishCodeCoverageResults@2
  displayName: 'Publish coverage'
  inputs:
    summaryFileLocation: '$(Build.ArtifactStagingDirectory)/coverage-report/Cobertura.xml'

- task: PublishPipelineArtifact@1
  displayName: 'Upload coverage report'
  inputs:
    targetPath: '$(Build.ArtifactStagingDirectory)/coverage-report'
    artifactName: 'coverage-report'
カバレッジのしきい値

スクリプトのステップで Cobertura XML を解析し、カバレッジの最低基準を適用します。

- script: |
    set -euo pipefail
    COVERAGE_FILE=$(find $(Agent.TempDirectory)/coverage -name 'coverage.cobertura.xml' | head -1)
    COVERAGE=$(python3 -c "
    import xml.etree.ElementTree as ET
    tree = ET.parse('$COVERAGE_FILE')
    print(float(tree.getroot().attrib['line-rate']) * 100)
    ")
    echo "Line coverage: ${COVERAGE}%"
    if (( $(echo "$COVERAGE < 80" | bc -l) )); then
      echo "##vso[task.logissue type=error]Coverage ${COVERAGE}% is below 80% threshold"
      exit 1
    fi
  displayName: 'Enforce coverage threshold'

マルチ TFM のマトリックス戦略

TFM とオペレーティングシステムをまたぐマトリックスビルド
jobs:
  - job: Test
    strategy:
      matrix:
        Linux_net80:
          vmImage: 'ubuntu-latest'
          tfm: 'net8.0'
          dotnetVersion: '8.0.x'
        Linux_net90:
          vmImage: 'ubuntu-latest'
          tfm: 'net9.0'
          dotnetVersion: '9.0.x'
        Windows_net80:
          vmImage: 'windows-latest'
          tfm: 'net8.0'
          dotnetVersion: '8.0.x'
        Windows_net90:
          vmImage: 'windows-latest'
          tfm: 'net9.0'
          dotnetVersion: '9.0.x'
    pool:
      vmImage: $(vmImage)
    steps:
      - task: UseDotNet@2
        displayName: 'Install .NET $(dotnetVersion)'
        inputs:
          packageType: 'sdk'
          version: $(dotnetVersion)

      - task: DotNetCoreCLI@2
        displayName: 'Test $(tfm) on $(vmImage)'
        inputs:
          command: 'test'
          projects: '**/*Tests.csproj'
          arguments: >-
            -c Release
            --framework $(tfm)
            --logger "trx;LogFileName=$(tfm)-results.trx"
            --results-directory $(Common.TestResultsDirectory)
        continueOnError: true

      - task: PublishTestResults@2
        displayName: 'Publish $(tfm) results'
        condition: always()
        inputs:
          testResultsFormat: 'VSTest'
          testResultsFiles: '$(Common.TestResultsDirectory)/**/*.trx'
          testRunTitle: '$(tfm) on $(vmImage)'
1 つのジョブでマルチ TFM を実行するための複数 SDK のインストール

マトリックスではなく、1 つのジョブですべての TFM を実行する場合は、必要な SDK をすべてインストールします。

steps:
  - task: UseDotNet@2
    displayName: 'Install .NET 8'
    inputs:
      packageType: 'sdk'
      version: '8.0.x'

  - task: UseDotNet@2
    displayName: 'Install .NET 9'
    inputs:
      packageType: 'sdk'
      version: '9.0.x'

  - task: DotNetCoreCLI@2
    displayName: 'Test all TFMs'
    inputs:
      command: 'test'
      projects: '**/*Tests.csproj'
      arguments: '-c Release'

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

再利用可能なテンプレートベースのマトリックス
# templates/jobs/matrix-test.yml
parameters:
  - name: configurations
    type: object
    default:
      - tfm: 'net8.0'
        dotnetVersion: '8.0.x'
      - tfm: 'net9.0'
        dotnetVersion: '9.0.x'

jobs:
  - ${{ each config in parameters.configurations }}:
    - job: Test_${{ replace(config.tfm, '.', '_') }}
      displayName: 'Test ${{ config.tfm }}'
      pool:
        vmImage: 'ubuntu-latest'
      steps:
        - task: UseDotNet@2
          inputs:
            packageType: 'sdk'
            version: ${{ config.dotnetVersion }}

        - task: DotNetCoreCLI@2
          displayName: 'Test ${{ config.tfm }}'
          inputs:
            command: 'test'
            projects: '**/*Tests.csproj'
            arguments: '-c Release --framework ${{ config.tfm }}'

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

  1. 複数行の script: ステップでは set -euo pipefail を使用します — Linux 上の ADO script: タスクでは、デフォルトで set -e が有効ですが、pipefail と nounset は設定されません。pipefail がないと、パイプでつないだコマンドの失敗が黙って無視されます。
  2. 結果の発行タスクではなく、テストタスクに continueOnError: true を設定します — 結果が発行される前にテストタスクでパイプラインが失敗しないようにする一方、発行タスクには実際のテスト結果を反映させる必要があります。
  3. マルチ TFM ビルドでは、必要な SDK バージョンをすべてインストールします — 対応する SDK がない状態で dotnet test を実行すると NETSDK1045 が発生します。必要なバージョンごとに UseDotNet@2 ステップを追加してください。
  4. NuGetAuthenticate@1 は復元ステップより前に実行する必要があります — 認証トークンはタスクの実行時にエージェントの NuGet 設定に注入されるため、認証前に復元を実行すると 401 エラーになります。
  5. 複雑なフィード構成では nuget.config と feedsToUse: 'config' を使用します — feedsToUse: 'select' がサポートする Azure Artifacts フィードは 1 つのみです。複数フィードを使用する場合は、nuget.config ファイルが必要です。
  6. カバレッジを収集するには --collect:"XPlat Code Coverage" が必要です — デフォルトの dotnet test ではカバレッジファイルは生成されません。XPlat Code Coverage コレクターは .NET SDK に組み込まれています。
  7. PublishCodeCoverageResults@2 が受け付けるのは Cobertura XML です — TRX などの形式をカバレッジ発行タスクに渡しても出力は生成されません。コレクターが Cobertura 形式で出力することを確認してください。
  8. ADO のマトリックス構文は GHA と異なります — ADO では配列ではなく、キーと値のペアを持つ名前付きマトリックスエントリを使用します。各エントリで、ジョブ内で使用する変数名をすべて定義する必要があります。
  9. パイプラインの YAML に認証情報をハードコードしないでください — Azure Key Vault にリンクした変数グループ、またはパイプラインレベルのシークレット変数を使用してください。ハードコードしたシークレットは、リポジトリの履歴から閲覧できます。

コメント