Skip to content
cccskills
FreePublished on GitHub

dotnet-gha-build-test

Configures GitHub Actions .NET build/test -- setup-dotnet, NuGet cache, reporting.

The English summary is being prepared. Showing the original description.

Works withGitHub
See how to install

Files included (1)

  • SKILL.md13.8 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

dotnet-gha-build-test

.NET build and test workflow patterns for GitHub Actions: actions/setup-dotnet@v4 configuration with multi-version installs and NuGet authentication, NuGet restore caching for fast CI, dotnet test with result publishing via dorny/test-reporter, code coverage upload to Codecov and Coveralls, multi-TFM matrix testing across net8.0 and net9.0, and test sharding strategies for large projects.

Version assumptions: actions/setup-dotnet@v4 for .NET 8/9/10 support. dorny/test-reporter@v1 for test result visualization. Codecov and Coveralls GitHub Apps for coverage reporting.

Scope

  • setup-dotnet action configuration with multi-version installs
  • NuGet restore caching for fast CI
  • dotnet test with result publishing and coverage upload
  • Multi-TFM matrix testing and test sharding
  • NuGet authentication for private feeds in GitHub Actions

Out of scope

  • Starter CI templates -- see [skill:dotnet-add-ci]
  • Test architecture and strategy -- see [skill:dotnet-testing-strategy]
  • Benchmark regression detection in CI -- see [skill:dotnet-ci-benchmarking]
  • Publishing and deployment -- see [skill:dotnet-gha-publish] and [skill:dotnet-gha-deploy]
  • Azure DevOps build/test pipelines -- see [skill:dotnet-ado-build-test]
  • Reusable workflow and composite action patterns -- see [skill:dotnet-gha-patterns]

Cross-references: [skill:dotnet-add-ci] for starter build/test templates, [skill:dotnet-testing-strategy] for test architecture guidance, [skill:dotnet-ci-benchmarking] for benchmark CI integration, [skill:dotnet-artifacts-output] for artifact upload path adjustments when using centralized build output layout.


actions/setup-dotnet@v4 Configuration

Basic Setup


steps:
  - uses: actions/checkout@v4

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

```text

### Multi-Version Install

Install multiple SDK versions for multi-TFM builds within a single job:

```yaml

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

```text

The first listed version becomes the default `dotnet` on PATH. All installed versions are available via `--framework`
targeting.

### NuGet Authentication for Private Feeds

Configure NuGet source authentication via `actions/setup-dotnet@v4`:

```yaml

- 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 }}

```json

For multiple private feeds, configure additional sources after setup:

```yaml

- 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

```text

The `--store-password-in-clear-text` flag is required on Linux runners where DPAPI encryption is unavailable.

### Global.json SDK Version Pinning

When `global.json` exists in the repository root, `actions/setup-dotnet@v4` can read it automatically:

```yaml

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

```json

This ensures CI uses the same SDK version as local development.

---

## NuGet Restore Caching

### Standard Cache Configuration

```yaml

- 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

```text

### Built-in Cache with setup-dotnet

`actions/setup-dotnet@v4` has built-in caching support using `packages.lock.json`:

```yaml

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

```json

Generate lock files locally first: `dotnet restore --use-lock-file`. Commit `packages.lock.json` files for deterministic
restore.

### Cache Key Strategy

| Key Component                              | Purpose                                           |
| ------------------------------------------ | ------------------------------------------------- |
| `runner.os`                                | Prevent cross-OS cache collisions                 |
| `hashFiles('**/*.csproj')`                 | Invalidate when package references change         |
| `hashFiles('**/Directory.Packages.props')` | Invalidate when centrally managed versions change |
| `restore-keys` prefix                      | Partial match for incremental cache reuse         |

---

## Test Result Publishing

### dorny/test-reporter

Publish `dotnet test` results as GitHub Actions check annotations with inline failure details:

```yaml

- 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

```text

**Key decisions:**

- `continue-on-error: true` on the test step ensures the reporter step always runs, even on failures
- `if: always()` on the reporter step publishes results regardless of test outcome
- `fail-on-error: true` on the reporter marks the check as failed when tests fail

### Alternative: EnricoMi/publish-unit-test-result-action

For richer PR comment integration with test counts:

```yaml

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

```text

---

## Code Coverage Upload

### Codecov

```yaml

- 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 }}

```text

### Coveralls

```yaml

- 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 }}

```xml

### Coverage Report Generation with ReportGenerator

Generate human-readable HTML coverage reports alongside CI upload:

```yaml

