CI/CD Tooling — GitHub Actions, Jenkins, GitLab CI
In this tutorial, you'll learn about CI/CD Tooling. We cover key concepts, practical examples, and best practices.
CI/CD pipelines automate the build, test, and deployment process — catching bugs early, enforcing quality gates, and delivering software to production reliably and repeatedly.
What You'll Learn
In this tutorial, you'll learn GitHub Actions for workflow automation, Jenkins Declarative and Scripted Pipelines with shared libraries, GitLab CI configuration, environment promotion (dev, staging, canary, production), artifact management, and deployment strategies including blue-green and rolling updates.
Why It Matters
Manual deployments are error-prone, slow, and don't scale. A well-designed CI/CD pipeline catches integration issues within minutes, enforces code quality checks, and deploys with zero downtime. Teams using CI/CD ship 2x more frequently with 3x fewer failures.
Real-World Use
Doda Browser's CI/CD pipeline uses GitHub Actions for PR checks (lint, test, build), then a Jenkins pipeline for staging deployment with integration tests, and finally a GitLab CI pipeline for production rollout with canary analysis and automatic rollback.
flowchart LR
A[Developer Push] --> B[GitHub Actions: CI]
B --> C{Lint + Test + Build}
C -->|Pass| D[Staging Deploy]
C -->|Fail| E[Fix and Re-push]
D --> F[Integration Tests]
F -->|Pass| G[Production Deploy]
G --> H[Canary: 5% traffic]
H --> I{Metrics OK?}
I -->|Yes| J[Full Rollout]
I -->|No| K[Auto-Rollback]
GitHub Actions
GitHub Actions uses YAML workflows triggered by events like push, PR, schedule, or manual dispatch.
Basic CI Workflow
# .github/workflows/ci.yml
name: CI
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_PASSWORD: test
options: >-
--health-cmd pg_isready
--health-interval 10s
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm test
env:
DATABASE_URL: postgres://postgres:test@localhost:5432/test
- run: npm run build
Expected behavior: On every PR to main, this workflow checks out code, installs dependencies, starts a PostgreSQL service container, runs tests, and builds. A green checkmark on the PR means all checks passed.
Deployment Workflow with Environments
name: Deploy
on:
workflow_run:
workflows: ["CI"]
branches: [main]
types: [completed]
jobs:
deploy-staging:
if: ${{ github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
environment: staging
steps:
- run: echo "Deploying to staging..."
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
environment:
name: production
url: https://dodabrowser.com
steps:
- run: echo "Deploying to production..."
Expected behavior: After CI passes on main, the deployment workflow starts. Staging deploys first, and only if it succeeds does production deploy. Each environment has its own approval rules and secrets.
Matrix Builds
jobs:
test-matrix:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node: [18, 20, 22]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: ${{ matrix.node }} }
- run: npm ci && npm test
Expected behavior: 9 parallel jobs run across platforms and Node versions. If any combination fails, the overall workflow is marked as failed.
Jenkins — Declarative Pipeline
Jenkins pipelines are defined in a Jenkinsfile checked into the repository.
// Jenkinsfile
pipeline {
agent any
environment {
DOCKER_REGISTRY = 'registry.example.com'
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Build') {
steps {
sh 'docker build -t ${DOCKER_REGISTRY}/app:${BUILD_NUMBER} .'
}
}
stage('Test') {
steps {
sh 'docker run --rm ${DOCKER_REGISTRY}/app:${BUILD_NUMBER} npm test'
}
}
stage('Deploy Staging') {
when {
branch 'main'
}
steps {
sh 'deploy-to-staging.sh ${BUILD_NUMBER}'
}
}
}
post {
failure {
slackSend(color: '#FF0000',
message: "Pipeline failed: ${env.JOB_NAME} #${env.BUILD_NUMBER}")
}
}
}
Expected behavior: Jenkins runs the pipeline on every push. If any stage fails, subsequent stages are skipped. On failure, a Slack notification is sent. The BUILD_NUMBER uniquely identifies each run.
GitLab CI
GitLab CI uses .gitlab-ci.yml with stages, jobs, and runners.
# .gitlab-ci.yml
stages:
- lint
- test
- build
- deploy
variables:
DOCKER_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
lint:
stage: lint
image: node:20-alpine
script:
- npm ci
- npm run lint
test:
stage: test
image: node:20-alpine
script:
- npm ci
- npm test
build:
stage: build
image: docker:latest
services:
- docker:dind
script:
- docker build -t $DOCKER_IMAGE .
- docker push $DOCKER_IMAGE
deploy:
stage: deploy
script:
- kubectl set image deployment/api api=$DOCKER_IMAGE
only:
- main
Expected behavior: Each stage runs sequentially. If lint fails, test and build are skipped. The build stage creates a Docker image tagged with the commit SHA. The deploy stage updates the Kubernetes deployment.
CI/CD Tool Comparison
| Tool | Configuration | Hosting | Pipeline Model | Best For |
|---|---|---|---|---|
| GitHub Actions | YAML | Cloud or self-hosted | Event-triggered, matrix | Open-source and GitHub projects |
| Jenkins | Groovy/DSL (Jenkinsfile) | Self-hosted | Stage-based with shared libs | Enterprise, complex pipelines |
| GitLab CI | YAML | Cloud or self-hosted | Stage-based with DAG | GitLab ecosystem users |
| CircleCI | YAML | Cloud or self-hosted | Workflow-based, parallelism | Teams needing fast feedback |
| ArgoCD | Kubernetes manifests | Kubernetes-native | GitOps pull model | Kubernetes-native deployments |
Common Errors
- Secrets hardcoded in pipeline config — Never put passwords in YAML/DSL files. Use secret variables (GitHub: Settings > Secrets, Jenkins: Credentials Binding, GitLab: CI/CD Variables).
- Long-running pipelines with no caching — Without dependency caching, every run does
npm installfrom scratch. Useactions/cache@v4(GitHub Actions) orcachekeyword (GitLab CI) to speed up builds. - Deploying without testing infrastructure — A green pipeline means unit tests passed, not that the deployment will succeed. Add smoke tests after deployment to verify the service is healthy.
- Not pinning runner versions —
ubuntu-latestcan change its base image at any time. Pin toubuntu-22.04or a specific image tag for reproducible builds. - Skipping staging environment — Deploying directly to production without staging means integration issues are found by users. Always promote through at least one intermediate environment.
Practice Questions
What is the difference between GitHub Actions and Jenkins for CI/CD? GitHub Actions is cloud-native with simpler YAML config and tight GitHub integration. Jenkins is self-hosted, more flexible, and supports complex pipeline patterns with shared libraries.
How do you prevent deployment to production if tests fail in CI? In GitHub Actions, use
needs:andif:conditions to gate deployment jobs. In Jenkins, failed stages prevent subsequent stages from running automatically.What is a matrix build and when would you use it? A matrix build runs the same job across multiple OS/version combinations. Use it to ensure compatibility across platforms and runtime versions.
How does GitLab CI handle environment promotion? GitLab CI uses environments with URLs and deployment jobs. You can restrict jobs to specific branches (
only: main) and add manual approval gates.
Challenge
Create a multi-stage CI/CD pipeline for a microservice: ran lint and unit tests on every PR, build and push a Docker image on merge to main, deploy to a staging environment with integration tests, and after manual approval, deploy to production with a canary rollout (5% traffic for 10 minutes, then full rollout or rollback).
Mini Project: Build a CI/CD Pipeline for a Security Scanner
Create a complete CI/CD pipeline for a file scanning microservice:
- GitHub Actions CI: lint (ESLint), unit tests (Jest), build (Docker image), publish to GitHub Container Registry
- Jenkins pipeline: deploy to staging, run integration tests (scan 100 test files and verify results), run security scan (Trivy on the Docker image)
- GitLab CI: deploy to production using Kubernetes with a blue-green strategy
- Add quality gates: test coverage must be above 80%, no critical vulnerabilities in the Docker image, performance benchmarks must not regress by more than 5%
- Add automatic rollback if health checks fail within 5 minutes of deployment
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro