π What is CI/CD?
π Definitionβ
CI/CD stands for Continuous Integration and Continuous Deployment, a development methodology that automatically tests and deploys code changes. CI automatically builds and tests code whenever it's merged, while CD automatically deploys tested code to production environments. This enables faster bug detection and shorter deployment cycles.
π― Understanding Through Analogiesβ
Factory Automation Lineβ
Traditional Development (Manual)
Developer 1 β Write code
Developer 2 β Write code
Developer 3 β Write code
β
One week later
β
Team lead manually merges
β
Conflicts occur! π±
β
Manual testing
β
Bugs found! π±
β
Manual deployment
β
Deployment fails! π±
CI/CD (Automated)
Developer 1 β Push code β Auto test β
β Auto deploy π
Developer 2 β Push code β Auto test β
β Auto deploy π
Developer 3 β Push code β Auto test β β Instant alert!
(Problems detected and fixed immediately)
Food Deliveryβ
Manual Deployment = Direct Delivery
1. Prepare food (Development)
2. Check packaging (Testing)
3. Verify address (Configuration)
4. Drive and deliver manually (Deployment)
5. Customer confirmation (Verification)
β Time-consuming, error-prone
CI/CD = Automated Delivery System
1. Prepare food (Development)
2. Auto packaging (Auto build)
3. Auto quality check (Auto testing)
4. Robot auto delivery (Auto deployment)
5. Real-time tracking (Monitoring)
β Fast and accurate
βοΈ How It Worksβ
1. CI/CD Pipelineβ
Code Push
β
βββββββββββββββββββββββββββββββ
β CI: Continuous Integration β
β β
β 1. Checkout code β
β 2. Install dependencies β
β 3. Lint check β
β 4. Build β
β 5. Unit tests β
β 6. Integration tests β
β β
β β
All succeed β
β β Any failure β Stop β
βββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββ
β CD: Continuous Deployment β
β β
β 1. Deploy to staging β
β 2. E2E tests β
β 3. Deploy to production β
β 4. Health check β
β 5. Send notifications β
βββββββββββββββββββββββββββββββ
β
π Deployment complete!
2. Traditional Approach vs CI/CDβ
Traditional Approach
βββββββββββββββββββββββββββββββββββββββ
β Monday β
β - Developer A: Develop feature 1 β
β - Developer B: Develop feature 2 β
β - Developer C: Fix bug β
βββββββββββββββββββββββββββββββββββββββ€
β Tuesday-Thursday β
β - Continue individual development β
β - Increasing conflict risk π° β
βββββββββββββββββββββββββββββββββββββββ€
β Friday (Integration Day) β
β - Merge all code β
β - Spend all day resolving conflicts π±β
β - Run tests β Find many bugs β
β - Work overtime through weekend π β
βββββββββββββββββββββββββββββββββββββββ
CI/CD Approach
βββββββββββββββββββββββββββββββββββββββ
β Every day, every commit β
β β
β Developer A: Push code β
β β Auto test β
β
β β Auto deploy π β
β β Reflected in production in 3 min β
β β
β Developer B: Push code β
β β Auto test β β
β β Instant alert β
β β Fix immediately (5 min) β
β β
β - Minimize conflicts β
β - Fast bug detection β
β - Leave on time π β
βββββββββββββββββββββββββββββββββββββββ
3. CI/CD Componentsβ
ββββββββββββββββββββββββββββββββββββββββ
β Version Control β
β Git, GitHub, GitLab β
βββββββββββββββββ¬βββββββββββββββββββββββ
β Push
ββββββββββββββββββββββββββββββββββββββββ
β CI Server β
β GitHub Actions, Jenkins, CircleCI β
β - Checkout code β
β - Auto build β
β - Auto test β
βββββββββββββββββ¬βββββββββββββββββββββββ
β Pass
ββββββββββββββββββββββββββββββββββββββββ
β Artifact Repository β
β Docker Hub, npm Registry β
βββββββββββββββββ¬βββββββββββββββββββββββ
β Deploy
ββββββββββββββββββββββββββββββββββββββββ
β Deployment β
β AWS, Heroku, Vercel, Netlify β
βββββββββββββββββ¬βββββββββββββββββββββββ
β Monitor
ββββββββββββββββββββββββββββββββββββββββ
β Monitoring β
β Sentry, DataDog, CloudWatch β
ββββββββββββββββββββββββββββββββββββββββ
π‘ Real Examplesβ
Basic GitHub Actions Exampleβ
# .github/workflows/ci.yml
# Node.js project CI/CD pipeline
name: CI/CD Pipeline
# Trigger: When pushed to main branch or PR is created
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
# Environment variables
env:
NODE_VERSION: '18'
# Job definitions
jobs:
# ========== Job 1: Test ==========
test:
name: Test and lint
runs-on: ubuntu-latest # Run on Ubuntu
steps:
# 1. Checkout code
- name: Checkout code
uses: actions/checkout@v3
# 2. Setup Node.js
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm' # Use npm cache
# 3. Install dependencies
- name: Install dependencies
run: npm ci # Faster and more stable than npm install
# 4. Lint check
- name: Run lint
run: npm run lint
# 5. Unit tests
- name: Run tests
run: npm test
# 6. Code coverage
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
# ========== Job 2: Build ==========
build:
name: Build
needs: test # Runs only if test succeeds
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
# Production build
- name: Production build
run: npm run build
env:
NODE_ENV: production
# Upload build artifacts
- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: build-files
path: dist/
retention-days: 7
# ========== Job 3: Deploy ==========
deploy:
name: Production deployment
needs: build # Runs only if build succeeds
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' # Only deploy main branch
steps:
- name: Checkout code
uses: actions/checkout@v3
# Download build artifacts
- name: Download build artifacts
uses: actions/download-artifact@v3
with:
name: build-files
path: dist/
# Deploy to Vercel
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'
# Slack notification
- name: Slack notification
if: always() # Run regardless of success/failure
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: 'Deployment ${{ job.status }}!'
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
Docker Build and Deploymentβ
# .github/workflows/docker.yml
# Docker image build and deployment
name: Docker CI/CD
on:
push:
branches: [ main ]
tags:
- 'v*' # Run when tags like v1.0.0 are pushed
env:
DOCKER_IMAGE: myapp
DOCKER_REGISTRY: docker.io
jobs:
docker:
name: Docker build and push
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
# Extract Docker metadata
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha
# Setup Docker Buildx (multi-platform build)
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v2
# Login to Docker Hub
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
# Build and push Docker image
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha # Use GitHub Actions cache
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm64 # Multi-platform
# Deploy (SSH to server and restart container)
- name: Deploy to server
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USERNAME }}
key: ${{ secrets.SERVER_SSH_KEY }}
script: |
docker pull ${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:latest
docker stop myapp || true
docker rm myapp || true
docker run -d \
--name myapp \
-p 3000:3000 \
--restart unless-stopped \
${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:latest
docker system prune -af
Automatic Pull Request Validationβ
# .github/workflows/pr.yml
# Automatic validation on PR creation
name: Pull Request Check
on:
pull_request:
types: [ opened, synchronize, reopened ]
jobs:
# ========== Code Quality Check ==========
lint:
name: Lint and format check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
# ESLint
- name: Run ESLint
run: npm run lint
# Prettier
- name: Check Prettier
run: npm run format:check
# TypeScript type check
- name: TypeScript type check
run: npm run type-check
# ========== Test ==========
test:
name: Test
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16, 18, 20] # Test on multiple Node versions
steps:
- uses: actions/checkout@v3
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test -- --coverage
- name: Upload test results
uses: actions/upload-artifact@v3
if: always()
with:
name: test-results-${{ matrix.node-version }}
path: coverage/
# ========== Security Check ==========
security:
name: Security check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
# npm audit
- name: npm security check
run: npm audit --audit-level=moderate
# Snyk security scan
- name: Snyk security scan
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
# ========== Performance Test ==========
performance:
name: Performance test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
# Lighthouse CI
- name: Lighthouse CI
uses: treosh/lighthouse-ci-action@v9
with:
urls: |
http://localhost:3000
uploadArtifacts: true
# ========== PR Comment ==========
comment:
name: PR comment
needs: [lint, test, security]
runs-on: ubuntu-latest
if: always()
steps:
- name: Write PR comment
uses: actions/github-script@v6
with:
script: |
const statuses = {
lint: '${{ needs.lint.result }}',
test: '${{ needs.test.result }}',
security: '${{ needs.security.result }}'
};
let message = '## π€ CI Check Results\n\n';
for (const [job, status] of Object.entries(statuses)) {
const emoji = status === 'success' ? 'β
' : 'β';
message += `${emoji} **${job}**: ${status}\n`;
}
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: message
});
Scheduled Tasksβ
# .github/workflows/scheduled.yml
# Regularly executed tasks
name: Scheduled Tasks
on:
schedule:
# Daily at 9 AM (UTC 0:00)
- cron: '0 0 * * *'
workflow_dispatch: # Manual execution also possible
jobs:
# ========== Dependency Update Check ==========
dependency-check:
name: Check dependency updates
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
# Check npm outdated
- name: Check outdated packages
run: npm outdated || true
# Dependabot alternative
- name: Check updatable packages
run: |
npx npm-check-updates
# ========== Backup ==========
backup:
name: Database backup
runs-on: ubuntu-latest
steps:
- name: Backup database
run: |
# Actually run database backup script
echo "Backing up database..."
# Upload to S3
- name: Upload backup to S3
uses: aws-actions/configure-aws-credentials@v2
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-2
- name: Upload to S3
run: |
aws s3 cp backup.sql s3://my-backups/$(date +%Y%m%d).sql
# ========== Performance Monitoring ==========
performance-monitor:
name: Performance monitoring
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run Lighthouse
uses: treosh/lighthouse-ci-action@v9
with:
urls: |
https://myapp.com
uploadArtifacts: true
# Slack notification on performance degradation
- name: Performance degradation alert
if: failure()
uses: 8398a7/action-slack@v3
with:
status: failure
text: 'β οΈ Website performance has degraded!'
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
Multi-Environment Deploymentβ
# .github/workflows/multi-env.yml
# Deployment by environment: development, staging, production
name: Multi-Environment Deployment
on:
push:
branches:
- develop # Development environment
- staging # Staging environment
- main # Production environment
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
# Build by environment
- name: Build
run: npm run build
env:
NODE_ENV: ${{
github.ref == 'refs/heads/main' && 'production' ||
github.ref == 'refs/heads/staging' && 'staging' ||
'development'
}}
# Deploy to development
- name: Deploy to development
if: github.ref == 'refs/heads/develop'
run: |
echo "Deploying to development environment..."
# Development server deployment command
env:
API_URL: https://api-dev.myapp.com
# Deploy to staging
- name: Deploy to staging
if: github.ref == 'refs/heads/staging'
run: |
echo "Deploying to staging environment..."
# Staging server deployment command
env:
API_URL: https://api-staging.myapp.com
# Deploy to production
- name: Deploy to production
if: github.ref == 'refs/heads/main'
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: task-definition.json
service: myapp-service
cluster: production-cluster
wait-for-service-stability: true
env:
API_URL: https://api.myapp.com
# Health check after deployment
- name: Health check
run: |
sleep 30 # Wait 30 seconds
URL=${{
github.ref == 'refs/heads/main' && 'https://myapp.com' ||
github.ref == 'refs/heads/staging' && 'https://staging.myapp.com' ||
'https://dev.myapp.com'
}}
STATUS=$(curl -s -o /dev/null -w "%{http_code}" $URL/health)
if [ $STATUS -eq 200 ]; then
echo "β
Health check passed"
else
echo "β Health check failed: $STATUS"
exit 1
fi
# Deployment success notification
- name: Deployment notification
if: success()
uses: 8398a7/action-slack@v3
with:
status: success
text: |
π Deployment complete!
Environment: ${{ github.ref_name }}
Commit: ${{ github.sha }}
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
π€ Frequently Asked Questionsβ
Q1. What are the benefits of adopting CI/CD?β
A:
β
Key Benefits:
1. Faster Bug Detection
Traditional: Discovered during integration after a week
CI/CD: Discovered within 5 minutes after commit
β 90% reduction in fix costs
2. Reduced Deployment Time
Traditional: 2 hours for manual deployment
CI/CD: 5 minutes for auto deployment
β Multiple deployments per day possible
3. Improved Code Quality
- Automatic lint, testing
- Automated code review
- Reduced bugs
4. Increased Developer Productivity
- Eliminate manual tasks
- Automate repetitive work
- Focus only on development
5. Enhanced Stability
- Automatic testing
- Automatic rollback
- Consistent deployment process
6. Better Collaboration
- Minimize code conflicts
- Fast feedback
- Transparent process
Real Case:
Company A: Before CI/CD
- Deployment: Once per month
- Deployment time: 4 hours
- Deployment failure rate: 30%
- Bug detection time: Average 3 days
Company A: After CI/CD
- Deployment: 10 times per day
- Deployment time: 5 minutes
- Deployment failure rate: 5%
- Bug detection time: Average 10 minutes
Q2. GitHub Actions vs Jenkins vs CircleCI?β
A:
// ========== GitHub Actions ==========
Pros:
- Perfect GitHub integration
- Free (public repos, 2000 min/month free)
- Simple setup (YAML files)
- Various actions in Marketplace
Cons:
- GitHub dependent
- Complex workflows limited
Best for:
- Using GitHub
- Simple CI/CD
- Quick start needed
Example:
name: CI
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm test
// ========== Jenkins ==========
Pros:
- Oldest and most mature
- Extensive plugin ecosystem
- Full customization
- Self-hosted (no cost)
Cons:
- Complex setup
- Outdated UI
- Maintenance required
- Steep learning curve
Best for:
- On-premises environment
- Complex pipelines
- Legacy system integration
// ========== CircleCI ==========
Pros:
- Fast build speed
- Excellent Docker support
- Strong parallelization
- Intuitive UI
Cons:
- Paid (limited free tier)
- Only GitHub/GitLab support
Best for:
- Performance critical
- Heavy Docker use
- Parallel testing needed
// ========== Comparison Table ==========
Feature | GitHub Actions | Jenkins | CircleCI
--------------|----------------|---------|----------
Price | Free(limited) | Free | Paid
Setup | Easy | Hard | Medium
Performance | Medium | High | High
Integration | GitHub perfect | Generic | Generic
Maintenance | Not needed | Needed | Not needed
Docker | Medium | Good | Very good
Recommendation:
- Small team, GitHub use β GitHub Actions
- Large team, complex requirements β Jenkins
- Performance critical, have budget β CircleCI
Q3. How to manage secrets?β
A:
# ========== GitHub Secrets Setup ==========
# Repository β Settings β Secrets and variables β Actions
# Usage
- name: Deploy
env:
API_KEY: ${{ secrets.API_KEY }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: npm run deploy
# ========== Environment-specific Secrets ==========
# Repository β Settings β Environments
# Production environment
environment: production
env:
API_URL: ${{ secrets.PROD_API_URL }}
# Staging environment
environment: staging
env:
API_URL: ${{ secrets.STAGING_API_URL }}
# ========== Security Best Practices ==========
# β
Good practice
- name: Deploy
env:
# Get from Secrets
AWS_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY }}
AWS_SECRET_KEY: ${{ secrets.AWS_SECRET_KEY }}
run: |
# Use environment variables
aws s3 sync dist/ s3://my-bucket
# β Bad practice
- name: Deploy
run: |
# Hardcoded in code (never do this!)
export AWS_ACCESS_KEY="AKIAIOSFODNN7EXAMPLE"
export AWS_SECRET_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
# ========== Create .env file ==========
- name: Create .env file
run: |
cat << EOF > .env
API_URL=${{ secrets.API_URL }}
DATABASE_URL=${{ secrets.DATABASE_URL }}
JWT_SECRET=${{ secrets.JWT_SECRET }}
EOF
# ========== Docker Secrets ==========
- name: Docker build
env:
DOCKER_BUILDKIT: 1
run: |
docker build \
--secret id=api_key,env=API_KEY \
--secret id=db_url,env=DATABASE_URL \
-t myapp .
# Dockerfile
# syntax=docker/dockerfile:1
FROM node:18
RUN --mount=type=secret,id=api_key \
API_KEY=$(cat /run/secrets/api_key) npm install
# ========== Using Vault (Advanced) ==========
- name: Get secrets from HashiCorp Vault
uses: hashicorp/vault-action@v2
with:
url: https://vault.mycompany.com
token: ${{ secrets.VAULT_TOKEN }}
secrets: |
secret/data/production api_key | API_KEY ;
secret/data/production db_url | DATABASE_URL
Q4. What happens when tests fail?β
A:
# ========== Default behavior: Pipeline stops ==========
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Run tests
run: npm test
# Test fails β Stop here
# Subsequent steps not executed
deploy:
needs: test # Only runs if test succeeds
runs-on: ubuntu-latest
steps:
- name: Deploy
run: npm run deploy
# This step won't run if test fails β
# ========== Continue on failure ==========
- name: Run tests
run: npm test
continue-on-error: true # Continue even on failure
# β οΈ Not recommended
# ========== Conditional execution ==========
- name: Notify on failure
if: failure() # Only when previous step fails
uses: 8398a7/action-slack@v3
with:
status: failure
text: 'β Tests failed!'
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
- name: Deploy on success
if: success() # Only when all steps succeed
run: npm run deploy
- name: Always run
if: always() # Run regardless of success/failure
run: npm run cleanup
# ========== Retry ==========
- name: Flaky tests (retry)
uses: nick-invision/retry@v2
with:
timeout_minutes: 10
max_attempts: 3
command: npm run test:e2e
# ========== Failure handling example ==========
- name: Test
id: test
run: npm test
- name: Create issue on test failure
if: failure() && steps.test.outcome == 'failure'
uses: actions/github-script@v6
with:
script: |
github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: 'β Test failure',
body: 'Tests have failed. Please check.',
labels: ['bug', 'ci']
});
# ========== Rollback ==========
- name: Deploy
id: deploy
run: npm run deploy
- name: Rollback on deployment failure
if: failure() && steps.deploy.outcome == 'failure'
run: |
echo "Deployment failed! Rolling back to previous version..."
npm run rollback
Q5. How to reduce costs?β
A:
# ========== Use caching ==========
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm' # npm caching (50% faster builds)
- name: Cache dependencies
uses: actions/cache@v3
with:
path: |
~/.npm
node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
# ========== Conditional execution ==========
on:
push:
branches: [ main ]
paths: # Run only on specific file changes
- 'src/**'
- 'package.json'
# Don't run on README.md changes β Cost savings
# ========== Limit concurrent runs ==========
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true # Cancel previous runs
# β Prevent unnecessary duplicate runs
# ========== Self-hosted Runner ==========
# Use own server (free)
jobs:
build:
runs-on: self-hosted # Don't use GitHub Actions free minutes
steps:
- run: npm run build
# ========== Run only short tasks ==========
- name: Check changed files
id: changes
uses: dorny/paths-filter@v2
with:
filters: |
frontend:
- 'frontend/**'
backend:
- 'backend/**'
- name: Frontend tests
if: steps.changes.outputs.frontend == 'true'
run: npm run test:frontend
# Run only on frontend changes β Time savings
- name: Backend tests
if: steps.changes.outputs.backend == 'true'
run: npm run test:backend
# ========== Cost example ==========
# GitHub Actions free tier:
# - Public repos: Unlimited
# - Private repos: 2000 min/month
# Before optimization:
# - Full test every commit (20 min)
# - 10 pushes per day
# - Monthly usage: 10 Γ 20 Γ 30 = 6000 min
# - Overage cost: (6000 - 2000) Γ $0.008 = $32
# After optimization:
# - 10 min saved with caching
# - Test only changed parts
# - Average build time 5 min
# - Monthly usage: 10 Γ 5 Γ 30 = 1500 min
# - Cost: $0 (within free tier)
# β Save $32/month!
π Next Stepsβ
Once you understand CI/CD, learn these topics:
- What is Git? (To be written) - Version control basics
- What is Docker? (To be written) - Containerized deployment
- What is TDD? (To be written) - Test-driven development
Try It Outβ
# ========== 1. Getting started with GitHub Actions ==========
# 1) Create workflow file in repository
mkdir -p .github/workflows
cat > .github/workflows/ci.yml << 'EOF'
name: CI
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- run: npm ci
- run: npm test
EOF
# 2) Commit and push
git add .github/workflows/ci.yml
git commit -m "Add CI workflow"
git push
# 3) Check Actions tab on GitHub
# β See the workflow running automatically!
# ========== 2. Test locally (act) ==========
# Use act to test GitHub Actions locally
# Install act (macOS)
brew install act
# Run workflow
act push
# Run specific job only
act -j test
# ========== 3. Deployment automation examples ==========
# Vercel deployment
npm install -g vercel
vercel login
vercel --prod
# Netlify deployment
npm install -g netlify-cli
netlify login
netlify deploy --prod
# Heroku deployment
heroku login
git push heroku main
π¬ Conclusionβ
CI/CD is an essential tool in modern development:
- CI: Automatically test and integrate code changes
- CD: Automatically deploy tested code
- Benefits: Fast feedback, improved stability, increased productivity
- Tools: GitHub Actions, Jenkins, CircleCI, etc.
Focus on development and deploy faster with automation! π