Introduction: Why CI/CD is Important for a C# Developer
In modern software development, manual testing and deployment are inefficient and risky. Every time a developer pushes code to a repository, there is a need to ensure that the build hasn't broken, tests pass, and the application is ready for deployment. This is where CI/CD (Continuous Integration / Continuous Deployment) comes to the rescue.
GitHub Actions is a built-in automation tool in GitHub that allows you to create workflows directly in your repository. It is ideal for C# (.NET) projects, as it supports both Windows and Linux runners, and also has a rich ecosystem of ready-made actions.
In this article, we will break down how to set up a full CI/CD pipeline for a C# project using GitHub Actions: from building and testing to publishing a NuGet package or deploying a web application.
GitHub Actions Basics: YAML and Workflow Structure
Each workflow in GitHub Actions is described in a YAML file, which is stored in the .github/workflows/ directory of your repository. Let's look at the basic structure:
name: .NET CI/CD
on: push: branches: [ main, develop ] pull_request: branches: [ main ]
jobs: build: runs-on: ubuntu-latest
steps: - name: Checkout code uses: actions/checkout@v4
- name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: '8.x'
- name: Restore dependencies run: dotnet restore
- name: Build run: dotnet build --configuration Release --no-restore
- name: Run tests run: dotnet test --configuration Release --no-build --verbosity normalKey elements:
- on — triggers for execution (push, pull_request, schedule, etc.)
- jobs — a set of tasks that run in parallel or sequentially
- runs-on — the operating system for execution (ubuntu-latest, windows-latest, macos-latest)
- steps — a sequence of commands or ready-made actions
Important: for .NET projects, use the actions/setup-dotnet action with the SDK version specified. This guarantees that the environment will contain the required .NET version.
Setting Up Testing and Linting for C# Code
Quality CI/CD cannot do without automated tests and code style checks. Let's add unit testing and static analysis stages to our workflow.
Unit Tests with xUnit and Code Coverage
Suppose you have a project with tests using xUnit. Let's add a step to generate a coverage report using Coverlet:
- name: Run tests with coverage run: | dotnet test --configuration Release \ --no-build \ --verbosity normal \ /p:CollectCoverage=true \ /p:CoverletOutputFormat=opencover \ /p:CoverletOutput=./coverage.xml
- name: Upload coverage to Codecov uses: codecov/codecov-action@v3 with: files: ./coverage.xml flags: unittests fail_ci_if_error: falseFor this, you need to add the coverlet.collector package to the test project. Example command:
dotnet add package coverlet.collectorStatic Analysis with Roslyn Analyzers
Let's add code checking using built-in .NET analyzers:
- name: Run Roslyn analyzers run: dotnet build --configuration Release --warnaserror -p:RunAnalyzersDuringBuild=trueYou can also use third-party tools, for example, SonarCloud:
- name: SonarCloud Scan uses: SonarSource/sonarcloud-github-action@master env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SON