dotnet-ado-patterns
Composable Azure DevOps YAML pipeline patterns for .NET projects: template references with extends, stages, jobs,
and steps keywords for hierarchical pipeline composition, variable groups and variable templates for centralized
configuration, pipeline decorators for organization-wide policy injection, conditional insertion with ${{ if }} and
${{ each }} expressions, multi-stage pipelines (build, test, deploy), and pipeline triggers for CI, PR, and scheduled
runs.
Version assumptions: Azure Pipelines YAML schema. DotNetCoreCLI@2 task for .NET 8/9/10 builds. Template
expressions syntax v2.
Scope
- Template references with extends, stages, jobs, and steps keywords
- Variable groups and variable templates for centralized configuration
- Pipeline decorators for organization-wide policy injection
- Conditional insertion with ${{ if }} and ${{ each }} expressions
- Multi-stage pipelines (build, test, deploy)
- Pipeline triggers for CI, PR, and scheduled runs
Out of scope
- Starter CI templates -- see [skill:dotnet-add-ci]
- CLI release pipelines (tag-triggered build-package-release for CLI tools) -- see [skill:dotnet-cli-release-pipeline]
- ADO-unique features (environments, service connections, classic releases) -- see [skill:dotnet-ado-unique]
- Build/test specifics -- see [skill:dotnet-ado-build-test]
- Publishing pipelines -- see [skill:dotnet-ado-publish]
- GitHub Actions workflow patterns -- see [skill:dotnet-gha-patterns]
Cross-references: [skill:dotnet-add-ci] for starter templates that these patterns extend,
[skill:dotnet-cli-release-pipeline] for CLI-specific release automation.
For detailed YAML examples (stage/job/step templates, extends, variable groups, conditional insertion, multi-stage
pipelines, triggers), see examples.md in this skill directory.
Code Navigation (Serena MCP)
Primary approach: Use Serena symbol operations for efficient code navigation:
- Find definitions:
serena_find_symbol instead of text search
- Understand structure:
serena_get_symbols_overview for file organization
- Track references:
serena_find_referencing_symbols for impact analysis
- 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"
Agent Gotchas
- Template parameter types are enforced at compile time -- passing a string where
type: boolean is expected
causes a validation error before the pipeline runs; always match types exactly.
extends templates cannot be overridden -- callers cannot inject steps before or after the mandatory stages;
this is by design for policy enforcement.
- Variable group secrets are not available in template expressions --
${{ variables.mySecret }} resolves at
compile time when secrets are not yet available; use $(mySecret) runtime syntax instead.
${{ each }} iterates at compile time -- the loop generates YAML before the pipeline runs; runtime variables
cannot be used as the iteration source.
- CI and PR triggers are mutually exclusive with
trigger: none and pr: none -- omitting both trigger and pr
sections enables default CI triggering on all branches; explicitly set trigger: none to disable.
- Path filters in triggers use repository root-relative paths -- do not prefix paths with
/ or ./; use src/**
not ./src/**.
- Scheduled triggers always run on the default branch first -- the
branches.include filter applies after the
schedule fires; the schedule itself is only evaluated from the default branch YAML.
- Pipeline resource triggers require the source pipeline name, not the YAML file path -- use the pipeline name as
shown in ADO, not the
azure-pipelines.yml file path.
1---2name: dotnet-ado-patterns3category: operations4subcategory: ci-cd5description: Composes Azure DevOps YAML pipelines. Templates, variable groups, multi-stage, triggers.6license: MIT7targets: ['*']8tags: [cicd, dotnet, skill]9version: '0.0.1'10author: 'dotnet-agent-harness'11invocable: true12claudecode:13 allowed-tools: ['Read', 'Grep', 'Glob', 'Bash', 'Write', 'Edit']14codexcli:15 short-description: '.NET skill guidance for cicd tasks'16opencode:17 allowed-tools: ['Read', 'Grep', 'Glob', 'Bash', 'Write', 'Edit']18copilot: {}19geminicli: {}20antigravity: {}21---2223# dotnet-ado-patterns2425Composable Azure DevOps YAML pipeline patterns for .NET projects: template references with `extends`, `stages`, `jobs`,26and `steps` keywords for hierarchical pipeline composition, variable groups and variable templates for centralized27configuration, pipeline decorators for organization-wide policy injection, conditional insertion with `${{ if }}` and28`${{ each }}` expressions, multi-stage pipelines (build, test, deploy), and pipeline triggers for CI, PR, and scheduled29runs.3031**Version assumptions:** Azure Pipelines YAML schema. `DotNetCoreCLI@2` task for .NET 8/9/10 builds. Template32expressions syntax v2.3334## Scope3536- Template references with extends, stages, jobs, and steps keywords37- Variable groups and variable templates for centralized configuration38- Pipeline decorators for organization-wide policy injection39- Conditional insertion with ${{ if }} and ${{ each }} expressions40- Multi-stage pipelines (build, test, deploy)41- Pipeline triggers for CI, PR, and scheduled runs4243## Out of scope4445- Starter CI templates -- see [skill:dotnet-add-ci]46- CLI release pipelines (tag-triggered build-package-release for CLI tools) -- see [skill:dotnet-cli-release-pipeline]47- ADO-unique features (environments, service connections, classic releases) -- see [skill:dotnet-ado-unique]48- Build/test specifics -- see [skill:dotnet-ado-build-test]49- Publishing pipelines -- see [skill:dotnet-ado-publish]50- GitHub Actions workflow patterns -- see [skill:dotnet-gha-patterns]5152Cross-references: [skill:dotnet-add-ci] for starter templates that these patterns extend,53[skill:dotnet-cli-release-pipeline] for CLI-specific release automation.5455---5657For detailed YAML examples (stage/job/step templates, extends, variable groups, conditional insertion, multi-stage58pipelines, triggers), see `examples.md` in this skill directory.5960## Code Navigation (Serena MCP)6162**Primary approach:** Use Serena symbol operations for efficient code navigation:63641. **Find definitions**: `serena_find_symbol` instead of text search652. **Understand structure**: `serena_get_symbols_overview` for file organization663. **Track references**: `serena_find_referencing_symbols` for impact analysis674. **Precise edits**: `serena_replace_symbol_body` for clean modifications6869**When to use Serena vs traditional tools:**7071- ✅ **Use Serena**: Navigation, refactoring, dependency analysis, precise edits72- ✅ **Use Read/Grep**: Reading full files, pattern matching, simple text operations73- ✅ **Fallback**: If Serena unavailable, traditional tools work fine7475**Example workflow:**7677```text78# Instead of:79Read: src/Services/OrderService.cs80Grep: "public void ProcessOrder"8182# Use:83serena_find_symbol: "OrderService/ProcessOrder"84serena_get_symbols_overview: "src/Services/OrderService.cs"85```8687## Agent Gotchas88891. **Template parameter types are enforced at compile time** -- passing a string where `type: boolean` is expected90 causes a validation error before the pipeline runs; always match types exactly.912. **`extends` templates cannot be overridden** -- callers cannot inject steps before or after the mandatory stages;92 this is by design for policy enforcement.933. **Variable group secrets are not available in template expressions** -- `${{ variables.mySecret }}` resolves at94 compile time when secrets are not yet available; use `$(mySecret)` runtime syntax instead.954. **`${{ each }}` iterates at compile time** -- the loop generates YAML before the pipeline runs; runtime variables96 cannot be used as the iteration source.975. **CI and PR triggers are mutually exclusive with `trigger: none` and `pr: none`** -- omitting both `trigger` and `pr`98 sections enables default CI triggering on all branches; explicitly set `trigger: none` to disable.996. **Path filters in triggers use repository root-relative paths** -- do not prefix paths with `/` or `./`; use `src/**`100 not `./src/**`.1017. **Scheduled triggers always run on the default branch first** -- the `branches.include` filter applies after the102 schedule fires; the schedule itself is only evaluated from the default branch YAML.1038. **Pipeline resource triggers require the source pipeline name, not the YAML file path** -- use the pipeline name as104 shown in ADO, not the `azure-pipelines.yml` file path.