What is CI/CD?
- CI (Continuous Integration) — automatically test code on every push
- CD (Continuous Deployment) — automatically deploy to production after tests pass
Basic Workflow
# .github/workflows/deploy.yml
name: Test and Deploy
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Run linter
run: npm run lint
deploy:
needs: test
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to server
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: deploy
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /var/www/myapp
git pull origin main
npm ci --production
npm run build
pm2 restart myapp
Setting Up Secrets
In your GitHub repository:
- Go to Settings → Secrets and variables → Actions
- Add secrets:
| Secret | Value |
|---|---|
SERVER_HOST |
Your server IP |
SSH_PRIVATE_KEY |
Deploy key private key |
DATABASE_URL |
Production database URL |
Docker Build and Push
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
push: true
tags: myorg/myapp:${{ github.sha }},myorg/myapp:latest
cache-from: type=gha
cache-to: type=gha,mode=max
Matrix Testing
Test across multiple versions:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20, 22]
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
Caching Dependencies
- uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
Tip Keep your CI pipeline under 5 minutes. Slow pipelines discourage frequent commits and slow down the entire team.