xtablo-source/docs/CLOUD_BUILD_SETUP.md
Arthur Belleville 247bc8b3af
Add docs
2025-11-14 09:14:25 +01:00

7.8 KiB

Google Cloud Build Setup Guide

Overview

This guide explains how to configure Google Cloud Build for automatic deployment of the XTablo API to Cloud Run.

Prerequisites

  1. Google Cloud Project with billing enabled
  2. Cloud Build API enabled
  3. Cloud Run API enabled
  4. Secret Manager API enabled
  5. Artifact Registry repository created

Required Substitution Variables

The cloudbuild.yaml uses substitution variables that must be configured in your Cloud Build trigger. Here's what each variable is for:

Build Configuration Variables

Variable Description Example
$_NODE_ENV Environment (staging/production) production
$_AR_PROJECT_ID Artifact Registry project ID your-project-id
$_AR_REPOSITORY Artifact Registry repository name xtablo
$_SERVICE_NAME Cloud Run service name xtablo-api

Application Environment Variables

Variable Description Example
$_SUPABASE_URL Supabase project URL https://xxx.supabase.co
$_STREAM_CHAT_API_KEY Stream Chat API key your-stream-api-key
$_EMAIL_USER Email sender address noreply@xtablo.com
$_EMAIL_CLIENT_ID OAuth2 client ID for email your-client-id
$_R2_ACCOUNT_ID Cloudflare R2 account ID your-r2-account-id
$_CORS_ORIGIN CORS allowed origin https://app.xtablo.com
$_XTABLO_URL Frontend application URL https://app.xtablo.com

Setting Up Substitution Variables

Option 1: Via Google Cloud Console

  1. Go to Cloud Build > Triggers
  2. Select your trigger (or create a new one)
  3. Scroll to "Substitution variables"
  4. Add each variable with its value:
    _NODE_ENV = production
    _AR_PROJECT_ID = your-project-id
    _AR_REPOSITORY = xtablo
    _SERVICE_NAME = xtablo-api
    _SUPABASE_URL = https://your-project.supabase.co
    _STREAM_CHAT_API_KEY = your-key
    _EMAIL_USER = noreply@xtablo.com
    _EMAIL_CLIENT_ID = your-client-id
    _R2_ACCOUNT_ID = your-account-id
    _CORS_ORIGIN = https://app.xtablo.com
    _XTABLO_URL = https://app.xtablo.com
    

Option 2: Via gcloud CLI

gcloud builds triggers create github \
  --name="xtablo-api-production" \
  --repo-name="xtablo-source" \
  --repo-owner="your-github-org" \
  --branch-pattern="^main$" \
  --build-config="apps/api/cloudbuild.yaml" \
  --substitutions='
    _NODE_ENV=production,
    _AR_PROJECT_ID=your-project-id,
    _AR_REPOSITORY=xtablo,
    _SERVICE_NAME=xtablo-api,
    _SUPABASE_URL=https://your-project.supabase.co,
    _STREAM_CHAT_API_KEY=your-key,
    _EMAIL_USER=noreply@xtablo.com,
    _EMAIL_CLIENT_ID=your-client-id,
    _R2_ACCOUNT_ID=your-account-id,
    _CORS_ORIGIN=https://app.xtablo.com,
    _XTABLO_URL=https://app.xtablo.com
  '

Setting Up Secrets in Secret Manager

The sensitive values (API keys, tokens, etc.) are stored in Google Cloud Secret Manager. Create these secrets:

Required Secrets

# Supabase secrets
echo -n "your-service-role-key" | gcloud secrets create supabase-service-role-key --data-file=-
echo -n "your-connection-string" | gcloud secrets create supabase-connection-string --data-file=-
echo -n "your-ca-cert" | gcloud secrets create supabase-ca-cert --data-file=-

# Stream Chat secret
echo -n "your-stream-secret" | gcloud secrets create stream-chat-api-secret --data-file=-

# Stripe secrets
echo -n "your-stripe-key" | gcloud secrets create stripe-secret-key --data-file=-
echo -n "your-webhook-secret" | gcloud secrets create stripe-webhook-secret --data-file=-

# Email secrets
echo -n "your-client-secret" | gcloud secrets create email-client-secret --data-file=-
echo -n "your-refresh-token" | gcloud secrets create email-refresh-token --data-file=-

