CROP
ProjectsParts Services

CI/CD Guide - CROP Microservices

Last Updated: 2025-11-19 Purpose: Continuous Integration and Deployment automation for CROP microservices Platform: Google Cloud Build, GitHub Actions

CI/CD Guide - CROP Microservices

Last Updated: 2025-11-19 Purpose: Continuous Integration and Deployment automation for CROP microservices Platform: Google Cloud Build, GitHub Actions


Table of Contents

  1. Overview
  2. Cloud Build Setup
  3. GitHub Actions Setup
  4. Automated Testing
  5. Environment Management
  6. Best Practices
  7. Troubleshooting

Overview

CI/CD Architecture

GitHub Push (main branch)

GitHub Actions Trigger

Cloud Build Submission

Docker Build (multi-stage)

Push to GCR

Deploy to Cloud Run

Health Check Verification

Notification (success/failure)

Deployment Triggers

TriggerActionTarget Environment
Push to mainAuto-deployDev/Staging
Tag v*.*.*Manual approval → DeployProduction
Pull RequestBuild + Test onlyNone
ManualCloud Build submitAny

Cloud Build Setup

Prerequisites

# Enable required APIs
gcloud services enable cloudbuild.googleapis.com
gcloud services enable run.googleapis.com
gcloud services enable containerregistry.googleapis.com

# Grant Cloud Build permissions
gcloud projects add-iam-policy-binding noted-bliss-466410-q6 \
  --member="serviceAccount:222426967009@cloudbuild.gserviceaccount.com" \
  --role="roles/run.admin"

gcloud projects add-iam-policy-binding noted-bliss-466410-q6 \
  --member="serviceAccount:222426967009@cloudbuild.gserviceaccount.com" \
  --role="roles/iam.serviceAccountUser"

Service-Specific Cloud Build Configuration

File: services/<service-name>/cloudbuild.yaml

# ============================================================================
# Cloud Build Configuration - CROP Microservices
# Service: <service-name>
# ============================================================================

steps:
  # Step 1: Build Docker image
  - name: 'gcr.io/cloud-builders/docker'
    id: 'build-image'
    env:
      - 'DOCKER_BUILDKIT=1'
    args:
      - 'build'
      - '--build-arg'
      - 'BUILD_TIMESTAMP=${BUILD_ID}'
      - '--build-arg'
      - 'GIT_COMMIT=${COMMIT_SHA}'
      - '-f'
      - 'services/${_SERVICE_NAME}/Dockerfile'
      - '-t'
      - 'gcr.io/${PROJECT_ID}/${_SERVICE_NAME}:${COMMIT_SHA}'
      - '-t'
      - 'gcr.io/${PROJECT_ID}/${_SERVICE_NAME}:${TAG_NAME}'
      - '-t'
      - 'gcr.io/${PROJECT_ID}/${_SERVICE_NAME}:latest'
      - '.'
    timeout: 900s

  # Step 2: Push to Container Registry
  - name: 'gcr.io/cloud-builders/docker'
    id: 'push-image'
    args:
      - 'push'
      - '--all-tags'
      - 'gcr.io/${PROJECT_ID}/${_SERVICE_NAME}'
    waitFor: ['build-image']

  # Step 3: Deploy to Cloud Run
  - name: 'gcr.io/cloud-builders/gcloud'
    id: 'deploy-to-cloud-run'
    args:
      - 'run'
      - 'deploy'
      - '${_SERVICE_NAME}'
      - '--image=gcr.io/${PROJECT_ID}/${_SERVICE_NAME}:${COMMIT_SHA}'
      - '--region=${_REGION}'
      - '--platform=managed'
      - '--execution-environment=gen2'
      - '--allow-unauthenticated'
      - '--memory=${_MEMORY}'
      - '--cpu=${_CPU}'
      - '--timeout=${_TIMEOUT}'
      - '--concurrency=${_CONCURRENCY}'
      - '--max-instances=${_MAX_INSTANCES}'
      - '--min-instances=${_MIN_INSTANCES}'
      - '--quiet'
    waitFor: ['push-image']

  # Step 4: Verify deployment
  - name: 'gcr.io/cloud-builders/curl'
    id: 'verify-health'
    entrypoint: 'bash'
    args:
      - '-c'
      - |
        # Get service URL
        SERVICE_URL=$(gcloud run services describe ${_SERVICE_NAME} \
          --region ${_REGION} \
          --format='value(status.url)')

        echo "Testing health endpoint: $SERVICE_URL/health"

        # Wait for deployment to stabilize
        sleep 10

        # Test health endpoint
        RESPONSE=$(curl -s -w "\n%{http_code}" $SERVICE_URL/health)
        HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
        BODY=$(echo "$RESPONSE" | head -n-1)

        echo "HTTP Status: $HTTP_CODE"
        echo "Response: $BODY"

        if [ "$HTTP_CODE" != "200" ]; then
          echo "Health check failed with status $HTTP_CODE"
          exit 1
        fi

        echo "✅ Health check passed"
    waitFor: ['deploy-to-cloud-run']

