Learn the fundamentals of Continuous Integration (CI) and how to automate your build, test, and validation processes. Master CI/CD pipelines using GitHub Actions, understand testing strategies, and implement automated quality checks.
Continuous Integration (CI) is a development practice where developers integrate code into a shared repository frequently, preferably several times a day. Each integration is automatically verified by building the project and running automated tests, allowing teams to detect problems early.
The Problem CI Solves:
Before CI, teams would develop features in isolation for weeks or months, then attempt to merge everything together - often resulting in "integration hell." Bugs would multiply, conflicts would arise, and releases would be delayed. Manual testing couldn't keep pace with code changes, and quality suffered.
How CI Changes Everything:
With CI, every commit triggers an automated pipeline that builds the code, runs tests, and checks quality. Problems are detected within minutes, not weeks. Developers get immediate feedback, bugs are caught early when they're cheapest to fix, and the codebase is always in a releasable state.
Real-World Impact:
Teams practicing CI deploy code 208 times more frequently with 106 times faster recovery from failures (2019 State of DevOps Report). CI enables rapid iteration, reduces risk, and dramatically improves software quality.
What You'll Learn:
This lesson covers CI fundamentals: understanding CI/CD concepts, creating automated pipelines with GitHub Actions, implementing comprehensive testing strategies, and establishing quality gates that ensure only good code reaches production.
Continuous Integration is a development practice where team members integrate their work frequently, with each integration verified by an automated build and test process.
Core CI Principles:
1. Single Source Repository
All code lives in a version control system (Git, SVN, etc.). Everyone commits to the main branch (or short-lived feature branches) regularly.
2. Automated Build
The build process is fully automated - one command builds the entire application from source. No manual steps, no special knowledge required.
3. Self-Testing Build
The build includes running automated tests. If tests fail, the build fails. This ensures code changes don't break existing functionality.
4. Frequent Commits
Developers commit code at least daily, often multiple times per day. Small, frequent commits are easier to integrate and debug than large, infrequent ones.
5. Build Every Commit
Every commit to the main branch triggers an automated build. This provides immediate feedback on integration issues.
6. Fast Builds
Builds should complete in under 10 minutes (ideally under 5). Fast feedback enables rapid iteration.
7. Test in Production Clone
The test environment should closely mirror production. "It works on my machine" is not acceptable.
8. Easy Access to Latest Build
Everyone can easily get the latest executable and see what changes it contains.
9. Visible Status
Build status is visible to the entire team. Failed builds are fixed immediately.
10. Automated Deployment
The deployment process is automated and can be triggered with a single action.
Benefits of CI:
1Faster Development:
2- Immediate feedback on code changes
3- Reduced debugging time
4- Faster iteration cycles
5
6Higher Quality:
7- Bugs caught early (when cheapest to fix)
8- Consistent build and test process
9- Reduced integration issues
10
11Better Collaboration:
12- Reduced conflicts from isolation
13- Shared understanding of code state
14- Improved team communication
15
16Risk Reduction:
17- Always-deployable codebase
18- Smaller, safer releases
19- Faster recovery from failuresCI vs CD vs CD:
The CI Pipeline:
A typical CI pipeline consists of multiple stages:
1Commit → Build → Test → Quality Checks → Package → Deploy
2 ↓ ↓ ↓ ↓ ↓ ↓
3 Code Compile Unit Linting Create Release
4 Check Tests Security Artifact to EnvCI Best Practices:
GitHub Actions is a CI/CD platform built into GitHub that allows you to automate your software development workflows.
Core Concepts:
Workflows
Automated processes defined in YAML files stored in .github/workflows/. Workflows contain one or more jobs.
Events
Triggers that start workflows (push, pull request, schedule, manual trigger, etc.).
Jobs
A set of steps that execute on the same runner. Jobs run in parallel by default but can be configured to run sequentially.
Steps
Individual tasks that run commands or actions. Steps execute sequentially within a job.
Actions
Reusable units of code that perform common tasks. You can use actions from the GitHub Marketplace or create your own.
Runners
Servers that run your workflows. GitHub provides hosted runners (Ubuntu, Windows, macOS) or you can host your own.
Basic Workflow Structure:
1name: CI Pipeline
2
3on: [push, pull_request]
4
5jobs:
6 build:
7 runs-on: ubuntu-latest
8
9 steps:
10 - name: Checkout code
11 uses: actions/checkout@v4
12
13 - name: Setup Node.js
14 uses: actions/setup-node@v4
15 with:
16 node-version: '20'
17
18 - name: Install dependencies
19 run: npm ci
20
21 - name: Run tests
22 run: npm test
23
24 - name: Build
25 run: npm run buildWorkflow Triggers:
1# On push to specific branches
2on:
3 push:
4 branches: [main, develop]
5
6# On pull requests
7on:
8 pull_request:
9 branches: [main]
10
11# On schedule (cron)
12on:
13 schedule:
14 - cron: '0 2 * * *' # Daily at 2 AM
15
16# Manual trigger
17on:
18 workflow_dispatch:
19
20# Multiple triggers
21on:
22 push:
23 branches: [main]
24 pull_request:
25 branches: [main]
26 schedule:
27 - cron: '0 0 * * 0' # WeeklyMatrix Builds:
Test across multiple versions and platforms:
1jobs:
2 test:
3 runs-on: ${{ matrix.os }}
4 strategy:
5 matrix:
6 os: [ubuntu-latest, windows-latest, macos-latest]
7 node-version: [18, 20, 22]
8
9 steps:
10 - uses: actions/checkout@v4
11 - uses: actions/setup-node@v4
12 with:
13 node-version: ${{ matrix.node-version }}
14 - run: npm ci
15 - run: npm testSecrets and Environment Variables:
1jobs:
2 deploy:
3 runs-on: ubuntu-latest
4
5 env:
6 NODE_ENV: production
7
8 steps:
9 - uses: actions/checkout@v4
10
11 - name: Deploy
12 env:
13 API_KEY: ${{ secrets.API_KEY }}
14 DATABASE_URL: ${{ secrets.DATABASE_URL }}
15 run: |
16 echo "Deploying with API_KEY"
17 ./deploy.shConditional Execution:
1jobs:
2 deploy:
3 runs-on: ubuntu-latest
4 if: github.ref == 'refs/heads/main'
5
6 steps:
7 - name: Deploy to production
8 if: success()
9 run: ./deploy.sh
10
11 - name: Notify on failure
12 if: failure()
13 run: ./notify-failure.shArtifacts:
Share data between jobs and preserve build outputs:
1jobs:
2 build:
3 runs-on: ubuntu-latest
4 steps:
5 - uses: actions/checkout@v4
6 - run: npm run build
7
8 - name: Upload build artifacts
9 uses: actions/upload-artifact@v4
10 with:
11 name: build-output
12 path: dist/
13
14 test:
15 needs: build
16 runs-on: ubuntu-latest
17 steps:
18 - name: Download artifacts
19 uses: actions/download-artifact@v4
20 with:
21 name: build-output
22 path: dist/
23
24 - name: Run tests
25 run: npm testCaching:
Speed up workflows by caching dependencies:
1steps:
2- uses: actions/checkout@v4
3
4- name: Cache dependencies
5 uses: actions/cache@v4
6 with:
7 path: ~/.npm
8 key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
9 restore-keys: |
10 ${{ runner.os }}-node-
11
12- run: npm ciReusable Workflows:
Create workflows that can be called from other workflows:
1# .github/workflows/reusable-test.yml
2on:
3 workflow_call:
4 inputs:
5 node-version:
6 required: true
7 type: string
8
9jobs:
10 test:
11 runs-on: ubuntu-latest
12 steps:
13 - uses: actions/checkout@v4
14 - uses: actions/setup-node@v4
15 with:
16 node-version: ${{ inputs.node-version }}
17 - run: npm ci && npm testStatus Badges:
Add build status to README:
1Automated testing is the backbone of Continuous Integration. Without automated tests, CI is just automated building - tests provide the safety net that allows rapid code changes.
Testing Pyramid:
1 /\
2 /E2E\ <- Few, slow, expensive
3 /______\
4 / \
5 /Integration\ <- Some, moderate speed
6 /____________\
7 / \
8 / Unit Tests \ <- Many, fast, cheap
9/__________________\Unit Tests:
Test individual functions or components in isolation.
1// Example: Unit test with Jest
2describe('calculateTotal', () => {
3 it('should sum array of numbers', () => {
4 expect(calculateTotal([1, 2, 3])).toBe(6);
5 });
6
7 it('should handle empty array', () => {
8 expect(calculateTotal([])).toBe(0);
9 });
10
11 it('should throw on invalid input', () => {
12 expect(() => calculateTotal('invalid')).toThrow();
13 });
14});Integration Tests:
Test how components work together.
1// Example: API integration test
2describe('User API', () => {
3 it('should create and retrieve user', async () => {
4 // Create user
5 const response = await request(app)
6 .post('/api/users')
7 .send({ name: 'Alice', email: '[email protected]' });
8
9 expect(response.status).toBe(201);
10 const userId = response.body.id;
11
12 // Retrieve user
13 const getResponse = await request(app)
14 .get(`/api/users/${userId}`);
15
16 expect(getResponse.body.name).toBe('Alice');
17 });
18});End-to-End Tests:
Test complete user workflows in a production-like environment.
1// Example: E2E test with Playwright
2test('user can complete checkout', async ({ page }) => {
3 await page.goto('https://example.com');
4 await page.click('text=Shop');
5 await page.click('button:has-text("Add to Cart")');
6 await page.click('text=Checkout');
7 await page.fill('input[name=email]', '[email protected]');
8 await page.fill('input[name=card]', '4242424242424242');
9 await page.click('button:has-text("Complete Purchase")');
10
11 await expect(page.locator('text=Order Confirmed')).toBeVisible();
12});Test Coverage:
Track which code is tested:
1- name: Run tests with coverage
2 run: npm test -- --coverage
3
4- name: Check coverage thresholds
5 run: |
6 if [ $(cat coverage/coverage-summary.json | jq '.total.lines.pct') -lt 80 ]; then
7 echo "Coverage below 80%"
8 exit 1
9 fiTesting Best Practices:
1// AAA Pattern
2test('should calculate discount correctly', () => {
3 // Arrange
4 const price = 100;
5 const discountPercent = 20;
6
7 // Act
8 const result = calculateDiscount(price, discountPercent);
9
10 // Assert
11 expect(result).toBe(80);
12});Test Data Management:
1// Use factories for test data
2const createUser = (overrides = {}) => ({
3 id: '123',
4 name: 'Test User',
5 email: '[email protected]',
6 ...overrides
7});
8
9// Use in tests
10const user = createUser({ email: '[email protected]' });Mocking:
1// Mock external dependencies
2jest.mock('./emailService', () => ({
3 sendEmail: jest.fn().mockResolvedValue({ success: true })
4}));
5
6test('should send welcome email', async () => {
7 const emailService = require('./emailService');
8
9 await registerUser({ email: '[email protected]' });
10
11 expect(emailService.sendEmail).toHaveBeenCalledWith({
12 to: '[email protected]',
13 subject: 'Welcome!'
14 });
15});Parallel Test Execution:
1jobs:
2 test:
3 runs-on: ubuntu-latest
4 strategy:
5 matrix:
6 shard: [1, 2, 3, 4]
7
8 steps:
9 - uses: actions/checkout@v4
10 - run: npm ci
11 - run: npm test -- --shard=${{ matrix.shard }}/4Flaky Test Detection:
1- name: Run tests multiple times
2 run: |
3 for i in {1..3}; do
4 npm test || exit 1
5 doneTest Reporting:
1- name: Test
2 run: npm test -- --json --outputFile=test-results.json
3
4- name: Publish test results
5 uses: dorny/test-reporter@v1
6 if: always()
7 with:
8 name: Test Results
9 path: test-results.json
10 reporter: jest-jsonAutomated quality checks ensure code meets standards before it reaches production. These checks act as gatekeepers in your CI pipeline.
Linting:
Enforce code style and catch common errors:
1- name: Run ESLint
2 run: npm run lint
3
4- name: Run Prettier
5 run: npm run format:checkTypeScript Type Checking:
1- name: Type check
2 run: npx tsc --noEmitSecurity Scanning:
1. Dependency Vulnerability Scanning:
1- name: Audit dependencies
2 run: npm audit --audit-level=moderate
3
4- name: Check for vulnerabilities
5 uses: snyk/actions/node@master
6 env:
7 SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}2. Static Application Security Testing (SAST):
1- name: Run CodeQL analysis
2 uses: github/codeql-action/analyze@v3
3 with:
4 languages: javascript, typescript
5
6- name: SonarCloud Scan
7 uses: SonarSource/sonarcloud-github-action@master
8 env:
9 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
10 SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}3. Container Security Scanning:
1- name: Build Docker image
2 run: docker build -t myapp:latest .
3
4- name: Scan image with Trivy
5 uses: aquasecurity/trivy-action@master
6 with:
7 image-ref: myapp:latest
8 severity: CRITICAL,HIGHLicense Compliance:
1- name: Check license compliance
2 run: npx license-checker --failOn 'GPL;AGPL'Code Complexity Analysis:
1- name: Analyze code complexity
2 run: |
3 npx complexity-report --format json src/ > complexity.json
4 COMPLEXITY=$(cat complexity.json | jq '.averageCyclomaticComplexity')
5 if (( $(echo "$COMPLEXITY > 10" | bc -l) )); then
6 echo "Code too complex!"
7 exit 1
8 fiCommit Message Validation:
1- name: Validate commit messages
2 uses: wagoid/commitlint-github-action@v6Quality Gates:
Define thresholds that must be met:
1jobs:
2 quality-gate:
3 runs-on: ubuntu-latest
4 steps:
5 - uses: actions/checkout@v4
6
7 - name: Run tests with coverage
8 run: npm test -- --coverage
9
10 - name: Quality gate
11 run: |
12 # Check test coverage
13 COVERAGE=$(cat coverage/coverage-summary.json | jq '.total.lines.pct')
14 if (( $(echo "$COVERAGE < 80" | bc -l) )); then
15 echo "Coverage $COVERAGE% is below 80%"
16 exit 1
17 fi
18
19 # Check for critical vulnerabilities
20 npm audit --audit-level=critical
21
22 # Check code quality
23 npm run lint
24
25 echo "✓ All quality gates passed"Branch Protection Rules:
Enforce quality standards before merging:
Pre-commit Hooks:
Run checks locally before commit:
1// package.json
2{
3 "husky": {
4 "hooks": {
5 "pre-commit": "lint-staged",
6 "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
7 }
8 },
9 "lint-staged": {
10 "*.{js,ts}": ["eslint --fix", "prettier --write"],
11 "*.{json,md}": ["prettier --write"]
12 }
13}Metrics to Track:
A well-optimized CI pipeline provides fast feedback while maintaining reliability and controlling costs.
Speed Optimization:
1. Parallel Execution:
1jobs:
2 test:
3 strategy:
4 matrix:
5 shard: [1, 2, 3, 4]
6 steps:
7 - run: npm test -- --shard=${{ matrix.shard }}/42. Dependency Caching:
1- uses: actions/cache@v4
2 with:
3 path: |
4 ~/.npm
5 node_modules
6 key: ${{ runner.os }}-deps-${{ hashFiles('**/package-lock.json') }}3. Docker Layer Caching:
1# Optimize Dockerfile for caching
2FROM node:20-alpine
3
4# Copy package files first (changes less frequently)
5COPY package*.json ./
6RUN npm ci --production
7
8# Then copy source code (changes more frequently)
9COPY . .
10RUN npm run build4. Incremental Builds:
Only build/test changed files:
1- name: Get changed files
2 id: changed-files
3 uses: tj-actions/changed-files@v44
4
5- name: Run tests on changed files
6 if: steps.changed-files.outputs.any_changed == 'true'
7 run: npm test -- ${{ steps.changed-files.outputs.all_changed_files }}5. Skip Redundant Work:
1- name: Check if tests needed
2 id: check
3 run: |
4 if git diff --name-only HEAD~1 | grep -q '^src/'; then
5 echo "run=true" >> $GITHUB_OUTPUT
6 fi
7
8- name: Run tests
9 if: steps.check.outputs.run == 'true'
10 run: npm testReliability Best Practices:
1. Flaky Test Management:
1- name: Run tests with retries
2 uses: nick-fields/retry@v3
3 with:
4 timeout_minutes: 10
5 max_attempts: 3
6 command: npm test2. Deterministic Builds:
1# Lock dependency versions
2- run: npm ci # Use ci instead of install
3
4# Use specific tool versions
5- uses: actions/setup-node@v4
6 with:
7 node-version: '20.10.0' # Exact version3. Health Checks:
1- name: Wait for service
2 run: |
3 timeout 60 bash -c 'until curl -f http://localhost:3000/health; do sleep 2; done'4. Proper Error Handling:
1- name: Deploy
2 run: ./deploy.sh
3 continue-on-error: false
4
5- name: Rollback on failure
6 if: failure()
7 run: ./rollback.shCost Optimization:
1. Conditional Workflows:
1on:
2 push:
3 branches: [main]
4 paths:
5 - 'src/**'
6 - 'tests/**'
7 - 'package.json'
8 # Only run on relevant changes2. Skip Duplicate Runs:
1- name: Cancel previous runs
2 uses: styfle/cancel-workflow-[email protected]
3 with:
4 access_token: ${{ github.token }}3. Optimize Test Suite:
1# Run fast tests first
2npm run test:unit # Fast
3npm run test:integration # Slower
4npm run test:e2e # SlowestMonitoring and Observability:
1- name: Report metrics
2 if: always()
3 run: |
4 curl -X POST https://metrics.example.com/ci \
5 -d "build_time=${{ job.duration }}" \
6 -d "status=${{ job.status }}"
7
8- name: Notify Slack
9 if: failure()
10 uses: 8398a7/action-slack@v3
11 with:
12 status: ${{ job.status }}
13 text: 'Build failed on ${{ github.ref }}'
14 webhook_url: ${{ secrets.SLACK_WEBHOOK }}Pipeline as Code Best Practices:
End-to-end CI/CD pipeline with all best practices: quality checks, testing, security, deployment, and monitoring
1# .github/workflows/production-pipeline.yml
2# Production-Grade CI/CD Pipeline
3
4name: Production Pipeline
5
6on:
7 push:
8 branches: [main, develop]
9 pull_request:
10 branches: [main]
11 workflow_dispatch:
12
13env:
14 NODE_VERSION: '20'
15 DOCKER_REGISTRY: ghcr.io
16 IMAGE_NAME: ${{ github.repository }}
17
18# Cancel previous runs on same branch
19concurrency:
20 group: ${{ github.workflow }}-${{ github.ref }}
21 cancel-in-progress: true
22
23jobs:
24 # ==========================================
25 # STAGE 1: Code Quality & Static Analysis
26 # ==========================================
27 quality:
28 name: Code Quality
29 runs-on: ubuntu-latest
30 timeout-minutes: 10
31
32 steps:
33 - name: Checkout code
34 uses: actions/checkout@v4
35 with:
36 fetch-depth: 0 # Full history for better analysis
37
38 - name: Setup Node.js
39 uses: actions/setup-node@v4
40 with:
41 node-version: ${{ env.NODE_VERSION }}
42 cache: 'npm'
43
44 - name: Install dependencies
45 run: npm ci
46
47 - name: Lint code
48 run: npm run lint
49
50 - name: Check formatting
51 run: npm run format:check
52
53 - name: Type check
54 run: npm run type-check
55
56 - name: Check for secrets
57 uses: gitleaks/gitleaks-action@v2
58
59 - name: SonarCloud Analysis
60 uses: SonarSource/sonarcloud-github-action@master
61 env:
62 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
63 SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
64
65 # ==========================================
66 # STAGE 2: Security Scanning
67 # ==========================================
68 security:
69 name: Security Scan
70 runs-on: ubuntu-latest
71 timeout-minutes: 15
72
73 steps:
74 - uses: actions/checkout@v4
75
76 - name: Setup Node.js
77 uses: actions/setup-node@v4
78 with:
79 node-version: ${{ env.NODE_VERSION }}
80
81 - name: npm audit
82 run: npm audit --audit-level=moderate
83
84 - name: Snyk security scan
85 uses: snyk/actions/node@master
86 env:
87 SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
88 with:
89 args: --severity-threshold=high --fail-on=all
90
91 - name: CodeQL Analysis
92 uses: github/codeql-action/init@v3
93 with:
94 languages: javascript, typescript
95
96 - name: Autobuild
97 uses: github/codeql-action/autobuild@v3
98
99 - name: Perform CodeQL Analysis
100 uses: github/codeql-action/analyze@v3
101
102 # ==========================================
103 # STAGE 3: Testing (Parallel Execution)
104 # ==========================================
105 unit-tests:
106 name: Unit Tests
107 runs-on: ubuntu-latest
108 timeout-minutes: 15
109
110 steps:
111 - uses: actions/checkout@v4
112
113 - name: Setup Node.js
114 uses: actions/setup-node@v4
115 with:
116 node-version: ${{ env.NODE_VERSION }}
117 cache: 'npm'
118
119 - name: Install dependencies
120 run: npm ci
121
122 - name: Run unit tests
123 run: npm run test:unit -- --coverage
124
125 - name: Upload coverage
126 uses: codecov/codecov-action@v4
127 with:
128 files: ./coverage/lcov.info
129 flags: unit-tests
130
131 integration-tests:
132 name: Integration Tests
133 runs-on: ubuntu-latest
134 timeout-minutes: 20
135
136 services:
137 postgres:
138 image: postgres:16
139 env:
140 POSTGRES_PASSWORD: test
141 POSTGRES_DB: testdb
142 options: >-
143 --health-cmd pg_isready
144 --health-interval 10s
145 --health-timeout 5s
146 --health-retries 5
147 ports:
148 - 5432:5432
149
150 redis:
151 image: redis:7
152 options: >-
153 --health-cmd "redis-cli ping"
154 --health-interval 10s
155 --health-timeout 5s
156 --health-retries 5
157 ports:
158 - 6379:6379
159
160 steps:
161 - uses: actions/checkout@v4
162
163 - name: Setup Node.js
164 uses: actions/setup-node@v4
165 with:
166 node-version: ${{ env.NODE_VERSION }}
167 cache: 'npm'
168
169 - name: Install dependencies
170 run: npm ci
171
172 - name: Run migrations
173 env:
174 DATABASE_URL: postgresql://postgres:test@localhost:5432/testdb
175 run: npm run migrate
176
177 - name: Run integration tests
178 env:
179 DATABASE_URL: postgresql://postgres:test@localhost:5432/testdb
180 REDIS_URL: redis://localhost:6379
181 run: npm run test:integration
182
183 e2e-tests:
184 name: E2E Tests
185 runs-on: ubuntu-latest
186 timeout-minutes: 30
187
188 steps:
189 - uses: actions/checkout@v4
190
191 - name: Setup Node.js
192 uses: actions/setup-node@v4
193 with:
194 node-version: ${{ env.NODE_VERSION }}
195 cache: 'npm'
196
197 - name: Install dependencies
198 run: npm ci
199
200 - name: Install Playwright
201 run: npx playwright install --with-deps
202
203 - name: Build application
204 run: npm run build
205
206 - name: Start application
207 run: npm start &
208
209 - name: Wait for app
210 run: npx wait-on http://localhost:3000 --timeout 60000
211
212 - name: Run E2E tests
213 run: npm run test:e2e
214
215 - name: Upload test results
216 if: always()
217 uses: actions/upload-artifact@v4
218 with:
219 name: playwright-report
220 path: playwright-report/
221
222 # ==========================================
223 # STAGE 4: Build & Package
224 # ==========================================
225 build:
226 name: Build Application
227 needs: [quality, security, unit-tests, integration-tests]
228 runs-on: ubuntu-latest
229 timeout-minutes: 15
230
231 steps:
232 - uses: actions/checkout@v4
233
234 - name: Setup Node.js
235 uses: actions/setup-node@v4
236 with:
237 node-version: ${{ env.NODE_VERSION }}
238 cache: 'npm'
239
240 - name: Install dependencies
241 run: npm ci
242
243 - name: Build
244 run: npm run build
245
246 - name: Upload build artifacts
247 uses: actions/upload-artifact@v4
248 with:
249 name: build-artifacts
250 path: dist/
251
252 docker-build:
253 name: Build Docker Image
254 needs: build
255 runs-on: ubuntu-latest
256 timeout-minutes: 20
257
258 steps:
259 - uses: actions/checkout@v4
260
261 - name: Download build artifacts
262 uses: actions/download-artifact@v4
263 with:
264 name: build-artifacts
265 path: dist/
266
267 - name: Set up Docker Buildx
268 uses: docker/setup-buildx-action@v3
269
270 - name: Log in to Container Registry
271 uses: docker/login-action@v3
272 with:
273 registry: ${{ env.DOCKER_REGISTRY }}
274 username: ${{ github.actor }}
275 password: ${{ secrets.GITHUB_TOKEN }}
276
277 - name: Extract metadata
278 id: meta
279 uses: docker/metadata-action@v5
280 with:
281 images: ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}
282 tags: |
283 type=ref,event=branch
284 type=ref,event=pr
285 type=semver,pattern={{version}}
286 type=sha
287
288 - name: Build and push Docker image
289 uses: docker/build-push-action@v5
290 with:
291 context: .
292 push: ${{ github.event_name != 'pull_request' }}
293 tags: ${{ steps.meta.outputs.tags }}
294 labels: ${{ steps.meta.outputs.labels }}
295 cache-from: type=gha
296 cache-to: type=gha,mode=max
297
298 - name: Scan Docker image
299 uses: aquasecurity/trivy-action@master
300 with:
301 image-ref: ${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${{ github.sha }}
302 format: 'sarif'
303 output: 'trivy-results.sarif'
304
305 - name: Upload Trivy results
306 uses: github/codeql-action/upload-sarif@v3
307 with:
308 sarif_file: 'trivy-results.sarif'
309
310 # ==========================================
311 # STAGE 5: Deployment
312 # ==========================================
313 deploy-staging:
314 name: Deploy to Staging
315 needs: [docker-build, e2e-tests]
316 if: github.ref == 'refs/heads/develop'
317 runs-on: ubuntu-latest
318 environment:
319 name: staging
320 url: https://staging.example.com
321
322 steps:
323 - uses: actions/checkout@v4
324
325 - name: Deploy to staging
326 env:
327 KUBE_CONFIG: ${{ secrets.STAGING_KUBE_CONFIG }}
328 run: |
329 echo "$KUBE_CONFIG" | base64 -d > kubeconfig
330 kubectl --kubeconfig=kubeconfig set image deployment/app \
331 app=${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${{ github.sha }}
332 kubectl --kubeconfig=kubeconfig rollout status deployment/app
333
334 - name: Smoke tests
335 run: |
336 curl -f https://staging.example.com/health || exit 1
337
338 deploy-production:
339 name: Deploy to Production
340 needs: [docker-build, e2e-tests]
341 if: github.ref == 'refs/heads/main'
342 runs-on: ubuntu-latest
343 environment:
344 name: production
345 url: https://example.com
346
347 steps:
348 - uses: actions/checkout@v4
349
350 - name: Deploy to production
351 env:
352 KUBE_CONFIG: ${{ secrets.PRODUCTION_KUBE_CONFIG }}
353 run: |
354 echo "$KUBE_CONFIG" | base64 -d > kubeconfig
355 kubectl --kubeconfig=kubeconfig set image deployment/app \
356 app=${{ env.DOCKER_REGISTRY }}/${{ env.IMAGE_NAME }}:sha-${{ github.sha }}
357 kubectl --kubeconfig=kubeconfig rollout status deployment/app
358
359 - name: Health check
360 run: |
361 for i in {1..30}; do
362 if curl -f https://example.com/health; then
363 echo "Health check passed"
364 exit 0
365 fi
366 echo "Waiting for deployment..."
367 sleep 10
368 done
369 echo "Health check failed"
370 exit 1
371
372 - name: Create release
373 uses: actions/create-release@v1
374 env:
375 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
376 with:
377 tag_name: v${{ github.run_number }}
378 release_name: Release v${{ github.run_number }}
379
380 # ==========================================
381 # STAGE 6: Notifications & Monitoring
382 # ==========================================
383 notify:
384 name: Notify Results
385 needs: [deploy-staging, deploy-production]
386 if: always()
387 runs-on: ubuntu-latest
388
389 steps:
390 - name: Slack notification
391 uses: 8398a7/action-slack@v3
392 with:
393 status: ${{ job.status }}
394 text: |
395 Pipeline: ${{ github.workflow }}
396 Branch: ${{ github.ref_name }}
397 Commit: ${{ github.sha }}
398 Status: ${{ job.status }}
399 webhook_url: ${{ secrets.SLACK_WEBHOOK }}This production-grade CI/CD pipeline demonstrates comprehensive automation with all DevOps best practices.
Pipeline Stages:
1. Code Quality (5-10 min)
2. Security Scanning (10-15 min)
3. Testing (Parallel, 15-30 min)
4. Build & Package (15-20 min)
5. Deployment (5-15 min)
6. Notifications
Key Features:
Speed Optimizations:
Reliability:
Security:
Observability:
Cost Optimization:
This pipeline ensures only high-quality, secure code reaches production while providing fast feedback to developers.
Your progress is saved in this browser, on this device only.