CI/CD Pipelines
Continuous Integration and Deployment Automation
CI/CD Overview
The Shirinzad E-Commerce Platform uses modern CI/CD practices to automate building, testing, and deploying the application. This ensures code quality, reduces manual errors, and accelerates the development lifecycle.
CI/CD Goals
- Automate build and test processes
- Ensure code quality through automated checks
- Enable rapid and reliable deployments
- Maintain consistent environments
- Reduce manual deployment errors
- Accelerate time to production
Pipeline Architecture
Automated CI/CD workflow from code commit to production
GitHub Actions Workflows
1. Build Pipeline
Automatically builds the solution and runs tests on every push and pull request.
Pipeline Steps
| Step | Action | Purpose |
|---|---|---|
| 1. Checkout | actions/checkout@v4 | Clone repository code |
| 2. Setup .NET | actions/setup-dotnet@v4 | Install .NET 9.0 SDK |
| 3. Restore | dotnet restore | Download NuGet packages |
| 4. Build | dotnet build --no-restore | Compile source code |
| 5. Test | dotnet test --no-build | Run all unit/integration tests |
| 6. Coverage | coverlet.collector | Generate code coverage reports |
# .github/workflows/build.yml
name: Build and Test
on:
push:
branches: [ master, develop, feature/* ]
pull_request:
branches: [ master, develop ]
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: '9.0.x'
- name: Restore dependencies
run: dotnet restore
- name: Build solution
run: dotnet build --no-restore --configuration Release
- name: Run tests
run: dotnet test --no-build --verbosity normal --configuration Release --collect:"XPlat Code Coverage"
- name: Upload coverage reports
uses: codecov/codecov-action@v3
with:
files: '**/coverage.cobertura.xml'
fail_ci_if_error: false
2. Test Pipeline with Coverage
Comprehensive testing with code coverage reporting and quality gates.
Test Coverage Configuration
# .github/workflows/test-coverage.yml
name: Test Coverage
on:
pull_request:
branches: [ master ]
jobs:
test-coverage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
- name: Install dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Test with coverage
run: |
dotnet test \
--no-build \
--verbosity normal \
/p:CollectCoverage=true \
/p:CoverletOutputFormat=opencover \
/p:CoverletOutput=./coverage/
- name: Generate coverage report
uses: danielpalme/[email protected]
with:
reports: '**/coverage.opencover.xml'
targetdir: 'coveragereport'
reporttypes: 'HtmlInline;Badges'
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
files: '**/coverage.opencover.xml'
flags: unittests
name: codecov-umbrella
- name: Check coverage threshold
run: |
COVERAGE=$(grep -oP 'Line coverage: \K[0-9.]+' coveragereport/index.html)
if (( $(echo "$COVERAGE < 80" | bc -l) )); then
echo "Coverage $COVERAGE% is below 80% threshold"
exit 1
fi
3. Docker Image Build Pipeline
Builds and publishes Docker images for containerized deployments.
# .github/workflows/docker-build.yml
name: Docker Build and Push
on:
push:
branches: [ master, develop ]
tags: [ 'v*.*.*' ]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
BUILD_CONFIGURATION=Release
4. Deployment Pipeline
Automated deployment to staging and production environments.
# .github/workflows/deploy.yml
name: Deploy to Environment
on:
push:
branches:
- develop # Deploy to staging
- master # Deploy to production
workflow_dispatch:
inputs:
environment:
description: 'Environment to deploy to'
required: true
type: choice
options:
- staging
- production
jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: ${{ github.ref == 'refs/heads/master' && 'production' || 'staging' }}
url: ${{ steps.deploy.outputs.url }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
- name: Build application
run: |
dotnet restore
dotnet build --configuration Release --no-restore
dotnet publish -c Release -o ./publish
- name: Run database migrations
env:
CONNECTION_STRING: ${{ secrets.DB_CONNECTION_STRING }}
run: |
cd src/Shirinzad.Shop.DbMigrator
dotnet run --configuration Release
- name: Deploy to Azure Web App
id: deploy
uses: azure/webapps-deploy@v2
with:
app-name: ${{ secrets.AZURE_WEBAPP_NAME }}
publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }}
package: ./publish
- name: Health check
run: |
ENDPOINT="${{ steps.deploy.outputs.url }}/health"
for i in {1..10}; do
STATUS=$(curl -s -o /dev/null -w "%{http_code}" $ENDPOINT)
if [ $STATUS -eq 200 ]; then
echo "Health check passed"
exit 0
fi
echo "Attempt $i failed, retrying..."
sleep 10
done
echo "Health check failed after 10 attempts"
exit 1
5. Code Quality Checks
Automated code quality analysis and security scanning.
# .github/workflows/code-quality.yml
name: Code Quality
on:
pull_request:
branches: [ master, develop ]
jobs:
code-analysis:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for better analysis
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
# Code formatting check
- name: Check code formatting
run: dotnet format --verify-no-changes --verbosity diagnostic
# Security scan
- name: Run security scan
uses: security-code-scan/security-code-scan-action@v1
# Dependency vulnerability check
- name: Check for vulnerabilities
run: dotnet list package --vulnerable --include-transitive
# Static code analysis
- name: Run static analysis
run: |
dotnet tool install --global dotnet-sonarscanner
dotnet sonarscanner begin \
/k:"shirinzad-shop" \
/o:"your-org" \
/d:sonar.host.url="https://sonarcloud.io" \
/d:sonar.login="${{ secrets.SONAR_TOKEN }}"
dotnet build
dotnet sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}"
Database Migration Automation
Automated database migrations ensure database schema is always in sync with the application code.
Migration Strategy
Migration Execution
# Migration script in deployment pipeline
#!/bin/bash
echo "Starting database migration..."
# Set connection string from environment
export ConnectionStrings__Default="${DB_CONNECTION_STRING}"
# Navigate to migrator project
cd src/Shirinzad.Shop.DbMigrator
# Run migrations
dotnet run --configuration Release
# Check exit code
if [ $? -eq 0 ]; then
echo "Database migration completed successfully"
else
echo "Database migration failed"
exit 1
fi
Migration Best Practices
- Always test migrations in staging first
- Create backup before applying migrations
- Use transactions for data migrations
- Keep migrations idempotent
- Add rollback scripts for critical changes
- Monitor migration execution time
Environment-Specific Configuration
Development
| Database | LocalDB |
| Cache | In-Memory |
| Logging | Console + Debug |
| HTTPS | Self-signed cert |
| Secrets | User secrets |
Staging
| Database | Azure SQL |
| Cache | Azure Redis |
| Logging | App Insights |
| HTTPS | Valid SSL cert |
| Secrets | Azure Key Vault |
Production
| Database | Azure SQL (HA) |
| Cache | Azure Redis (Premium) |
| Logging | App Insights + Seq |
| HTTPS | Valid SSL cert |
| Secrets | Azure Key Vault |
Configuration Management
- Environment variables
- appsettings.{Environment}.json
- Azure Key Vault integration
- User secrets (development)
- Configuration validation on startup
Secrets Management
Secure handling of sensitive configuration data across environments.
GitHub Secrets Configuration
| Secret Name | Description | Used In |
|---|---|---|
DB_CONNECTION_STRING |
Database connection string | Deployment, Migrations |
AZURE_WEBAPP_NAME |
Azure App Service name | Deployment |
AZURE_WEBAPP_PUBLISH_PROFILE |
Publishing credentials | Deployment |
REDIS_CONNECTION_STRING |
Redis cache connection | Deployment |
SONAR_TOKEN |
SonarCloud authentication | Code quality checks |
CODECOV_TOKEN |
Codecov authentication | Coverage reporting |
Local Development Secrets
# Initialize user secrets
cd src/Shirinzad.Shop.Web
dotnet user-secrets init
# Set secrets
dotnet user-secrets set "ConnectionStrings:Default" "Server=...;Database=Shop;..."
dotnet user-secrets set "Redis:Configuration" "localhost:6379"
dotnet user-secrets set "Email:ApiKey" "your-api-key"
# List all secrets
dotnet user-secrets list
Pipeline Triggers
Automatic Triggers
| Event | Branches | Pipelines | Actions |
|---|---|---|---|
| Push | master, develop, feature/* | Build, Test | Build, test, code quality |
| Pull Request | master, develop | Build, Test, Quality | Full validation + coverage |
| Push to develop | develop | Build, Deploy | Deploy to staging |
| Push to master | master | Build, Deploy | Deploy to production |
| Tag push | v*.*.* | Build, Docker, Release | Build release artifacts |
| Schedule | master | Security scan | Daily security checks |
Manual Triggers
# Manual deployment workflow
name: Manual Deploy
on:
workflow_dispatch:
inputs:
environment:
description: 'Target environment'
required: true
type: choice
options:
- staging
- production
skip_tests:
description: 'Skip tests (not recommended)'
type: boolean
default: false
migrate_database:
description: 'Run database migrations'
type: boolean
default: true
Branch Protection Rules
Enforce code quality and review processes through branch protection.
Master Branch Protection
- Require pull request reviews (minimum 1 approval)
- Require status checks to pass before merging
- Require branches to be up to date before merging
- Require conversation resolution before merging
- Require linear history (no merge commits)
- Prohibit force pushes
- Restrict who can push to master
Required Status Checks
| Check | Description | Required For |
|---|---|---|
| Build | Successful build | All branches |
| Tests | All tests passing | All branches |
| Code Coverage | Minimum 80% coverage | master only |
| Code Quality | SonarCloud quality gate | master only |
| Security Scan | No critical vulnerabilities | master only |
Develop Branch Protection
- Require pull request reviews
- Require build and test checks
- Allow merge commits
- Prohibit force pushes
Alternative CI/CD Platforms
Azure DevOps Pipelines
Example Azure Pipelines configuration for the Shirinzad platform.
# azure-pipelines.yml
trigger:
branches:
include:
- master
- develop
- feature/*
pool:
vmImage: 'ubuntu-latest'
variables:
buildConfiguration: 'Release'
dotnetVersion: '9.0.x'
stages:
- stage: Build
displayName: 'Build and Test'
jobs:
- job: Build
displayName: 'Build Solution'
steps:
- task: UseDotNet@2
displayName: 'Install .NET SDK'
inputs:
version: $(dotnetVersion)
- task: DotNetCoreCLI@2
displayName: 'Restore packages'
inputs:
command: 'restore'
- task: DotNetCoreCLI@2
displayName: 'Build solution'
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 coverage'
inputs:
codeCoverageTool: 'Cobertura'
summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml'
- stage: Deploy
displayName: 'Deploy to Staging'
dependsOn: Build
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop'))
jobs:
- deployment: DeployStaging
displayName: 'Deploy to Staging Environment'
environment: 'staging'
strategy:
runOnce:
deploy:
steps:
- task: AzureWebApp@1
displayName: 'Deploy to Azure Web App'
inputs:
azureSubscription: 'Azure-Service-Connection'
appName: 'shirinzad-staging'
package: '$(Pipeline.Workspace)/**/*.zip'
Azure DevOps Features
- Integrated with Azure services
- Built-in artifact management
- Release management with approvals
- Extensive marketplace extensions
- Private agent pools support
- Advanced deployment strategies
Jenkins Pipeline
Example Jenkinsfile for building and deploying the application.
// Jenkinsfile
pipeline {
agent any
environment {
DOTNET_CLI_HOME = '/tmp/dotnet'
DOTNET_VERSION = '9.0'
}
stages {
stage('Setup') {
steps {
sh 'curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --version ${DOTNET_VERSION}'
}
}
stage('Restore') {
steps {
sh 'dotnet restore'
}
}
stage('Build') {
steps {
sh 'dotnet build --configuration Release --no-restore'
}
}
stage('Test') {
steps {
sh 'dotnet test --configuration Release --no-build --logger trx --collect:"XPlat Code Coverage"'
}
post {
always {
mstest testResultsFile: '**/*.trx'
publishCoverage adapters: [coberturaAdapter('**/coverage.cobertura.xml')]
}
}
}
stage('Publish') {
when {
branch 'master'
}
steps {
sh 'dotnet publish -c Release -o ./publish'
archiveArtifacts artifacts: 'publish/**/*', fingerprint: true
}
}
stage('Deploy to Staging') {
when {
branch 'develop'
}
steps {
script {
// Deploy to staging server
sh '''
scp -r ./publish/* user@staging-server:/var/www/shirinzad/
ssh user@staging-server 'systemctl restart shirinzad'
'''
}
}
}
stage('Deploy to Production') {
when {
branch 'master'
}
steps {
input message: 'Deploy to production?', ok: 'Deploy'
script {
// Deploy to production server
sh '''
scp -r ./publish/* user@prod-server:/var/www/shirinzad/
ssh user@prod-server 'systemctl restart shirinzad'
'''
}
}
}
}
post {
success {
emailext(
subject: "Build Successful: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
body: "Build ${env.BUILD_NUMBER} completed successfully.",
to: "[email protected]"
)
}
failure {
emailext(
subject: "Build Failed: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
body: "Build ${env.BUILD_NUMBER} failed. Check console output.",
to: "[email protected]"
)
}
}
}
Jenkins Advantages
- Self-hosted with full control
- Extensive plugin ecosystem
- Flexible pipeline configuration
- Support for complex workflows
- Integration with various tools
- Cost-effective for large teams
Monitoring and Notifications
Pipeline Monitoring
- Build Status: Real-time build status badges
- Test Results: Test trend analysis
- Code Coverage: Coverage trend tracking
- Deployment History: Deployment logs and rollback capability
- Performance Metrics: Build duration tracking
Notification Channels
- Email: Build and deployment results
- Slack: Real-time pipeline updates
- Teams: Integration with Microsoft Teams
- GitHub: Status checks on pull requests
- SMS: Critical production failures
Slack Notification Example
# Add to workflow for Slack notifications
- name: Notify Slack on Success
if: success()
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "✅ Build #${{ github.run_number }} succeeded",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Build Successful*\nBranch: ${{ github.ref }}\nCommit: ${{ github.sha }}\nAuthor: ${{ github.actor }}"
}
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
CI/CD Best Practices
Pipeline Design
- Keep pipelines fast (under 10 minutes)
- Fail fast on critical errors
- Use caching for dependencies
- Parallelize independent jobs
- Use matrix builds for multiple targets
- Separate build and deployment stages
Security
- Store secrets securely
- Use least privilege for service accounts
- Scan for vulnerabilities in dependencies
- Sign artifacts and containers
- Audit pipeline changes
- Rotate credentials regularly
Testing
- Run tests on every commit
- Maintain high code coverage (80%+)
- Use test result trends for quality
- Separate unit and integration tests
- Include smoke tests in deployment
- Automate regression testing
Deployment
- Deploy to staging first
- Use blue-green or canary deployments
- Implement automatic rollback
- Validate deployments with health checks
- Keep deployment scripts in version control
- Document deployment procedures
Troubleshooting Common Issues
| Issue | Possible Cause | Solution |
|---|---|---|
| Build fails on restore | NuGet package not found | Check NuGet.Config, verify package sources |
| Tests timeout | Long-running tests | Increase timeout, optimize tests, use mocks |
| Migration fails | Database connection issue | Verify connection string, check firewall rules |
| Deployment hangs | Health check failing | Check application logs, verify endpoints |
| Docker build slow | No layer caching | Optimize Dockerfile, use build cache |
| Secrets not available | Missing environment variables | Configure GitHub secrets, check permissions |