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. 02在 CI 运行间缓存还原结果
  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 构建和测试工作流模式:配置 actions/setup-dotnet@v4,安装多个版本并进行 NuGet 身份验证;缓存 NuGet 还原以加快 CI;使用 dorny/test-reporter 发布 dotnet test 结果;将代码覆盖率上传到 Codecov 和 Coveralls;跨 net8.0 和 net9.0 进行多 TFM 矩阵测试;以及大型项目的测试分片策略。

版本假设: 使用 actions/setup-dotnet@v4 支持 .NET 8/9/10。使用 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'
安装多个版本

在单个作业中安装多个 SDK 版本,以支持多 TFM 构建:

- 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

在 Linux 运行器上,由于无法使用 DPAPI 加密,因此必须使用 --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防止不同操作系统之间发生缓存冲突
hashFiles('**/*.csproj')在包引用发生更改时使缓存失效
hashFiles('**/Directory.Packages.props')在集中管理的版本发生更改时使缓存失效
restore-keys 前缀部分匹配,以增量方式复用缓存

发布测试结果

dorny/test-reporter

将 dotnet test 结果作为 GitHub Actions 检查注释发布,并内联显示失败详情:

- 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

Agent 注意事项

  1. 始终在多行 bash run 块中设置 set -euo pipefail —— 如果没有 pipefail,管道中失败的命令不会传递错误,导致 CI 错误地显示为绿色。
  2. 在测试步骤而非报告器上使用 continue-on-error: true —— 测试步骤不能提前使任务失败,以便报告器发布结果;但测试失败时,报告器应使检查失败。
  3. 在 NuGet 缓存键中包含 runner.os —— NuGet 包包含特定于操作系统的原生资源;跨操作系统命中缓存会导致还原失败。
  4. 为多 TFM 安装所有必需的 SDK 版本 —— 如果没有匹配的 SDK,dotnet test 会产生 NETSDK1045;请在 dotnet-version 中列出所有必需的版本。
  5. 不要在工作流文件中硬编码 TFM 字符串 —— 使用矩阵变量,使工作流文件与项目配置保持同步;当项目从 net8.0 升级到 net9.0 时,CI 中硬编码的 net8.0 会导致问题。
  6. 收集覆盖率需要使用 --collect:"XPlat Code Coverage" —— 默认的 dotnet test 不会生成覆盖率文件;XPlat Code Coverage 收集器已内置于 .NET SDK 中。
  7. TRX logger 路径必须与报告器的 glob 匹配 —— 如果 logger 将文件写入 test-results/results.trx,报告器的 path 必须在其 glob 模式中包含该目录。
  8. 切勿将 NuGet 凭据提交到工作流文件中 —— 所有身份验证令牌都应使用 ${{ secrets.* }} 引用;标准做法是使用 NUGET_AUTH_TOKEN 环境变量。

评论

支持 Markdown