DE
CI/CD Pipelines
Devops core v1.0.0
CI/CD Pipelines
Overview
Continuous Integration and Continuous Delivery (CI/CD) pipelines automate the build, test, security scan, and deployment process for every code change. A well-designed pipeline ensures that code merging to main is always production-ready, with automated quality gates at each stage. GitHub Actions is the default CI/CD platform for the full-lifecycle pipeline, with artifacts flowing through build → test → scan → publish → deploy stages.
Key Concepts
Pipeline Architecture
┌─────────┐ ┌─────────┐ ┌──────────┐ ┌─────────┐ ┌──────────┐
│ Build │──▶│ Test │──▶│ Security │──▶│ Publish │──▶│ Deploy │
│ │ │ │ │ Scan │ │ │ │ │
│ compile │ │ unit │ │ SAST │ │ Docker │ │ staging │
│ lint │ │ integ │ │ SCA │ │ push │ │ prod │
│ format │ │ e2e │ │ secrets │ │ SBOM │ │ smoke │
└─────────┘ └─────────┘ └──────────┘ └─────────┘ └──────────┘
↓ fail ↓ fail ↓ fail ↓ fail ↓ fail
❌ STOP ❌ STOP ❌ STOP ❌ STOP 🔄 ROLLBACK
Pipeline Triggers
| Trigger | When | Pipeline Scope |
|---|---|---|
push: main | Merge to main | Full pipeline + deploy |
pull_request | PR opened/updated | Build + test + scan (no deploy) |
schedule | Cron (nightly) | Full test suite + dependency scan |
workflow_dispatch | Manual trigger | On-demand deploy or full run |
release | Tag created | Build + publish + production deploy |
Quality Gates
| Gate | Tool | Threshold | Blocks |
|---|---|---|---|
| Compile | javac / tsc | 0 errors | Build |
| Lint | Checkstyle / ESLint | 0 warnings | Build |
| Unit Tests | JUnit / Jest | 100% pass | Test |
| Coverage | JaCoCo / Istanbul | 80%+ line | Test |
| SAST | CodeQL / Semgrep | 0 critical | Scan |
| SCA | Dependabot / Trivy | 0 critical CVEs | Scan |
| Container Scan | Trivy | 0 critical | Publish |
Best Practices
- Fail fast — Run lint and compile before tests; cheapest checks first
- Cache dependencies — Cache Maven/npm/Docker layers for 2-5x speed improvement
- Use matrix builds — Test across Java 17/21 and Node 18/20 in parallel
- Pin action versions — Use SHA hashes, not tags:
actions/checkout@v4→actions/checkout@abc123 - Store secrets in GitHub Secrets — Never hardcode credentials in workflow files
- Use reusable workflows — DRY principle: shared
.github/workflows/called by services - Generate SBOM — Software Bill of Materials for every release
- Require PR reviews — Branch protection: 1+ approval + CI pass before merge
Code Examples
✅ Good: Complete GitHub Actions Pipeline
# .github/workflows/ci-cd.yml
name: CI/CD Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
packages: write
security-events: write
env:
JAVA_VERSION: '21'
NODE_VERSION: '20'
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: 'temurin'
cache: 'maven'
- run: mvn compile -B -q
- run: mvn checkstyle:check -B
test:
needs: build
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_DB: testdb
POSTGRES_PASSWORD: test
ports: ['5432:5432']
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: 'temurin'
cache: 'maven'
- run: mvn verify -B
env:
SPRING_DATASOURCE_URL: jdbc:postgresql://localhost:5432/testdb
- uses: actions/upload-artifact@v4
if: always()
with:
name: test-results
path: target/surefire-reports/
security-scan:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: github/codeql-action/init@v3
with:
languages: java
- uses: github/codeql-action/autobuild@v3
- uses: github/codeql-action/analyze@v3
publish:
needs: [test, security-scan]
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v5
with:
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
deploy-staging:
needs: publish
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: staging
steps:
- run: echo "Deploy to staging"
# Add deployment commands here
❌ Bad: Pipeline Anti-Patterns
# No caching — rebuilds everything from scratch
# No quality gates — deploys even if tests fail
# Secrets in plain text — hardcoded credentials
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@main # Unpinned version
- run: mvn package -DskipTests # Skipping tests
- run: docker build -t myapp .
- run: docker push myapp
env:
DOCKER_PASSWORD: "hardcoded123" # NEVER do this
Anti-Patterns
- Skip tests in CI —
-DskipTestsdefeats the purpose of CI - Unpinned action versions —
@maincan break without warning - No caching — Rebuilding dependencies on every run wastes 2-5 minutes
- Manual deployments — If it’s not in the pipeline, it’s not reproducible
- Single monolithic job — No parallelism; everything runs serially
- No branch protection — Direct push to main without PR or CI
Testing Strategies
- Pipeline dry runs — Use
actto test GitHub Actions locally - Matrix testing — Test across JDK and Node versions simultaneously
- Deployment smoke tests — Health check endpoint after each deploy
- Pipeline performance — Track build times; alert if >10 minutes