Agent skill
dotnet-cicd-patterns
Implement CI/CD for .NET applications with GitHub Actions, Azure DevOps, Docker, Kubernetes deployments, automated testing, and release pipelines. Use when setting up continuous integration and deployment for .NET projects.
Stars
163
Forks
31
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/devops/dotnet-cicd-patterns-onesmartguy-next-level-real-esta
SKILL.md
.NET CI/CD Patterns
Master CI/CD for .NET 8+ with GitHub Actions, Azure DevOps, Docker, and Kubernetes.
GitHub Actions - .NET CI/CD
yaml
# .github/workflows/dotnet-ci.yml
name: .NET CI/CD
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
env:
DOTNET_VERSION: '8.0.x'
AZURE_WEBAPP_NAME: myapp
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore --configuration Release
- name: Test
run: dotnet test --no-build --configuration Release --verbosity normal --collect:"XPlat Code Coverage"
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: '**/coverage.cobertura.xml'
build-docker:
needs: build-and-test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:latest
ghcr.io/${{ github.repository }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
deploy-azure:
needs: build-docker
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Azure Login
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Deploy to Azure Web App
uses: azure/webapps-deploy@v2
with:
app-name: ${{ env.AZURE_WEBAPP_NAME }}
images: 'ghcr.io/${{ github.repository }}:${{ github.sha }}'
Azure DevOps Pipeline
yaml
# azure-pipelines.yml
trigger:
branches:
include:
- main
- develop
pool:
vmImage: 'ubuntu-latest'
variables:
buildConfiguration: 'Release'
dotnetSdkVersion: '8.x'
stages:
- stage: Build
displayName: 'Build and Test'
jobs:
- job: BuildJob
steps:
- task: UseDotNet@2
displayName: 'Use .NET SDK'
inputs:
version: $(dotnetSdkVersion)
- task: DotNetCoreCLI@2
displayName: 'Restore packages'
inputs:
command: 'restore'
- task: DotNetCoreCLI@2
displayName: 'Build'
inputs:
command: 'build'
arguments: '--configuration $(buildConfiguration) --no-restore'
- task: DotNetCoreCLI@2
displayName: 'Run tests'
inputs:
command: 'test'
arguments: '--configuration $(buildConfiguration) --no-build --collect:"XPlat Code Coverage"'
publishTestResults: true
- task: PublishCodeCoverageResults@1
displayName: 'Publish code coverage'
inputs:
codeCoverageTool: 'Cobertura'
summaryFileLocation: '$(Agent.TempDirectory)/**/*coverage.cobertura.xml'
- task: DotNetCoreCLI@2
displayName: 'Publish'
inputs:
command: 'publish'
publishWebProjects: true
arguments: '--configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)'
- task: PublishBuildArtifacts@1
displayName: 'Publish artifacts'
inputs:
pathToPublish: '$(Build.ArtifactStagingDirectory)'
artifactName: 'drop'
- stage: Deploy
displayName: 'Deploy to Azure'
dependsOn: Build
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
jobs:
- deployment: DeployJob
environment: 'production'
strategy:
runOnce:
deploy:
steps:
- task: AzureWebApp@1
displayName: 'Deploy to Azure Web App'
inputs:
azureSubscription: '$(azureSubscription)'
appName: '$(webAppName)'
package: '$(Pipeline.Workspace)/drop/**/*.zip'
Docker Multi-Stage Build
dockerfile
# Dockerfile
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
# Copy csproj and restore
COPY ["src/MyApp.Api/MyApp.Api.csproj", "src/MyApp.Api/"]
COPY ["src/MyApp.Application/MyApp.Application.csproj", "src/MyApp.Application/"]
COPY ["src/MyApp.Domain/MyApp.Domain.csproj", "src/MyApp.Domain/"]
COPY ["src/MyApp.Infrastructure/MyApp.Infrastructure.csproj", "src/MyApp.Infrastructure/"]
RUN dotnet restore "src/MyApp.Api/MyApp.Api.csproj"
# Copy everything and build
COPY . .
WORKDIR "/src/src/MyApp.Api"
RUN dotnet build "MyApp.Api.csproj" -c Release -o /app/build
# Publish
FROM build AS publish
RUN dotnet publish "MyApp.Api.csproj" -c Release -o /app/publish /p:UseAppHost=false
# Runtime
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final
WORKDIR /app
EXPOSE 80
EXPOSE 443
# Non-root user
RUN adduser --disabled-password --gecos '' appuser && chown -R appuser /app
USER appuser
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "MyApp.Api.dll"]
Kubernetes Deployment
yaml
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: api
image: ghcr.io/myorg/myapp:latest
ports:
- containerPort: 80
env:
- name: ASPNETCORE_ENVIRONMENT
value: "Production"
- name: ConnectionStrings__DefaultConnection
valueFrom:
secretKeyRef:
name: myapp-secrets
key: db-connection-string
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health/live
port: 80
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health/ready
port: 80
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 80
type: LoadBalancer
Database Migrations in CI/CD
yaml
# .github/workflows/db-migration.yml
name: Database Migration
on:
workflow_dispatch:
push:
branches: [main]
paths:
- 'src/**/Migrations/**'
jobs:
migrate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Install EF Core tools
run: dotnet tool install --global dotnet-ef
- name: Apply migrations
run: |
dotnet ef database update \
--project src/MyApp.Infrastructure \
--startup-project src/MyApp.Api \
--connection "${{ secrets.DB_CONNECTION_STRING }}"
env:
ASPNETCORE_ENVIRONMENT: Production
Automated Versioning
yaml
# Using GitVersion
- name: Install GitVersion
uses: gittools/actions/gitversion/setup@v0
with:
versionSpec: '5.x'
- name: Determine Version
uses: gittools/actions/gitversion/execute@v0
id: gitversion
- name: Display version
run: |
echo "Version: ${{ steps.gitversion.outputs.semVer }}"
echo "AssemblyVersion: ${{ steps.gitversion.outputs.assemblySemVer }}"
- name: Build with version
run: dotnet build -p:Version=${{ steps.gitversion.outputs.semVer }}
Release Management
yaml
# GitHub Release
- name: Create Release
if: github.ref == 'refs/heads/main'
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: v${{ steps.gitversion.outputs.semVer }}
release_name: Release v${{ steps.gitversion.outputs.semVer }}
body: |
Changes in this release:
${{ github.event.head_commit.message }}
draft: false
prerelease: false
Best Practices
- Separate Build and Deploy - Build once, deploy many
- Use Secrets Management - Never hardcode credentials
- Implement Health Checks - For readiness and liveness probes
- Version Everything - Code, containers, deployments
- Test Before Deploy - Run all tests in CI
- Use Caching - Cache NuGet packages and Docker layers
- Monitor Deployments - Track deployment success rates
Didn't find tool you were looking for?