ci-cd

>

codewithmukesh/dotnet-claude-kit652 installsMITSynced Aug 26

Works with

Claude CodeCursorCodex CLIGitHub CopilotGemini CLI
---
name: ci-cd
description: >
license: MIT
---

# CI/CD

## Core Principles

1. **Pipeline as code** — YAML pipelines committed to the repo. No click-ops in the UI.
2. **Fast feedback** — Build and test on every push. Cache NuGet packages. Fail fast.
3. **Build once, deploy many** — Build the artifact once, promote it through environments (dev → staging → production).
4. **Never skip tests** — Tests gate the pipeline. No deployment without passing tests.

## Patterns

### GitHub Actions — Build + Test

```yaml
# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

env:
  DOTNET_VERSION: '10.0.x'
  DOTNET_NOLOGO: true
  DOTNET_CLI_TELEMETRY_OPTOUT: true

jobs:
  build-and-test:
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:18
        env:
          POSTGRES_DB: testdb
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - uses: actions/checkout@v5

      - name: Setup .NET
        uses: actions/setup-dotnet@v5
        with:
          dotnet-version: ${{ env.DOTNET_VERSION }}

      - name: Restore
        run: dotnet restore

      - name: Build
        run: dotnet build --no-restore --configuration Release

      - name: Format check
        run: dotnet format --verify-no-changes --no-restore

      - name: Test
        run: dotnet test --no-build --configuration Release --logger trx --results-directory TestResults
        env:
          ConnectionStrings__Default: "Host=localhost;Database=testdb;Username=postgres;Password=postgres"

      - name: Publish test results
        uses: actions/upload-artifact@v5
        if: always()
        with:
          name: test-results
          path: TestResults/*.trx
```

### GitHub Actions — Build + Publish Docker Image

```yaml
# .github/workflows/publish.yml
name: Publish

on:
  push:
    tags: ['v*']

jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - uses: actions/checkout@v5

      - name: Login to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract version from tag
        id: version
        run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT

      - name: Build and push
        uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: |
            ghcr.io/${{ github.repository }}:${{ steps.version.outputs.VERSION }}
            ghcr.io/${{ github.repository }}:latest
```

### Azure DevOps — Build + Test

Same restore → build → format → test flow as GitHub Actions. Key differences:

```yaml
# azure-pipelines.yml
trigger:
  branches:
    include: [main]
  paths:
    exclude: ['*.md', docs/]

pool:
  vmImage: 'ubuntu-latest'          # vs runs-on: ubuntu-latest

variables:
  dotnetVersion: '10.0.x'

# Key task differences from GitHub Actions:
#   Setup .NET:  task: UseDotNet@2  (inputs: version: $(dotnetVersion))
#   Test results: task: PublishTestResults@2  (testResultsFormat: VSTest)
#   Steps use `script:` + `displayName:` instead of `- name:` + `run:`
#   Services (e.g., Postgres) require a separate Docker task or pipeline service connection
```

### NuGet Package Publishing

```yaml
# Part of GitHub Actions workflow
- name: Pack
  run: dotnet pack src/MyLibrary -c Release -o ./nupkg --no-build

- name: Push to NuGet
  run: dotnet nuget push ./nupkg/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json
```

## Anti-patterns

### Don't Build Different Artifacts per Environment

```yaml
# BAD — building separately for each environment
- script: dotnet publish -c Debug   # for dev
- script: dotnet publish -c Release # for prod

# GOOD — build once, deploy everywhere
- script: dotnet publish -c Release -o ./publish
# Then deploy the same ./publish artifact to dev, staging, prod
```

### Don't Skip Format Checks in CI

```yaml
# BAD — no format enforcement
steps:
  - run: dotnet build
  - run: dotnet test

# GOOD — format check catches style issues early
steps:
  - run: dotnet build
  - run: dotnet format --verify-no-changes
  - run: dotnet test
```

### Don't Hardcode Secrets in Pipelines

```yaml
# BAD — secret in pipeline YAML
env:
  DB_PASSWORD: "my-secret-password"

# GOOD — use pipeline secrets
env:
  DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
```

## Decision Guide

| Scenario | Recommendation |
|----------|---------------|
| Open source project | GitHub Actions |
| Enterprise with Azure | Azure DevOps Pipelines |
| Docker deployment | Multi-stage build in CI, push to container registry |
| NuGet library | Build → Test → Pack → Push on tag |
| Database migrations | Run in CI test stage, script for production |
| Environment promotion | Same artifact, different configuration |

More General & Other skills

← All General & Other skills

Check your AI visibility

One URL in, a 0–100 score and the exact fixes out.

RUN THE CHECK

Browse all the tools

15 tools across six categories
13 of them never send your data anywhere

Free · No signup · No trial clock

SEE THE DIRECTORY