GitHub Actions simplifies CI/CD for .NET developers. Here's what you need to know:
- Built into GitHub, no extra setup needed
- Responds to GitHub events like pushes and pull requests
- Free for public repos, 2,000 minutes/month for private ones
- Access to over 10,000 pre-written actions
Key steps to set up GitHub Actions for .NET CI/CD:
- Create a GitHub repo and .NET project
- Set up
.github/workflows
folder - Create YAML workflow file
- Configure triggers (e.g., push, pull request)
- Set up build job (choose runner, install .NET SDK)
- Add tests and code quality checks
- Package the app for deployment
- Deploy to staging and production environments
Quick Comparison: GitHub Actions vs Jenkins
Feature | GitHub Actions | Jenkins |
---|---|---|
Setup | Easy, built-in | More complex |
Customization | Good for most cases | Highly flexible |
Learning curve | Gentle (YAML) | Steeper |
Hosting | GitHub or self-hosted | Self-hosted |
Plugins | Growing marketplace | Mature ecosystem |
This guide covers everything from basic setup to advanced tips for optimizing your .NET CI/CD pipeline using GitHub Actions.
Related video from YouTube
Before you start
Setting up GitHub Actions for .NET CI/CD? Here's what you need:
GitHub and .NET prep
1. GitHub account and repo
No GitHub account? Create one. Then:
- Click "New repository" on GitHub
- Name it, set to public (free Actions minutes)
- Clone to your machine
2. .NET project
Your project should:
- Use .NET Core SDK 6.0+
- Have working build and tests
- Include a .NET
.gitignore
3. Actions folder
Create .github/workflows
in your repo's root. This holds your workflow files.
YAML crash course
GitHub Actions uses YAML. Here's the gist:
name: .NET CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.0.x'
- run: dotnet build
This workflow builds your project on every push.
Pro tip: Use spaces, not tabs, in YAML. Indentation matters!
Now you're set to create your first .NET CI/CD workflow with GitHub Actions.
Setting up your GitHub repository
Let's get your GitHub repo ready for CI/CD with GitHub Actions. Here's what you need to do:
Repository settings
Head to your repo's settings page and tweak these key settings:
- Turn on branch protection
- Require pull request reviews
- Enable status checks
- Set up auto-merge
Quick setup:
- Go to "Settings" in your repo
- Click "Branches" on the left
- Hit "Add rule" under "Branch protection rules"
- Pick your branch (like
main
) - Choose your rules
- Save changes
Managing branches
For smooth CI/CD, set up two main branches:
- Main branch: Your production-ready code
- Development branch: For ongoing work
To create a new branch:
git checkout -b development
git push -u origin development
That's it! You're now ready to build your CI/CD pipeline with GitHub Actions. Next up: creating your first workflow file.
Creating a workflow file
Let's set up a workflow file for your .NET project. This file tells GitHub Actions how to build, test, and deploy your app.
Workflow file basics
A GitHub Actions workflow file has four main parts:
- Name: What your workflow does
- Triggers: When it runs
- Jobs: Groups of tasks
- Steps: Individual tasks
Here's a simple workflow for a .NET project:
name: .NET CI/CD
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: '8.0.x'
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Test
run: dotnet test --no-build --verbosity normal
This workflow runs when you push or make a pull request to the main branch. It uses Ubuntu, checks out your code, sets up .NET 8, restores dependencies, builds your project, and runs tests.
Where to put the file
GitHub Actions needs your workflow file in a specific spot:
- Make a
.github
folder in your repo's main directory - Inside
.github
, create aworkflows
folder - Add your workflow file (like
dotnet-ci-cd.yml
) inworkflows
Your file structure should look like this:
your-repo/
.github/
workflows/
dotnet-ci-cd.yml
You can add this file right on GitHub:
- Go to your GitHub repo
- Click "Actions"
- Pick "New workflow"
- Choose "set up a workflow yourself"
- Name your file and paste in the YAML
- Commit the file
That's it! Your workflow is ready to go.
Setting up workflow triggers
GitHub Actions kicks off your CI/CD when specific events happen in your repo. Here's how to set it up for .NET:
Push and pull request triggers
Want your workflow to run when someone pushes to main or opens a PR? Here's the YAML:
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
This fires when:
- Code hits the main branch
- A PR targeting main is opened, reopened, or updated
Need more specific triggers? Try this:
on:
push:
branches:
- 'main'
- 'releases/**'
tags:
- 'v*'
Now your workflow runs when:
- Main gets new code
- Any "releases/" branch is updated
- A new "v" tag is created
Timed triggers
Got tasks that need a schedule? Use schedule
with cron:
on:
schedule:
- cron: '0 2 * * 0' # 2 AM every Sunday
Want multiple schedules? No problem:
on:
schedule:
- cron: '0 2 * * 0' # 2 AM every Sunday
- cron: '0 12 * * 1-5' # Noon Monday to Friday
Here's a quick rundown of trigger types:
Trigger | When It Runs | What It's For |
---|---|---|
Push | Code pushed to specific branches | Run tests, build code |
Pull Request | PRs opened, reopened, or updated | Check proposed changes |
Schedule | Set times (cron syntax) | Regular maintenance |
Setting up the build job
Let's create a build job for our .NET project. This job will run our code, restore dependencies, and compile the app.
Choosing where to run
First, pick an environment for your build:
Runner | OS | Best for |
---|---|---|
ubuntu-latest | Linux | Most .NET Core projects |
windows-latest | Windows | .NET Framework or Windows-specific builds |
macos-latest | macOS | iOS or macOS apps |
For most .NET Core projects, go with ubuntu-latest
. It's fast and works well with cross-platform .NET apps.
In your workflow:
jobs:
build:
runs-on: ubuntu-latest
Installing .NET SDK
Next, set up the .NET SDK:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '6.0.x'
Need multiple versions? List them:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
3.1.x
6.0.x
Getting dependencies and building
Now, restore dependencies and build:
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --configuration Release --no-restore
Here's a complete build job example:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '6.0.x'
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --configuration Release --no-restore
- name: Test
run: dotnet test --no-restore --verbosity normal
This job checks out your code, sets up .NET, restores packages, builds the project, and runs tests. Simple, right?
Adding tests
Let's add tests to your GitHub Actions workflow. This helps catch bugs early and keeps your code in good shape.
Running unit tests
After building your project, add this step to run tests:
- name: Run tests
run: dotnet test --configuration Release --verbosity normal --logger trx --collect:"XPlat Code Coverage"
This command runs tests, logs results, and collects coverage data.
Got multiple test projects? Combine coverage reports:
- name: Combine Coverage Reports
uses: danielpalme/ReportGenerator-GitHub-Action@5.2.4
with:
reports: "**/*.cobertura.xml"
targetdir: "${{ github.workspace }}"
reporttypes: "Cobertura"
Creating test reports
Make test results easy to find:
- name: Upload Test Result Files
uses: actions/upload-artifact@v4
with:
name: test-results
path: ${{ github.workspace }}/**/TestResults/**/*
Want a detailed view? Use this:
- name: Publish Test Results
uses: EnricoMi/publish-unit-test-result-action@v2.16.1
with:
trx_files: "${{ github.workspace }}/**/*.trx"
It shows test results in pull requests and workflow runs.
For a code coverage report:
- name: Publish Code Coverage Report
uses: irongut/CodeCoverageSummary@v1.3.0
with:
filename: "Cobertura.xml"
format: markdown
This creates a Markdown summary of your coverage. You can add it to PR comments or docs.
sbb-itb-29cd4f6
Checking code quality
Want to catch issues early and keep your codebase clean? Add automated code checks to your GitHub Actions workflow. Here's how to do it for .NET projects:
Using code checkers
SonarLint and StyleCop are go-to tools for .NET code analysis. Here's how to set them up:
1. SonarLint
First, grab the SonarAnalyzer.CSharp NuGet package. It'll hunt down bugs, vulnerabilities, and code smells. Add this to your workflow:
- name: Install SonarAnalyzer
run: dotnet add package SonarAnalyzer.CSharp
- name: Run SonarAnalyzer
run: dotnet build-server shutdown && dotnet build /p:TreatWarningsAsErrors=true
2. StyleCop
For StyleCop, you'll need the StyleCop.Analyzers package. Don't forget to create a Directory.Build.props
file in your solution root. Here's the workflow addition:
- name: Install StyleCop
run: dotnet add package StyleCop.Analyzers
- name: Run StyleCop
run: dotnet build /p:TreatWarningsAsErrors=true
Setting quality rules
Want to define your code quality standards? Create an .editorconfig
file in your repo. It's like a rulebook for your code. Here's a quick example:
root = true
[*.cs]
dotnet_analyzer_diagnostic.category-Style.severity = warning
dotnet_code_quality.CA1062.null_check_validation_methods = ThrowArgumentNullException
To make sure these rules are followed, add this to your workflow:
- name: Enforce code style
run: dotnet format --verify-no-changes --verbosity diagnostic
With these tools in place, you're on your way to cleaner, more consistent code. Happy coding!
Packaging the app
Let's talk about packaging your .NET app for deployment. It's a crucial step in your CI/CD pipeline.
Creating deployment packages
Here's how to package your .NET app using GitHub Actions:
- name: Publish
run: dotnet publish -c Release -o ./publish
This builds your app and puts the files in a publish
directory.
For web apps, you might want a Web Deploy package:
- name: Create Web Deploy Package
run: msbuild /p:Configuration=Release /p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:PackageLocation="./publish"
This creates a zip with your app and deployment scripts.
Managing versions
Versioning helps track changes. Here's how to do it automatically:
1. Install GitVersion:
- name: Install GitVersion
uses: gittools/actions/gitversion/setup@v0.9.7
with:
versionSpec: '5.x'
2. Get the version:
- name: Determine Version
id: gitversion
uses: gittools/actions/gitversion/execute@v0.9.7
3. Use it in your build:
- name: Build
run: dotnet build --configuration Release /p:Version=${{ steps.gitversion.outputs.semVer }}
This updates your app's version based on Git history.
To include version info in your app, add this to your .csproj
:
<PropertyGroup>
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>
<InformationalVersion>$(Version)</InformationalVersion>
</PropertyGroup>
Deploying to different environments
Let's set up automated deployments for staging and production using GitHub Actions.
Staging deployment
Create staging-deploy.yml
in .github/workflows/
:
on:
push:
branches:
- develop
jobs:
deploy-staging:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.0.x'
- run: |
dotnet build --configuration Release
dotnet publish -c Release -o ./publish
- uses: azure/webapps-deploy@v2
with:
app-name: 'your-staging-app-name'
publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE_STAGING }}
package: ./publish
Add AZURE_WEBAPP_PUBLISH_PROFILE_STAGING
secret to your GitHub repo with the staging Azure Web App publish profile.
Production deployment
Create production-deploy.yml
in .github/workflows/
:
on:
workflow_dispatch:
jobs:
deploy-production:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.0.x'
- run: |
dotnet build --configuration Release
dotnet publish -c Release -o ./publish
- uses: azure/webapps-deploy@v2
with:
app-name: 'your-production-app-name'
publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE_PRODUCTION }}
package: ./publish
Add AZURE_WEBAPP_PUBLISH_PROFILE_PRODUCTION
secret with the production Azure Web App publish profile.
This setup triggers staging deployments on pushes to develop
and allows manual production deployments from the GitHub Actions tab.
Speed Up Your Workflows
Want faster GitHub Actions workflows for .NET CI/CD? Focus on two things: caching and parallel jobs.
Cache Your Stuff
Caching saves time. It stores files you use a lot. For .NET, cache your NuGet packages.
Here's how:
- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.302
cache: true
cache-dependency-path: "**/packages.lock.json"
Add this to your project files:
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
Then, restore packages like this:
- name: Restore dependencies
run: dotnet restore --locked-mode
This keeps package versions consistent and builds faster. Our tests showed a 50 MB cache size. After that, builds grabbed packages from the cache. Way quicker!
Run Jobs Together
Parallel jobs cut workflow time. Here's the setup:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: dotnet format --verify-no-changes
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: dotnet test
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: dotnet build
For jobs that need others, use needs
:
deploy:
needs: [lint, test, build]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: dotnet publish
Want to test on multiple platforms? Use a matrix:
test:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- run: dotnet test
These tweaks will make your workflows zip along. Try them out!
Checking and fixing workflows
GitHub Actions workflows can act up. Here's how to keep them in line:
Looking at workflow runs
Want to see how your workflow's doing? Easy:
- Hit up your GitHub repo
- Click "Actions"
- Pick your workflow
- Click a run for the nitty-gritty
You'll see a rundown of jobs and steps. Green checks? You're golden. Red X's? Houston, we have a problem.
For the command line junkies:
gh run list -L 5 -w "CI"
This shows your last 5 "CI" workflow runs.
Need more details? Try:
gh run view 1234567890 -v --log
Just swap out that number with your actual run ID.
Fixing failed workflows
Workflow on the fritz? Here's your game plan:
- Dive into the logs: Where's the fire?
- Check the setup: Does your runner have what it needs?
- Try it at home: Can you make it fail on your machine?
- Freshen up: Old packages can be troublemakers.
- What's new?: Did a recent commit throw a wrench in the works?
Here's a real-world fix:
A dev's .NET 6 tests were acing it locally but bombing on GitHub Actions. The error?
error NETSDK1045: The current .NET SDK does not support targeting .NET 6.0.
The fix was simple. They added this to their workflow:
- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: '6.0.x'
Boom. Problem solved. The runner now had the right .NET version to play ball.
Tips for better workflows
Want to supercharge your .NET CI/CD pipeline with GitHub Actions? Here are two key tips:
Breaking workflows into parts
Split your workflows into smaller, reusable chunks. It's like building with LEGO:
# .github/workflows/build.yml
name: Build
on: [push]
jobs:
build:
uses: ./.github/workflows/reusable-build.yml
# .github/workflows/reusable-build.yml
name: Reusable Build
on:
workflow_call:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: '6.0.x'
- run: dotnet build
Now you can reuse that build job anywhere. Less copy-paste, more efficiency.
Keeping secrets safe
Don't be the person who accidentally leaks API keys. Use GitHub Secrets:
- Go to your repo's Settings > Secrets
- Add a new secret (let's call it
API_KEY
) - Use it in your workflow:
steps:
- name: Use API
env:
API_KEY: ${{ secrets.API_KEY }}
run: ./my-script.sh
Your secrets stay encrypted and hidden. No more accidental leaks!
"The worst possible way to handle secrets is to hard code them alongside the CI/CD pipeline code. [...] A good practice is to leverage the GitHub encrypted secrets functionality", says a GitHub security expert.
Remember: Good workflows are like good code - modular, reusable, and secure.
Wrap-up
You've got the tools to set up a solid CI/CD pipeline for .NET projects using GitHub Actions. Here's what we covered:
- GitHub Actions automates your .NET development workflow
- Setting up a basic pipeline is easy, but you can customize it
- Break workflows into reusable parts and secure your secrets
A good CI/CD pipeline evolves. Keep improving your workflow. Here's what to do next:
1. Start small, then grow
Build a simple workflow with build and test steps. Add more as you get comfortable.
2. Watch and improve
Use GitHub's analytics to track your workflow. Find and fix slow spots.
3. Stay current
Keep your actions and .NET SDK up to date. You'll get new features and better security.
Remember: Your pipeline should grow with your project. Keep tweaking it to fit your needs.