What is CI/CD?
CI/CD is a methodology for automating the process from code changes to deployment.
CI (Continuous Integration)
Frequently integrate code changes and automatically verify them
CD (Continuous Deployment)
Automatically deploy verified code to production
Pipeline Flow
Why is automation necessary?
- Prevent human error: Eliminate mistakes from manual processes
- Fast feedback: Catch problems immediately
- Consistency: Deploy through the same process every time
- Time savings: Free up time to focus on development
GitHub Actions Basics
GitHub Actions is a free CI/CD tool built into GitHub. You can use it directly from your code repository without a separate server.
Workflow File Location
my-project/
├── .github/
│ └── workflows/
│ ├── ci.yml # CI workflow
│ ├── deploy.yml # Deployment workflow
│ └── test.yml # Test workflow
├── src/
└── ...YAML Syntax Essentials
name: Hello World CI # Workflow name
on: # Trigger conditions
push:
branches: [main]
pull_request:
branches: [main]
jobs: # Jobs to run
build:
runs-on: ubuntu-latest # Execution environment
steps: # Steps to execute sequentially
- name: Checkout code
uses: actions/checkout@v4
- name: Print Hello World
run: echo "Hello, GitHub Actions!"Trigger Types
| Trigger | Description | Example |
|---|---|---|
| push | On code push | branches: [main] |
| pull_request | On PR creation/update | types: [opened, synchronize] |
| schedule | At scheduled times (cron) | cron: '0 0 * * *' |
| workflow_dispatch | Manual trigger | Click button in GitHub UI |
Commonly Used Actions
actions/checkout@v4
Checkout repository code
actions/setup-node@v4
Set up Node.js environment
actions/setup-python@v5
Set up Python environment
actions/cache@v4
Dependency caching
Frontend Automated Deployment
Automatically deploy React/Vue projects to Cloudflare Pages.
Cloudflare Pages Deployment Workflow
name: Deploy Frontend
on:
push:
branches: [main]
paths:
- 'frontend/**' # Only run when frontend folder changes
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json
- name: Install dependencies
working-directory: frontend
run: npm ci
- name: Build
working-directory: frontend
run: npm run build
env:
VITE_API_URL: ${{ secrets.VITE_API_URL }}
- name: Deploy to Cloudflare Pages
uses: cloudflare/pages-action@v1
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
projectName: my-frontend
directory: frontend/distHow to Set Up Secrets
- GitHub repository → Settings → Secrets and variables → Actions
- Click New repository secret
- Name:
CLOUDFLARE_API_TOKEN - Value: API token generated from Cloudflare dashboard
Creating a Cloudflare API Token
- Cloudflare dashboard → My Profile → API Tokens
- Create Token → Use the Edit Cloudflare Workers template
- Account Resources: Select your account
- Zone Resources: All zones (or a specific domain)
Backend Automated Deployment
Automatically deploy FastAPI/Node.js backends to Cloud Run.
FastAPI → Cloud Run Deployment
name: Deploy Backend (FastAPI)
on:
push:
branches: [main]
paths:
- 'backend/**'
env:
PROJECT_ID: my-gcp-project
REGION: asia-northeast3
SERVICE_NAME: my-api
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Google Auth
uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.GCP_SA_KEY }}
- name: Setup Cloud SDK
uses: google-github-actions/setup-gcloud@v2
- name: Configure Docker
run: gcloud auth configure-docker ${{ env.REGION }}-docker.pkg.dev
- name: Build and Push Docker Image
working-directory: backend
run: |
docker build -t ${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/my-repo/${{ env.SERVICE_NAME }}:${{ github.sha }} .
docker push ${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/my-repo/${{ env.SERVICE_NAME }}:${{ github.sha }}
- name: Deploy to Cloud Run
run: |
gcloud run deploy ${{ env.SERVICE_NAME }} \
--image=${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/my-repo/${{ env.SERVICE_NAME }}:${{ github.sha }} \
--region=${{ env.REGION }} \
--platform=managed \
--allow-unauthenticated \
--set-env-vars="DATABASE_URL=${{ secrets.DATABASE_URL }}"Node.js (Hono) → Cloud Run Deployment
name: Deploy Backend (Hono)
on:
push:
branches: [main]
paths:
- 'backend/**'
env:
PROJECT_ID: my-gcp-project
REGION: asia-northeast3
SERVICE_NAME: my-api
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Google Auth
uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.GCP_SA_KEY }}
- name: Setup Cloud SDK
uses: google-github-actions/setup-gcloud@v2
- name: Configure Docker
run: gcloud auth configure-docker ${{ env.REGION }}-docker.pkg.dev
- name: Build and Push Docker Image
working-directory: backend
run: |
docker build -t ${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/my-repo/${{ env.SERVICE_NAME }}:${{ github.sha }} .
docker push ${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/my-repo/${{ env.SERVICE_NAME }}:${{ github.sha }}
- name: Deploy to Cloud Run
run: |
gcloud run deploy ${{ env.SERVICE_NAME }} \
--image=${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/my-repo/${{ env.SERVICE_NAME }}:${{ github.sha }} \
--region=${{ env.REGION }} \
--platform=managed \
--allow-unauthenticatedCreating GCP Service Account Key
# 1. Create service account
gcloud iam service-accounts create github-actions \
--display-name="GitHub Actions"
# 2. Grant required roles
gcloud projects add-iam-policy-binding PROJECT_ID \
--member="serviceAccount:github-actions@PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/run.admin"
gcloud projects add-iam-policy-binding PROJECT_ID \
--member="serviceAccount:github-actions@PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/storage.admin"
gcloud projects add-iam-policy-binding PROJECT_ID \
--member="serviceAccount:github-actions@PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/iam.serviceAccountUser"
# 3. Generate key (JSON file)
gcloud iam service-accounts keys create key.json \
--iam-account=github-actions@PROJECT_ID.iam.gserviceaccount.com
# 4. Register the contents of key.json as the GCP_SA_KEY secretEnvironment-based Deployment (dev/staging/prod)
name: Deploy by Branch
on:
push:
branches:
- main # Production
- staging # Staging
- develop # Development
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Set environment
run: |
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
echo "ENV=prod" >> $GITHUB_ENV
echo "SERVICE_NAME=my-api" >> $GITHUB_ENV
elif [ "${{ github.ref }}" = "refs/heads/staging" ]; then
echo "ENV=staging" >> $GITHUB_ENV
echo "SERVICE_NAME=my-api-staging" >> $GITHUB_ENV
else
echo "ENV=dev" >> $GITHUB_ENV
echo "SERVICE_NAME=my-api-dev" >> $GITHUB_ENV
fi
# ... Use ${{ env.SERVICE_NAME }} in subsequent deploy stepsAdvanced Patterns
Matrix Build
Test across multiple versions/environments simultaneously.
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20, 22]
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm testSpeed Up Builds with Caching
# Node.js dependency caching
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm' # Automatic caching!
# Python dependency caching
- uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip' # Automatic caching!
# Docker layer caching
- uses: docker/build-push-action@v5
with:
cache-from: type=gha
cache-to: type=gha,mode=maxConditional Execution
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Build
run: npm run build
deploy:
needs: build # Run after build completes
if: github.ref == 'refs/heads/main' # Only on main branch
runs-on: ubuntu-latest
steps:
- name: Deploy
run: echo "Deploying..."Free Tier & Tips
GitHub Actions Free Limits
| Plan | Free Allowance |
|---|---|
| Public repositories | Unlimited |
| Private (Free plan) | 2,000 minutes/month |
| Private (Pro plan) | 3,000 minutes/month |
Cost Optimization Tips
- Use caching: Reduce build time with dependency caching
- Path filters: Only build changed folders (paths option)
- Conditional execution: Skip unnecessary workflows
- Self-hosted Runner: Use your own runner for heavy builds
Common Errors
❌ Permission denied
Service account is missing required roles
→ Add roles like roles/run.admin in IAM
❌ Secret not found
Referencing a variable not registered in Secrets
→ Add the variable in Settings → Secrets
❌ Docker build failed
Works locally but fails in CI
→ Check .dockerignore, verify environment variables
🎉 Learning Cycles Complete!
Congratulations! You've completed all learning cycles. You can now build the entire web service deployment pipeline from domain setup to automated deployment. Try starting a real project using Agent Recipes!