Choorai
Cycle 8

CI/CD - Automated Deployment

Push code and get automatic builds, tests, and deployments. Free yourself from repetitive manual deployments and focus on development.

What you'll learn in this Cycle

  • CI/CD concepts and pipeline understanding
  • Writing GitHub Actions workflows
  • Building automated frontend/backend deployment
  • Environment-based deployment strategies

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

Code push
Automatic build
Automatic testing

CD (Continuous Deployment)

Automatically deploy verified code to production

Build success
Automatic deployment
Release complete

Pipeline Flow

📝 Push
🔨 Build
🧪 Test
🚀 Deploy

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

Project structure
my-project/
├── .github/
│   └── workflows/
│       ├── ci.yml          # CI workflow
│       ├── deploy.yml      # Deployment workflow
│       └── test.yml        # Test workflow
├── src/
└── ...

YAML Syntax Essentials

.github/workflows/hello.yml
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

.github/workflows/deploy-frontend.yml
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/dist

How to Set Up Secrets

  1. GitHub repository → SettingsSecrets and variablesActions
  2. Click New repository secret
  3. Name: CLOUDFLARE_API_TOKEN
  4. Value: API token generated from Cloudflare dashboard

Creating a Cloudflare API Token

  1. Cloudflare dashboard → My Profile → API Tokens
  2. Create Token → Use the Edit Cloudflare Workers template
  3. Account Resources: Select your account
  4. Zone Resources: All zones (or a specific domain)

Backend Automated Deployment

Automatically deploy FastAPI/Node.js backends to Cloud Run.

FastAPI → Cloud Run Deployment

.github/workflows/deploy-backend.yml (FastAPI)
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

.github/workflows/deploy-backend.yml (Hono)
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-unauthenticated

Creating GCP Service Account Key

Terminal
# 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 secret

Environment-based Deployment (dev/staging/prod)

Environment-based deployment example
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 steps

Advanced Patterns

Matrix Build

Test across multiple versions/environments simultaneously.

Matrix Build
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 test

Speed Up Builds with Caching

Caching examples
# 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=max

Conditional Execution

Conditional 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!

Last updated: February 22, 2026 · Version: v0.0.1

Send Feedback

Opens a new issue page with your message.

Open GitHub Issue