Continuous Integration (CI) and Continuous Delivery/Deployment (CD) are cornerstones of modern DevOps practices. They automate the software delivery pipeline, enabling faster, more reliable, and frequent releases. This document outlines best practices for implementing CI/CD using GitHub as the SCM platform and GitHub Actions as the automation engine. It also covers integrating security (DevSecOps) and quality assurance (SonarQube) into the pipeline, along with environment promotion strategies using GitHub Environments.
main
branch (or master
) should always be in a deployable state..github/workflows/
) and can be triggered by various events (push, pull request, schedule, manual dispatch).This workflow triggers on push
and pull_request
events to the main
branch, building the application and running unit tests.
# .github/workflows/ci.yml
name: CI Build and Test
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build-and-test:
runs-on: ubuntu-latest # Use a GitHub-hosted runner
steps:
- name: Checkout code
uses: actions/checkout@v4 # Action to checkout your repository code
- name: Setup Node.js (Example for a Node.js project)
uses: actions/setup-node@v4
with:
node-version: '18' # Specify your Node.js version
- name: Install dependencies
run: npm ci # Use npm ci for clean installs in CI environments
- name: Run tests
run: npm test # Execute your test suite
- name: Build application
run: npm run build # Build your application (e.g., for deployment artifacts)
# Example for a Java project (replace Node.js steps)
# - name: Setup Java JDK
# uses: actions/setup-java@v4
# with:
# distribution: 'temurin'
# java-version: '17'
# - name: Build with Maven
# run: mvn clean install
Integrate SonarQube for static code analysis to maintain code quality and identify code smells, bugs, and vulnerabilities. This typically runs as part of the CI process.
# .github/workflows/ci.yml (add to existing CI workflow or create a new one)
name: CI Build, Test, and SonarQube
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build-test-sonarqube:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
# SonarQube requires a full history for accurate analysis
fetch-depth: 0
- name: Setup Java JDK (Required for SonarQube Scanner)
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
- name: Cache SonarQube packages
uses: actions/cache@v4
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- name: Build with Maven and SonarQube analysis (Example for Java/Maven)
run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey= -Dsonar.host.url= -Dsonar.token=${{ secrets.SONAR_TOKEN }}
env:
# SONAR_TOKEN should be stored as a GitHub Secret in your repository settings
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
# For JavaScript/TypeScript projects, you might use sonar-scanner-cli
# - name: Install SonarQube Scanner CLI
# run: npm install -g sonarqube-scanner
# - name: Run SonarQube Scan
# run: sonar-scanner -Dsonar.projectKey= -Dsonar.sources=. -Dsonar.host.url= -Dsonar.token=${{ secrets.SONAR_TOKEN }}
# env:
# SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
Note: Replace <YOUR_SONARQUBE_PROJECT_KEY>
and <YOUR_SONARQUBE_URL>
with your actual SonarQube project key and server URL. The SONAR_TOKEN
must be stored as a GitHub Secret.
Integrating security scanning early in the pipeline is a key DevSecOps practice. Trivy is a popular open-source vulnerability scanner for container images, file systems, and more.
# .github/workflows/ci.yml (add to existing CI workflow or create a new one)
name: CI Build, Test, and Security Scan
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build-test-scan:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
# ... (Your build and test steps here) ...
- name: Build Docker image (if applicable)
run: docker build -t my-app:latest . # Replace with your image build command
- name: Run Trivy vulnerability scan on image
uses: aquasecurity/trivy-action@master # Use the official Trivy GitHub Action
with:
image-ref: 'my-app:latest' # Scan the image you just built
format: 'table' # Output format (table, json, sarif)
exit-code: '1' # Fail the workflow if vulnerabilities are found (e.g., critical, high)
severity: 'CRITICAL,HIGH' # Specify severity levels to fail on
- name: Run Trivy file system scan (alternative/additional scan)
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs' # Scan the file system (your repository code)
input: '.' # Scan the current directory
format: 'table'
exit-code: '1'
severity: 'CRITICAL,HIGH'
Aqua Security: While Trivy is a standalone tool from Aqua Security, Aqua also offers a comprehensive platform for cloud-native security. For full integration with Aqua Security's enterprise platform (e.g., image assurance policies, runtime protection), you would typically use their dedicated scanner or integrate with their API. This often involves more complex setup beyond a simple GitHub Action example, but the principle remains: integrate security scans early and often.
GitHub Environments allow you to define deployment environments (e.g., Development, Staging, Production) and configure environment-specific rules, including required reviewers, wait timers, and environment secrets. This is crucial for controlled promotion through your CD pipeline.
development
, staging
, production
.This example demonstrates a multi-stage deployment pipeline using environments with approvals.
# .github/workflows/cd.yml
name: CD Deployment Pipeline
on:
push:
branches:
- main # Trigger deployment when code is pushed to main
jobs:
deploy-to-development:
runs-on: ubuntu-latest
environment:
name: development # Target the 'development' environment
url: https://dev.your-app.com # Optional: URL for the deployed application
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Deploy to Development
run: |
echo "Deploying to Development environment..."
# Add your deployment commands here (e.g., kubectl apply, serverless deploy, ansible playbook)
# Example: deploy using a simple script
echo "Application deployed to Development!"
deploy-to-staging:
runs-on: ubuntu-latest
needs: deploy-to-development # This job runs only after 'deploy-to-development' succeeds
environment:
name: staging # Target the 'staging' environment
url: https://staging.your-app.com # Optional: URL for the deployed application
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Deploy to Staging
run: |
echo "Deploying to Staging environment..."
# Add your deployment commands here
echo "Application deployed to Staging!"
deploy-to-production:
runs-on: ubuntu-latest
needs: deploy-to-staging # This job runs only after 'deploy-to-staging' succeeds
environment:
name: production # Target the 'production' environment
url: https://your-app.com # Optional: URL for the deployed application
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Deploy to Production
run: |
echo "Deploying to Production environment..."
# Add your deployment commands here
echo "Application deployed to Production!"
To set up approvals: In your GitHub repository settings, under Environments, click on the staging
and production
environments. Under "Deployment protection rules," enable "Required reviewers" and select the teams or individuals who must approve deployments to these environments. When a workflow attempts to deploy to these environments, it will pause until the required reviewers approve.
By leveraging GitHub and GitHub Actions for CI/CD, teams can establish robust, automated pipelines that accelerate software delivery while maintaining high standards of quality and security. Integrating tools like SonarQube for code quality and Trivy for vulnerability scanning enables a "shift-left" approach to DevSecOps. Furthermore, GitHub Environments with approval gates provide critical control and governance over deployments to sensitive environments, ensuring a secure and reliable release process.