# Store images in Container Registry
images:
  - 'gcr.io/${PROJECT_ID}/${_SERVICE_NAME}:${COMMIT_SHA}'
  - 'gcr.io/${PROJECT_ID}/${_SERVICE_NAME}:${TAG_NAME}'
  - 'gcr.io/${PROJECT_ID}/${_SERVICE_NAME}:latest'

# Substitutions (can be overridden)
substitutions:
  _SERVICE_NAME: my-service
  _REGION: us-east1
  _MEMORY: 512Mi
  _CPU: '1'
  _TIMEOUT: 300s
  _CONCURRENCY: '80'
  _MAX_INSTANCES: '10'
  _MIN_INSTANCES: '0'

# Build options
options:
  machineType: 'E2_HIGHCPU_8'
  substitution_option: 'ALLOW_LOOSE'
  logging: CLOUD_LOGGING_ONLY

# Timeout for entire build
timeout: 1800s

# Tags for organization
tags:
  - 'microservice'
  - '${_SERVICE_NAME}'
  - 'cloud-run'

Manual Cloud Build Trigger

# From repository root
cd /Users/vova/Code/CROP/microservices

# Submit build for specific service
gcloud builds submit \
  --config services/search/cloudbuild.yaml \
  --substitutions=_SERVICE_NAME=search-service,_REGION=us-east1 \
  --project noted-bliss-466410-q6

# With custom memory and CPU
gcloud builds submit \
  --config services/search/cloudbuild.yaml \
  --substitutions=_SERVICE_NAME=search-service,_MEMORY=1Gi,_CPU=2 \
  --project noted-bliss-466410-q6

Automatic Build Triggers

Create Build Trigger via gcloud

# Create trigger for main branch
gcloud builds triggers create github \
  --name="deploy-search-service" \
  --repo-name="microservices" \
  --repo-owner="your-org" \
  --branch-pattern="^main$" \
  --build-config="services/search/cloudbuild.yaml" \
  --included-files="services/search/**,packages/**" \
  --substitutions="_SERVICE_NAME=search-service,_REGION=us-east1" \
  --project=noted-bliss-466410-q6

# Create trigger for production tags
gcloud builds triggers create github \
  --name="deploy-search-service-prod" \
  --repo-name="microservices" \
  --repo-owner="your-org" \
  --tag-pattern="^search-v[0-9]+\\.[0-9]+\\.[0-9]+$" \
  --build-config="services/search/cloudbuild.yaml" \
  --substitutions="_SERVICE_NAME=search-service,_REGION=us-east1,_MIN_INSTANCES=1" \
  --require-approval \
  --project=noted-bliss-466410-q6

Create Build Trigger via Console

  1. Go to: https://console.cloud.google.com/cloud-build/triggers
  2. Click "CREATE TRIGGER"
  3. Configure:

Basic Settings:

  • Name: deploy-<service-name>
  • Region: us-east1
  • Event: Push to a branch
  • Repository: Connect your GitHub repo
  • Branch: ^main$

Build Configuration:

  • Type: Cloud Build configuration file
  • Location: services/<service-name>/cloudbuild.yaml

Filters:

  • Included files: services/<service-name>/**, packages/**

Substitution Variables:

_SERVICE_NAME = <service-name>
_REGION = us-east1
  1. Click "CREATE"

GitHub Actions Setup

Prerequisites

Create Service Account Key:

# Create service account
gcloud iam service-accounts create github-actions-deployer \
  --display-name="GitHub Actions Deployer" \
  --project=noted-bliss-466410-q6

# Grant permissions
gcloud projects add-iam-policy-binding noted-bliss-466410-q6 \
  --member="serviceAccount:github-actions-deployer@noted-bliss-466410-q6.iam.gserviceaccount.com" \
  --role="roles/cloudbuild.builds.editor"

gcloud projects add-iam-policy-binding noted-bliss-466410-q6 \
  --member="serviceAccount:github-actions-deployer@noted-bliss-466410-q6.iam.gserviceaccount.com" \
  --role="roles/run.admin"

gcloud projects add-iam-policy-binding noted-bliss-466410-q6 \
  --member="serviceAccount:github-actions-deployer@noted-bliss-466410-q6.iam.gserviceaccount.com" \
  --role="roles/iam.serviceAccountUser"

# Create and download key
gcloud iam service-accounts keys create github-actions-key.json \
  --iam-account=github-actions-deployer@noted-bliss-466410-q6.iam.gserviceaccount.com \
  --project=noted-bliss-466410-q6

# Output base64 for GitHub Secret
cat github-actions-key.json | base64

Add to GitHub Secrets:

  1. Go to: https://github.com/your-org/microservices/settings/secrets/actions
  2. Click "New repository secret"
  3. Name: GCP_SA_KEY
  4. Value: Paste the base64-encoded key
  5. Click "Add secret"

GitHub Actions Workflow

File: .github/workflows/deploy-<service-name>.yml

name: Deploy <Service Name>

on:
  push:
    branches:
      - main
    paths:
      - 'services/<service-name>/**'
      - 'packages/**'
      - '.github/workflows/deploy-<service-name>.yml'

  # Allow manual trigger
  workflow_dispatch:
    inputs:
      environment:
        description: 'Environment to deploy to'
        required: true
        default: 'staging'
        type: choice
        options:
          - staging
          - production

env:
  PROJECT_ID: noted-bliss-466410-q6
  SERVICE_NAME: <service-name>
  REGION: us-east1

jobs:
  deploy:
    name: Build and Deploy
    runs-on: ubuntu-latest

    # Set environment based on branch
    environment:
      name: ${{ github.ref == 'refs/heads/main' && 'staging' || 'production' }}

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Authenticate to Google Cloud
        id: auth
        uses: google-github-actions/auth@v2
        with:
          credentials_json: '${{ secrets.GCP_SA_KEY }}'

      - name: Set up Cloud SDK
        uses: google-github-actions/setup-gcloud@v2
        with:
          project_id: ${{ env.PROJECT_ID }}

      - name: Configure Docker for GCR
        run: |
          gcloud auth configure-docker

      - name: Get git commit SHA (short)
        id: git_sha
        run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT

      - name: Build and submit to Cloud Build
        id: build
        run: |
          gcloud builds submit \
            --config services/${{ env.SERVICE_NAME }}/cloudbuild.yaml \
            --substitutions=_SERVICE_NAME=${{ env.SERVICE_NAME }},_REGION=${{ env.REGION }},COMMIT_SHA=${{ steps.git_sha.outputs.sha_short }},TAG_NAME=build-${{ github.run_number }} \
            --project ${{ env.PROJECT_ID }}

      - name: Get service URL
        id: service_url
        run: |
          URL=$(gcloud run services describe ${{ env.SERVICE_NAME }} \
            --region ${{ env.REGION }} \
            --format='value(status.url)' \
            --project ${{ env.PROJECT_ID }})
          echo "url=$URL" >> $GITHUB_OUTPUT

      - name: Test deployment
        run: |
          echo "Testing service at: ${{ steps.service_url.outputs.url }}"

          # Wait for service to stabilize
          sleep 15

          # Test health endpoint
          curl -f ${{ steps.service_url.outputs.url }}/health || exit 1

          echo "✅ Deployment successful"

      - name: Create deployment summary
        run: |
          echo "## Deployment Summary" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "**Service:** ${{ env.SERVICE_NAME }}" >> $GITHUB_STEP_SUMMARY
          echo "**Environment:** ${{ github.ref == 'refs/heads/main' && 'staging' || 'production' }}" >> $GITHUB_STEP_SUMMARY
          echo "**Region:** ${{ env.REGION }}" >> $GITHUB_STEP_SUMMARY
          echo "**Commit:** ${{ steps.git_sha.outputs.sha_short }}" >> $GITHUB_STEP_SUMMARY
          echo "**URL:** ${{ steps.service_url.outputs.url }}" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "✅ Health check passed" >> $GITHUB_STEP_SUMMARY

      - name: Notify on failure
        if: failure()
        run: |
          echo "❌ Deployment failed for ${{ env.SERVICE_NAME }}"
          echo "Check logs: https://console.cloud.google.com/cloud-build/builds?project=${{ env.PROJECT_ID }}"

Pull Request Workflow

File: .github/workflows/pr-checks.yml

name: PR Checks

on:
  pull_request:
    branches:
      - main
    paths:
      - 'services/**'
      - 'packages/**'

jobs:
  detect-changes:
    name: Detect Changed Services
    runs-on: ubuntu-latest
    outputs:
      services: ${{ steps.filter.outputs.changes }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Detect changed services
        uses: dorny/paths-filter@v2
        id: filter
        with:
          filters: |
            search:
              - 'services/search/**'
            health-analytics:
              - 'services/health-analytics/**'
            catalog:
              - 'services/catalog/**'

  test-and-build:
    name: Test and Build
    needs: detect-changes
    if: needs.detect-changes.outputs.services != '[]'
    runs-on: ubuntu-latest
    strategy:
      matrix:
        service: ${{ fromJSON(needs.detect-changes.outputs.services) }}

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Bun
        uses: oven-sh/setup-bun@v1
        with:
          bun-version: 1.3.6

      - name: Install dependencies
        run: |
          cd services/${{ matrix.service }}
          bun install

      - name: Run type check
        run: |
          cd services/${{ matrix.service }}
          bun run typecheck

      - name: Run tests
        run: |
          cd services/${{ matrix.service }}
          bun test

      - name: Build Docker image (test)
        run: |
          docker build \
            -f services/${{ matrix.service }}/Dockerfile \
            -t test-${{ matrix.service }}:pr-${{ github.event.pull_request.number }} \
            .

      - name: Comment PR
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `✅ **${{ matrix.service }}** build and tests passed`
            })

Automated Testing

Unit Tests Configuration

File: services/<service-name>/src/index.test.ts

import { describe, test, expect } from 'bun:test'

describe('Health Endpoint', () => {
  test('GET /health returns 200', async () => {
    const response = await fetch('http://localhost:8080/health')
    expect(response.status).toBe(200)

    const data = await response.json()
    expect(data.status).toBe('ok')
  })
})

describe('API Endpoint', () => {
  test('GET /api/search returns results', async () => {
    const response = await fetch('http://localhost:8080/api/search?q=test')
    expect(response.status).toBe(200)

    const data = await response.json()
    expect(data).toHaveProperty('results')
    expect(data).toHaveProperty('pagination')
  })
})

Integration Tests

File: services/<service-name>/tests/integration.test.ts

import { describe, test, expect, beforeAll, afterAll } from 'bun:test'
import { MongoClient } from 'mongodb'

let mongoClient: MongoClient

beforeAll(async () => {
  // Setup test database
  mongoClient = new MongoClient(process.env.MONGODB_URI!)
  await mongoClient.connect()
})

afterAll(async () => {
  // Cleanup
  await mongoClient.close()
})

describe('Database Integration', () => {
  test('Can connect to MongoDB', async () => {
    const admin = mongoClient.db().admin()
    const result = await admin.ping()
    expect(result.ok).toBe(1)
  })

  test('Can query database', async () => {
    const db = mongoClient.db('crop_dev')
    const collection = db.collection('test')

    const count = await collection.countDocuments()
    expect(count).toBeGreaterThanOrEqual(0)
  })
})

Test Script in package.json

{
  "scripts": {
    "test": "bun test",
    "test:watch": "bun test --watch",
    "test:coverage": "bun test --coverage"
  }
}

CI Test Step

# In cloudbuild.yaml or GitHub Actions
- name: 'oven/bun:1.3.6-debian'
  id: 'run-tests'
  entrypoint: 'bun'
  args: ['test']
  dir: 'services/${_SERVICE_NAME}'
  env:
    - 'NODE_ENV=test'

Environment Management

Environment-Specific Configurations

File Structure:

services/<service-name>/
├── .env.example          # Template
├── .env.development      # Local dev (git-ignored)
├── .env.staging          # Staging config
├── .env.production       # Production config

Environment Variable Management

Development (local):

# Use .env.development
cp .env.example .env.development
# Edit with local values
bun run dev

Staging (via Cloud Build):

# In cloudbuild.yaml
substitutions:
  _ENV_FILE: '.env.staging'

steps:
  - name: 'gcr.io/cloud-builders/gcloud'
    args:
      - 'run'
      - 'services'
      - 'update'
      - '${_SERVICE_NAME}'
      - '--env-vars-file=${_ENV_FILE}'

Production (via Secret Manager):

# Store secrets
echo -n "mongodb://..." | gcloud secrets create mongodb-uri \
  --data-file=- \
  --replication-policy=automatic \
  --project=noted-bliss-466410-q6

# Reference in deployment
gcloud run services update <service-name> \
  --update-secrets=MONGODB_URI=mongodb-uri:latest \
  --region=us-east1

Multi-Environment Deployment

File: .github/workflows/deploy-multi-env.yml

name: Multi-Environment Deployment

on:
  push:
    branches:
      - main
      - develop
  workflow_dispatch:
    inputs:
      environment:
        required: true
        type: choice
        options:
          - development
          - staging
          - production

jobs:
  deploy:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        include:
          - branch: develop
            environment: development
            region: us-east1
            min_instances: 0
          - branch: main
            environment: staging
            region: us-east1
            min_instances: 0
          - branch: production
            environment: production
            region: us-east1
            min_instances: 1

    # Only run if branch matches
    if: github.ref == format('refs/heads/{0}', matrix.branch)

    environment:
      name: ${{ matrix.environment }}
      url: ${{ steps.deploy.outputs.url }}

    steps:
      # ... auth steps ...

      - name: Deploy to ${{ matrix.environment }}
        id: deploy
        run: |
          gcloud run deploy $SERVICE_NAME-${{ matrix.environment }} \
            --image gcr.io/$PROJECT_ID/$SERVICE_NAME:${{ github.sha }} \
            --region ${{ matrix.region }} \
            --min-instances ${{ matrix.min_instances }} \
            --allow-unauthenticated

Best Practices

1. Build Optimization

Use BuildKit for faster builds:

# In cloudbuild.yaml
env:
  - 'DOCKER_BUILDKIT=1'

Use build caching:

# In cloudbuild.yaml
options:
  machineType: 'E2_HIGHCPU_8'
  diskSizeGb: 100

Multi-stage builds:

# Separate dev and prod dependencies
FROM base AS deps-dev
RUN bun install

FROM base AS deps-prod
RUN bun install --production

FROM base AS prod
COPY --from=deps-prod /repo/node_modules ./node_modules

2. Deployment Safety

Health checks before traffic:

# In cloudbuild.yaml
- name: 'gcr.io/cloud-builders/curl'
  args:
    - '-f'  # Fail on HTTP errors
    - '${_SERVICE_URL}/health'

Gradual rollout:

# Deploy new revision without traffic
gcloud run deploy <service> \
  --image <new-image> \
  --no-traffic \
  --tag canary

# Gradually shift traffic
gcloud run services update-traffic <service> \
  --to-revisions <new-revision>=10,<old-revision>=90

Always tag images:

# Tag with git commit SHA and build number
-t gcr.io/${PROJECT_ID}/${SERVICE}:${COMMIT_SHA}
-t gcr.io/${PROJECT_ID}/${SERVICE}:build-${BUILD_NUMBER}

3. Monitoring Deployments

Cloud Build notifications:

# Create Pub/Sub topic
gcloud pubsub topics create cloud-builds

# Subscribe to build updates
gcloud builds submit --async \
  --config cloudbuild.yaml \
  --substitutions=_NOTIFICATION_TOPIC=cloud-builds

Slack notifications (GitHub Actions):

- name: Notify Slack
  uses: slackapi/slack-github-action@v1
  if: always()
  with:
    webhook-url: ${{ secrets.SLACK_WEBHOOK }}
    payload: |
      {
        "text": "Deployment ${{ job.status }}: ${{ env.SERVICE_NAME }}"
      }

4. Rollback Strategy

Keep previous revisions:

# Don't auto-delete old revisions
gcloud run services update <service> \
  --no-traffic \
  --tag stable

Quick rollback:

# In GitHub Actions
- name: Rollback on failure
  if: failure()
  run: |
    gcloud run services update-traffic $SERVICE_NAME \
      --to-revisions $PREVIOUS_REVISION=100 \
      --region $REGION

5. Security

Scan images:

# In cloudbuild.yaml
- name: 'gcr.io/cloud-builders/gcloud'
  args:
    - 'container'
    - 'images'
    - 'scan'
    - 'gcr.io/${PROJECT_ID}/${_SERVICE_NAME}:${COMMIT_SHA}'

Use Secret Manager:

# Never commit secrets to git
# Always use Secret Manager or env vars
gcloud run services update <service> \
  --update-secrets=API_KEY=api-key:latest

Least privilege service accounts:

# Use custom service account
gcloud run services update <service> \
  --service-account=<service>-sa@project.iam.gserviceaccount.com

Troubleshooting

Issue 1: Build Timeout

Error:

ERROR: build timed out after 10 minutes

Solution:

# Increase timeout in cloudbuild.yaml
timeout: 1800s  # 30 minutes

# Use faster machine
options:
  machineType: 'E2_HIGHCPU_8'

Issue 2: GitHub Actions Authentication Failure

Error:

ERROR: (gcloud.auth.activate-service-account) Invalid JWT

Solution:

# Regenerate service account key
gcloud iam service-accounts keys create new-key.json \
  --iam-account=github-actions-deployer@project.iam.gserviceaccount.com

# Update GitHub secret with new key (base64 encoded)
cat new-key.json | base64

Issue 3: Deployment Fails but Build Succeeds

Error:

ERROR: (gcloud.run.deploy) Revision failed with: container startup probe failed

Diagnosis:

# Check service logs
gcloud logging read "resource.type=cloud_run_revision \
  AND resource.labels.service_name=<service> \
  AND severity>=ERROR" \
  --limit=50

# Check revision status
gcloud run revisions describe <revision> \
  --region=us-east1 \
  --format=yaml

Common causes:

  • Missing environment variables
  • Database connection failure
  • Port misconfiguration (not listening on PORT env var)
  • Health check endpoint not responding

Issue 4: Path Filter Not Triggering

Error: GitHub Actions not triggering on push

Solution:

# Check path patterns
on:
  push:
    paths:
      - 'services/search/**'
      - 'packages/**'  # Include shared packages
      - '!**.md'       # Exclude markdown files

Test locally:

# List changed files
git diff --name-only HEAD~1

# Verify they match your path filters

Issue 5: Cloud Build Can't Find Dockerfile

Error:

ERROR: failed to build: failed to get filesystem: unable to find Dockerfile

Solution:

# Ensure Dockerfile path is relative to repo root
- name: 'gcr.io/cloud-builders/docker'
  args:
    - 'build'
    - '-f'
    - 'services/<service-name>/Dockerfile'  # Correct path
    - '.'  # Build context is repo root

Quick Reference

Common Commands

# Submit manual build
gcloud builds submit \
  --config services/<service>/cloudbuild.yaml \
  --substitutions=_SERVICE_NAME=<service>

# List recent builds
gcloud builds list --limit=10

# View build logs
gcloud builds log <build-id>

# Cancel running build
gcloud builds cancel <build-id>

# Trigger GitHub Actions workflow
gh workflow run deploy-<service>.yml

# View GitHub Actions runs
gh run list --workflow=deploy-<service>.yml

# Watch GitHub Actions run
gh run watch

Substitution Variables Reference

VariableDescriptionExample
${PROJECT_ID}GCP Project IDnoted-bliss-466410-q6
${COMMIT_SHA}Git commit SHAfa5d0769c8a...
${SHORT_SHA}Short commit SHAfa5d076
${BRANCH_NAME}Git branchmain
${TAG_NAME}Git tagv1.0.0
${BUILD_ID}Cloud Build ID1234-5678-90ab
${_CUSTOM}Custom substitutionUser-defined

Checklist

Pre-Setup

  • GCP APIs enabled (Cloud Build, Cloud Run, GCR)
  • Cloud Build service account has permissions
  • GitHub repository connected to GCP
  • Service account key created for GitHub Actions
  • GitHub secrets configured

Per-Service Setup

  • cloudbuild.yaml created in service directory
  • Dockerfile tested and working
  • Health endpoint implemented
  • Tests written and passing
  • Environment variables documented
  • Build trigger created (optional)
  • GitHub Actions workflow created (optional)

Post-Deployment

  • Service deployed successfully
  • Health checks passing
  • Logs checked for errors
  • Service URL tested
  • Rollback procedure tested
  • Documentation updated

Next Steps

  1. Set up Cloud Build trigger for your first service
  2. Test automated deployment by pushing to main branch
  3. Configure GitHub Actions for additional control
  4. Add automated tests to CI pipeline
  5. Set up monitoring for build/deployment failures
  6. Document service-specific deployment notes

See Also:

  • DEPLOYMENT_GUIDE.md - Manual deployment procedures
  • NEW_SERVICE_TEMPLATE.md - Creating new services
  • ARCHITECTURE.md - System architecture overview

Maintained by: Dev Team Last Review: 2025-11-19

On this page