- 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

```text

---

## Multi-TFM Matrix Testing

### Matrix Strategy for TFMs

```yaml

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

```text

### Install All Required SDKs

When running multi-TFM tests in a single job instead of a matrix, install all required SDKs upfront:

```yaml

- 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

```text

Without the matching SDK installed, `dotnet test` cannot build for that TFM and fails with `NETSDK1045`.

---

## Test Sharding for Large Projects

### Splitting Tests Across Parallel Jobs

For large test suites, split test projects across parallel runners to reduce total CI time:

```yaml

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

```text

### Sharding by Test Class Within a Project

For a single large test project, use `dotnet test --filter` to split by namespace:

```yaml

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

```text

---

## Agent Gotchas

1. **Always set `set -euo pipefail` in multi-line bash `run` blocks** -- without `pipefail`, piped commands that fail do
   not propagate the error, producing false-green CI.
2. **Use `continue-on-error: true` on the test step, not on the reporter** -- the test step must not fail the job
   prematurely so the reporter can publish results, but the reporter should fail the check when tests fail.
3. **Include `runner.os` in NuGet cache keys** -- NuGet packages have OS-specific native assets; cross-OS cache hits
   cause restore failures.
4. **Install all required SDK versions for multi-TFM** -- `dotnet test` without the matching SDK produces `NETSDK1045`;
   list every required version in `dotnet-version`.
5. **Do not hardcode TFM strings in workflow files** -- use matrix variables to keep workflow files in sync with project
   configuration; hardcoded `net8.0` in CI breaks when the project moves to `net9.0`.
6. **Coverage collection requires `--collect:"XPlat Code Coverage"`** -- the default `dotnet test` does not produce
   coverage files; the `XPlat Code Coverage` collector is built into the .NET SDK.
7. **TRX logger path must match reporter glob** -- if the logger writes to `test-results/results.trx`, the reporter
   `path` must include that directory in its glob pattern.
8. **Never commit NuGet credentials to workflow files** -- use `${{ secrets.* }}` references for all authentication
   tokens; the `NUGET_AUTH_TOKEN` environment variable is the standard pattern.

Code Navigation (Serena MCP)

Primary approach: Use Serena symbol operations for efficient code navigation:

  1. Find definitions: serena_find_symbol instead of text search
  2. Understand structure: serena_get_symbols_overview for file organization
  3. Track references: serena_find_referencing_symbols for impact analysis
  4. Precise edits: serena_replace_symbol_body for clean modifications

When to use Serena vs traditional tools:

  • Use Serena: Navigation, refactoring, dependency analysis, precise edits
  • Use Read/Grep: Reading full files, pattern matching, simple text operations
  • Fallback: If Serena unavailable, traditional tools work fine

Example workflow:

# Instead of:
Read: src/Services/OrderService.cs
Grep: "public void ProcessOrder"

# Use:
serena_find_symbol: "OrderService/ProcessOrder"
serena_get_symbols_overview: "src/Services/OrderService.cs"

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

Patterns and techniques for evaluating and improving AI agent outputs.

The English summary is being prepared. Showing the original description.

rudironsoni/Synaxis2Updated Mar 17, 2026

Comprehensive AI prompt engineering safety review and improvement prompt. Analyzes prompts for safety, bias, security vulnerabilities, and effectiveness while providing detailed improvement recommendations.

The English summary is being prepared. Showing the original description.

rudironsoni/Synaxis2Updated Mar 17, 2026

Use when user requests research requiring multiple sources, comprehensive analysis, or synthesis across topics - technical research, domain knowledge gathering, market analysis, or learning about complex subjects

The English summary is being prepared. Showing the original description.

rudironsoni/Synaxis2Updated Mar 17, 2026

AI-powered wiki generation for code repositories with commands, agents, and skills

The English summary is being prepared. Showing the original description.

rudironsoni/Synaxis2Updated Mar 17, 2026

Use when building .NET 10 or C# 14 applications; when using minimal APIs, modular monolith patterns, or feature folders; when implementing HTTP resilience, Options pattern, Channels, or validation; when seeing outdated patterns like old extension method syntax

The English summary is being prepared. Showing the original description.

rudironsoni/Synaxis2Updated Mar 17, 2026

Implements accessible .NET UI. SemanticProperties, ARIA, AutomationPeer, testing per platform.

The English summary is being prepared. Showing the original description.

rudironsoni/Synaxis2Updated Mar 17, 2026

See all skills by rudironsoni

Report a problem with this skill