dotnet-gha-patterns
Composes GitHub Actions workflows. Reusable workflows, composite actions, matrix, caching.
Best use case
dotnet-gha-patterns is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Composes GitHub Actions workflows. Reusable workflows, composite actions, matrix, caching.
Teams using dotnet-gha-patterns should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/dotnet-gha-patterns/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How dotnet-gha-patterns Compares
| Feature / Agent | dotnet-gha-patterns | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Composes GitHub Actions workflows. Reusable workflows, composite actions, matrix, caching.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# dotnet-gha-patterns
Composable GitHub Actions workflow patterns for .NET projects: reusable workflows with `workflow_call`, composite
actions for shared step sequences, matrix builds across TFMs and operating systems, path-based triggers, concurrency
groups for duplicate run cancellation, environment protection rules, NuGet and SDK caching strategies, and
`workflow_dispatch` inputs for manual triggers.
**Version assumptions:** GitHub Actions workflow syntax v2. `actions/setup-dotnet@v4` for .NET 8/9/10 support.
`actions/cache@v4` for dependency caching.
## Scope
- Reusable workflows with workflow_call
- Composite actions for shared step sequences
- Matrix builds across TFMs and operating systems
- Path-based triggers and concurrency groups
- NuGet and SDK caching strategies
- workflow_dispatch inputs for manual triggers
## Out of scope
- Starter CI/CD templates -- see [skill:dotnet-add-ci]
- CLI release pipelines (tag-triggered build-package-release for CLI tools) -- see [skill:dotnet-cli-release-pipeline]
- Benchmark CI workflows -- see [skill:dotnet-ci-benchmarking]
- Azure DevOps pipeline patterns -- see [skill:dotnet-ado-patterns]
- Build/test specifics -- see [skill:dotnet-gha-build-test]
- Publishing workflows -- see [skill:dotnet-gha-publish]
- Deployment patterns -- see [skill:dotnet-gha-deploy]
Cross-references: [skill:dotnet-add-ci] for starter templates that these patterns extend,
[skill:dotnet-cli-release-pipeline] for CLI-specific release automation, [skill:dotnet-ci-benchmarking] for
benchmark-specific CI integration.
---
## Reusable Workflows (`workflow_call`)
### Defining a Reusable Workflow
Reusable workflows allow callers to invoke an entire workflow as a single step. Define inputs, outputs, and secrets for
a clean contract:
````yaml
# .github/workflows/build-reusable.yml
name: Build (Reusable)
on:
workflow_call:
inputs:
dotnet-version:
description: '.NET SDK version to install'
required: false
type: string
default: '8.0.x'
configuration:
description: 'Build configuration'
required: false
type: string
default: 'Release'
project-path:
description: 'Path to solution or project file'
required: true
type: string
outputs:
artifact-name:
description: 'Name of the uploaded build artifact'
value: ${{ jobs.build.outputs.artifact-name }}
secrets:
NUGET_AUTH_TOKEN:
description: 'NuGet feed authentication token'
required: false
jobs:
build:
runs-on: ubuntu-latest
outputs:
artifact-name: build-${{ github.sha }}
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ inputs.dotnet-version }}
- name: Restore
run: dotnet restore ${{ inputs.project-path }}
- name: Build
run: dotnet build ${{ inputs.project-path }} -c ${{ inputs.configuration }} --no-restore
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: build-${{ github.sha }}
path: |
**/bin/${{ inputs.configuration }}/**
retention-days: 7
```text
### Calling a Reusable Workflow
```yaml
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
uses: ./.github/workflows/build-reusable.yml
with:
dotnet-version: '8.0.x'
project-path: MyApp.sln
secrets:
NUGET_AUTH_TOKEN: ${{ secrets.NUGET_AUTH_TOKEN }}
test:
needs: build
uses: ./.github/workflows/test-reusable.yml
with:
dotnet-version: '8.0.x'
project-path: MyApp.sln
```yaml
### Cross-Repository Reusable Workflows
Reference workflows from other repositories using the full path:
```yaml
jobs:
build:
uses: my-org/.github-workflows/.github/workflows/dotnet-build.yml@v1
with:
dotnet-version: '9.0.x'
secrets: inherit # pass all secrets from caller
```yaml
Use `secrets: inherit` when the reusable workflow needs access to the same secrets as the calling workflow without
explicit enumeration.
---
## Composite Actions
### Creating a Composite Action
Composite actions bundle multiple steps into a single reusable action. Use them for shared step sequences that appear
across multiple workflows:
```yaml
# .github/actions/dotnet-setup/action.yml
name: 'Setup .NET Environment'
description: 'Install .NET SDK and restore NuGet packages with caching'
inputs:
dotnet-version:
description: '.NET SDK version'
required: false
default: '8.0.x'
project-path:
description: 'Path to solution or project'
required: true
runs:
using: 'composite'
steps:
- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ inputs.dotnet-version }}
- 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
shell: bash
run: dotnet restore ${{ inputs.project-path }}
```bash
### Using a Composite Action
```yaml
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET environment
uses: ./.github/actions/dotnet-setup
with:
dotnet-version: '9.0.x'
project-path: MyApp.sln
- name: Build
run: dotnet build MyApp.sln -c Release --no-restore
```text
### Reusable Workflow vs Composite Action
| Feature | Reusable Workflow | Composite Action |
| ---------------- | ------------------------------- | ------------------------------- |
| Scope | Entire job with runner | Steps within a job |
| Runner selection | Own `runs-on` | Caller's runner |
| Secrets access | Explicit or `inherit` | Caller's context |
| Outputs | Job-level outputs | Step-level outputs |
| Best for | Complete build/test/deploy jobs | Shared setup/teardown sequences |
---
## Matrix Builds
### Multi-TFM and Multi-OS Matrix
```yaml
jobs:
test:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
dotnet-version: ['8.0.x', '9.0.x']
include:
- os: ubuntu-latest
dotnet-version: '10.0.x'
exclude:
- os: macos-latest
dotnet-version: '8.0.x'
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Setup .NET ${{ matrix.dotnet-version }}
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ matrix.dotnet-version }}
- name: Test
run:
dotnet test --framework net${{ matrix.dotnet-version == '8.0.x' && '8.0' || matrix.dotnet-version == '9.0.x'
&& '9.0' || '10.0' }}
```text
**Key decisions:**
- `fail-fast: false` ensures all matrix combinations run even if one fails, giving full signal on which platforms/TFMs
are broken
- `include` adds specific combinations not in the Cartesian product
- `exclude` removes combinations that are unnecessary or unsupported
### Dynamic Matrix from JSON
Generate matrix values dynamically for complex scenarios:
```yaml
jobs:
compute-matrix:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- uses: actions/checkout@v4
- id: set-matrix
shell: bash
run: |
set -euo pipefail
# Extract TFMs from Directory.Build.props or csproj files
TFMS=$(grep -rh '<TargetFrameworks\?>' **/*.csproj | \
sed 's/.*<TargetFrameworks\?>//' | sed 's/<.*//' | \
tr ';' '\n' | sort -u | jq -R . | jq -sc .)
echo "matrix={\"tfm\":$TFMS}" >> "$GITHUB_OUTPUT"
test:
needs: compute-matrix
strategy:
matrix: ${{ fromJson(needs.compute-matrix.outputs.matrix) }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: dotnet test --framework ${{ matrix.tfm }}
```text
---
## Path-Based Triggers
### Selective Workflow Execution
Trigger workflows only when relevant files change. Reduces CI cost and feedback time:
```yaml
on:
push:
branches: [main]
paths:
- 'src/**'
- 'tests/**'
- '*.sln'
- 'Directory.Build.props'
- 'Directory.Packages.props'
- '.github/workflows/ci.yml'
pull_request:
branches: [main]
paths:
- 'src/**'
- 'tests/**'
- '*.sln'
- 'Directory.Build.props'
- 'Directory.Packages.props'
```xml
### Ignoring Non-Code Changes
Use `paths-ignore` to skip builds for documentation-only changes:
```yaml
on:
push:
branches: [main]
paths-ignore:
- 'docs/**'
- '*.md'
- 'LICENSE'
- '.editorconfig'
```markdown
**Choose `paths` or `paths-ignore`, not both.** When both are specified on the same event, `paths-ignore` is ignored.
Use `paths` (allowlist) for focused workflows; use `paths-ignore` (denylist) for broad workflows.
---
## Concurrency Groups
### Cancelling Duplicate Runs
Prevent wasted CI time by cancelling in-progress runs when new commits are pushed to the same branch or PR:
```yaml
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
```yaml
### Environment-Scoped Concurrency
Prevent parallel deployments to the same environment:
```yaml
concurrency:
group: deploy-production
cancel-in-progress: false # queue, do not cancel deployments
```yaml
Use `cancel-in-progress: true` for build/test (newer commit supersedes older), but `cancel-in-progress: false` for
deployments (do not cancel an in-progress deploy).
---
## Environment Protection Rules
### Configuring Environments
```yaml
jobs:
deploy-staging:
runs-on: ubuntu-latest
environment:
name: staging
url: https://staging.example.com
steps:
- name: Deploy to staging
run: echo "Deploying..."
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
environment:
name: production
url: https://example.com
steps:
- name: Deploy to production
run: echo "Deploying..."
```text
Configure protection rules in GitHub Settings > Environments:
| Rule | Purpose |
| ---------------------------------- | ---------------------------------------------- |
| Required reviewers | Manual approval before deployment |
| Wait timer | Cooldown period (e.g., 15 minutes) |
| Branch restrictions | Only `main` or `release/*` branches can deploy |
| Custom deployment protection rules | Third-party integrations (monitoring checks) |
### Environment Secrets
Environments can have their own secrets that override repository-level secrets. Use environment-scoped secrets for
deployment credentials:
```yaml
jobs:
deploy:
environment: production
runs-on: ubuntu-latest
steps:
- name: Deploy
env:
# These resolve to environment-specific values
CONNECTION_STRING: ${{ secrets.CONNECTION_STRING }}
API_KEY: ${{ secrets.API_KEY }}
run: ./deploy.sh
```text
---
## Caching Strategies
### NuGet Package Cache
```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 }}-
```csharp
The `restore-keys` prefix match ensures a partial cache hit when csproj files change (most packages remain cached).
### .NET SDK Cache
For self-hosted runners or scenarios where SDK installation is slow:
```yaml
- name: Setup .NET with cache
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
cache: true
cache-dependency-path: '**/packages.lock.json'
```json
The `cache: true` option in `actions/setup-dotnet@v4` enables built-in NuGet caching using `packages.lock.json` as the
cache key.
### Build Output Cache (.NET 9+)
.NET 9 introduced MSBuild build-check caching. For incremental CI builds:
```yaml
- name: Cache build output
uses: actions/cache@v4
with:
path: |
**/bin/
**/obj/
key: build-${{ runner.os }}-${{ hashFiles('**/*.csproj', '**/*.cs') }}
restore-keys: |
build-${{ runner.os }}-
```csharp
Use build output caching cautiously -- stale caches can mask build errors. Prefer NuGet caching as the primary CI speed
optimization.
---
## `workflow_dispatch` Inputs
### Manual Trigger with Parameters
```yaml
on:
workflow_dispatch:
inputs:
environment:
description: 'Target deployment environment'
required: true
type: choice
options:
- staging
- production
default: staging
version:
description: 'Version to deploy (e.g., 1.2.3)'
required: true
type: string
dry-run:
description: 'Simulate deployment without applying changes'
required: false
type: boolean
default: false
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
steps:
- uses: actions/checkout@v4
with:
ref: v${{ inputs.version }}
- name: Deploy
env:
DRY_RUN: ${{ inputs.dry-run }}
run: |
set -euo pipefail
if [ "$DRY_RUN" = "true" ]; then
echo "DRY RUN: would deploy v${{ inputs.version }} to ${{ inputs.environment }}"
else
./deploy.sh --version ${{ inputs.version }}
fi
```text
Input types: `string`, `boolean`, `choice`, `environment` (selects from configured environments).
---
## Agent Gotchas
1. **Do not mix `paths` and `paths-ignore` on the same event** -- when both are specified, `paths-ignore` is silently
ignored. Use one or the other.
2. **Set `fail-fast: false` on matrix builds** -- default `fail-fast: true` cancels sibling jobs when one fails, hiding
which other combinations also break.
3. **Use `set -euo pipefail` in all bash steps** -- without `pipefail`, a non-zero exit from a piped command (e.g.,
`script | tee`) does not fail the step.
4. **Reusable workflow inputs are strings by default** -- boolean and number types must be explicitly declared with
`type:` in the workflow_call inputs.
5. **Cache keys must include `runner.os`** -- NuGet packages are OS-dependent; a Linux-built cache restoring on Windows
causes restore failures.
6. **Do not hardcode TFMs in workflow files** -- use matrix variables or extract from csproj to keep workflows in sync
with project configuration.
7. **`secrets: inherit` passes all caller secrets** -- use explicit secret declarations for security-sensitive reusable
workflows to limit exposure.
8. **Concurrency groups for deploys must use `cancel-in-progress: false`** -- cancelling an in-progress deployment can
leave infrastructure in an inconsistent state.
````
## 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:**
```text
# 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"
```Related Skills
dotnet-wpf
.NET WPF component and application patterns Triggers on: **/*.xaml, **/*.cs
dbt-transformation-patterns
Master dbt (data build tool) for analytics engineering with model organization, testing, documentation, and incremental strategies. Use when building data transformations, creating data models, or ...
data-fetching-patterns
Explains data fetching strategies including fetch on render, fetch then render, render as you fetch, and server-side data fetching. Use when implementing data loading, optimizing loading performance, or choosing between client and server data fetching.
airflow-dag-patterns
Build production Apache Airflow DAGs with best practices for operators, sensors, testing, and deployment. Use when creating data pipelines, orchestrating workflows, or scheduling batch jobs.
ai-product-patterns
Builds AI-native products using OpenAI's development philosophy and modern AI UX patterns. Use when integrating AI features, designing for model improvements, implementing evals as product specs, or creating AI-first experiences. Based on Kevin Weil (OpenAI CPO) on building for future models, hybrid approaches, and cost optimization.
a2a-executor-patterns
Agent-to-Agent (A2A) executor implementation patterns for task handling, execution management, and agent coordination. Use when building A2A executors, implementing task handlers, creating agent execution flows, or when user mentions A2A protocol, task execution, agent executors, task handlers, or agent coordination.
GitOps Patterns
ArgoCD ApplicationSets, progressive delivery, Harness GitX, and multi-cluster GitOps patterns
bats-testing-patterns
Comprehensive guide for writing shell script tests using Bats (Bash Automated Testing System). Use when writing or improving tests for Bash/shell scripts, creating test fixtures, mocking commands, or setting up CI/CD for shell script testing. Includes patterns for assertions, setup/teardown, mocking, fixtures, and integration with GitHub Actions.
bash-defensive-patterns
Master defensive Bash programming techniques for production-grade scripts. Use when writing robust shell scripts, CI/CD pipelines, or system utilities requiring fault tolerance and safety.
apollo-client-patterns
Use when implementing Apollo Client patterns for queries, mutations, cache management, and local state in React applications.
url-routing-patterns
Use when designing URL structures, slug generation, SEO-friendly URLs, redirects, or localized URL patterns. Covers route configuration, URL rewriting, canonical URLs, and routing APIs for headless CMS.
sns-patterns
SNS posting patterns and strategy