# R2 (Cloudflare) secrets
echo -n "your-access-key-id" | gcloud secrets create r2-access-key-id --data-file=-
echo -n "your-secret-access-key" | gcloud secrets create r2-secret-access-key --data-file=-

Grant Cloud Run Access to Secrets

# Get your Cloud Run service account
PROJECT_ID="your-project-id"
SERVICE_ACCOUNT="${PROJECT_ID}@appspot.gserviceaccount.com"

# Grant access to each secret
for secret in \
  supabase-service-role-key \
  supabase-connection-string \
  supabase-ca-cert \
  stream-chat-api-secret \
  stripe-secret-key \
  stripe-webhook-secret \
  email-client-secret \
  email-refresh-token \
  r2-access-key-id \
  r2-secret-access-key
do
  gcloud secrets add-iam-policy-binding $secret \
    --member="serviceAccount:${SERVICE_ACCOUNT}" \
    --role="roles/secretmanager.secretAccessor"
done

Environment-Specific Configurations

Production Trigger

_NODE_ENV: production
_CORS_ORIGIN: https://app.xtablo.com
_XTABLO_URL: https://app.xtablo.com

Staging Trigger

_NODE_ENV: staging
_CORS_ORIGIN: https://staging.xtablo.com
_XTABLO_URL: https://staging.xtablo.com
_SERVICE_NAME: xtablo-api-staging

Verifying the Setup

After creating your trigger and setting up the secrets:

  1. Test the trigger manually:

    gcloud builds triggers run xtablo-api-production --branch=main
    
  2. Check the build logs:

    gcloud builds list --limit=5
    gcloud builds log BUILD_ID
    
  3. Verify the deployed service:

    gcloud run services describe xtablo-api --region=europe-west1
    
  4. Check environment variables:

    gcloud run services describe xtablo-api --region=europe-west1 --format="value(spec.template.spec.containers[0].env)"
    

Troubleshooting

Build Fails with "Missing Substitution Variable"

  • Ensure all $_VARIABLE names are defined in your trigger
  • Check for typos in variable names

Cloud Run Deployment Fails

  • Verify the service account has necessary permissions
  • Check that the Artifact Registry URL is correct
  • Ensure the image was successfully pushed

Application Fails to Start

  • Check Cloud Run logs: gcloud run logs read --service=xtablo-api --region=europe-west1
  • Verify all secrets are accessible to the service account
  • Check that environment variables are properly set

Secret Access Denied

  • Ensure the Cloud Run service account has roles/secretmanager.secretAccessor for each secret
  • Verify secrets exist: gcloud secrets list

Additional Configuration

Service Account Permissions

Your Cloud Run service needs these roles:

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:${SERVICE_ACCOUNT}" \
  --role="roles/secretmanager.secretAccessor"

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:${SERVICE_ACCOUNT}" \
  --role="roles/cloudsql.client"  # If using Cloud SQL

Cloud Run Configuration Options

You can add these to your cloudbuild.yaml deploy step:

- '--memory'
- '512Mi'
- '--cpu'
- '1'
- '--max-instances'
- '10'
- '--min-instances'
- '0'
- '--concurrency'
- '80'
- '--timeout'
- '300'
- '--allow-unauthenticated'  # If your API is public

Manual Deployment

If you need to deploy manually without Cloud Build:

# Build the image
docker build -f apps/api/Dockerfile --target production -t xtablo-api:latest .

# Tag for Artifact Registry
docker tag xtablo-api:latest europe-west1-docker.pkg.dev/PROJECT_ID/xtablo/xtablo-api:latest

# Push to Artifact Registry
docker push europe-west1-docker.pkg.dev/PROJECT_ID/xtablo/xtablo-api:latest

# Deploy to Cloud Run
gcloud run deploy xtablo-api \
  --image=europe-west1-docker.pkg.dev/PROJECT_ID/xtablo/xtablo-api:latest \
  --region=europe-west1 \
  --set-env-vars="NODE_ENV=production,PORT=8080,..." \
  --update-secrets="SUPABASE_SERVICE_ROLE_KEY=supabase-service-role-key:latest,..."

Support

For more information: