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
- Overview
- Cloud Build Setup
- GitHub Actions Setup
- Automated Testing
- Environment Management
- Best Practices
- 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
| Trigger | Action | Target Environment |
|---|---|---|
Push to main | Auto-deploy | Dev/Staging |
Tag v*.*.* | Manual approval → Deploy | Production |
| Pull Request | Build + Test only | None |
| Manual | Cloud Build submit | Any |
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-q6Automatic 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-q6Create Build Trigger via Console
- Go to: https://console.cloud.google.com/cloud-build/triggers
- Click "CREATE TRIGGER"
- 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- 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 | base64Add to GitHub Secrets:
- Go to:
https://github.com/your-org/microservices/settings/secrets/actions - Click "New repository secret"
- Name:
GCP_SA_KEY - Value: Paste the base64-encoded key
- 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 configEnvironment Variable Management
Development (local):
# Use .env.development
cp .env.example .env.development
# Edit with local values
bun run devStaging (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-east1Multi-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-unauthenticatedBest 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: 100Multi-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_modules2. 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>=90Always 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-buildsSlack 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 stableQuick rollback:
# In GitHub Actions
- name: Rollback on failure
if: failure()
run: |
gcloud run services update-traffic $SERVICE_NAME \
--to-revisions $PREVIOUS_REVISION=100 \
--region $REGION5. 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:latestLeast privilege service accounts:
# Use custom service account
gcloud run services update <service> \
--service-account=<service>-sa@project.iam.gserviceaccount.comTroubleshooting
Issue 1: Build Timeout
Error:
ERROR: build timed out after 10 minutesSolution:
# 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 JWTSolution:
# 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 | base64Issue 3: Deployment Fails but Build Succeeds
Error:
ERROR: (gcloud.run.deploy) Revision failed with: container startup probe failedDiagnosis:
# 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=yamlCommon 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 filesTest locally:
# List changed files
git diff --name-only HEAD~1
# Verify they match your path filtersIssue 5: Cloud Build Can't Find Dockerfile
Error:
ERROR: failed to build: failed to get filesystem: unable to find DockerfileSolution:
# 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 rootQuick 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 watchSubstitution Variables Reference
| Variable | Description | Example |
|---|---|---|
${PROJECT_ID} | GCP Project ID | noted-bliss-466410-q6 |
${COMMIT_SHA} | Git commit SHA | fa5d0769c8a... |
${SHORT_SHA} | Short commit SHA | fa5d076 |
${BRANCH_NAME} | Git branch | main |
${TAG_NAME} | Git tag | v1.0.0 |
${BUILD_ID} | Cloud Build ID | 1234-5678-90ab |
${_CUSTOM} | Custom substitution | User-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.yamlcreated 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
- Set up Cloud Build trigger for your first service
- Test automated deployment by pushing to main branch
- Configure GitHub Actions for additional control
- Add automated tests to CI pipeline
- Set up monitoring for build/deployment failures
- Document service-specific deployment notes
See Also:
DEPLOYMENT_GUIDE.md- Manual deployment proceduresNEW_SERVICE_TEMPLATE.md- Creating new servicesARCHITECTURE.md- System architecture overview
Maintained by: Dev Team Last Review: 2025-11-19