CI/CD for C#: How to Stop Deploying Manually and Start Living
\n\nIf you are still building your C# project locally, uploading builds via FTP, and praying that nothing breaks — I have news for you. In 2025, it's shameful to do this. Even for a small pet project.
\n\nCI/CD is not the magic of senior developers earning 300k, but simply a set of scripts that handle the routine for you. Today, we will set up a full-fledged CI/CD pipeline for C# from scratch. No fluff, with real examples and error analysis.
\n\nBy the way, if you write in Python — we also have automation guides on pythonlib.ru. But today we're working with C#.
\n\nWhat is CI/CD and Why Does a C# Developer Need It
\n\nCI (Continuous Integration) — this is when every commit you make to Git is automatically built and tested. No more "but it works on my machine."
\n\nCD (Continuous Delivery/Deployment) — this is when, after a successful build, the code automatically goes to the server (or the cloud).
\n\nFor C#, this is especially relevant: building via dotnet build can differ on different machines, and almost everyone forgets to run tests. CI/CD solves both problems.
Choosing a Tool: GitHub Actions vs GitLab CI vs Jenkins
\n\nTo start, I recommend GitHub Actions. Why?
\n\n- \n
- Free for public repositories (yes, for C# too) \n
- No need to set up a server \n
- Ready-made actions for .NET \n
- YAML — easy to read \n
If you have GitLab — the principle is the same, the syntax is similar. We'll leave Jenkins for corporate hell.
\n\nWriting Your First Pipeline for a C# Project
\n\nCreate a file .github/workflows/dotnet.yml in the root of your repository. Yes, the file must be exactly in this folder.
Here is a minimal working pipeline that builds the project and runs tests:
\n\nname: .NET CI\n\non:\n push:\n branches: [ main, develop ]\n pull_request:\n branches: [ main ]\n\njobs:\n build-and-test:\n runs-on: ubuntu-latest\n\n steps:\n - uses: actions/checkout@v4\n\n - name: Setup .NET\n uses: actions/setup-dotnet@v4\n with:\n dotnet-version: '8.0.x'\n\n - name: Restore dependencies\n run: dotnet restore\n\n - name: Build\n run: dotnet build --no-restore --configuration Release\n\n - name: Run tests\n run: dotnet test --no-build --configuration Release --verbosity normal\n\nLet's break it down step by step:
\n\n- \n
on: push— the pipeline runs on a push to main or develop \n runs-on: ubuntu-latest— we build on Linux (C# under .NET Core/8+ works everywhere) \n setup-dotnet— installs the required .NET SDK version \n dotnet restore— restores NuGet packages \n dotnet build— compiles the release version \n dotnet test— runs all tests from the project \n
After pushing, go to the Actions tab of your repository — you will see the pipeline running. If the tests fail, you will get a red status and an email notification.
\n\nAdding Code Coverage (because tests without coverage are a sham)
\n\nA good pipeline should show how much code is covered by tests. For C#, we use coverlet in conjunction with ReportGenerator.
\n\nFirst, add NuGet packages to your test project:
\n\ndotnet add YourTestProject.csproj package coverlet.collector\ndotnet add YourTestProject.csproj package coverlet.msbuild\n\nNow, update the test step in the pipeline:
\n\n - name: Run tests with coverage\n run: |\n dotnet test --no-build --configuration Release \\\n /p:CollectCoverage=true \\\n /p:CoverletOutputFormat=opencover \\\n /p:CoverletOutput=./TestResults/coverage.xml\n\n - name: Install ReportGenerator\n run: dotnet t