# 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 ```bash 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 ```bash # 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 ```bash # 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 ```yaml _NODE_ENV: production _CORS_ORIGIN: https://app.xtablo.com _XTABLO_URL: https://app.xtablo.com ``` ### Staging Trigger ```yaml _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:** ```bash gcloud builds triggers run xtablo-api-production --branch=main ``` 2. **Check the build logs:** ```bash gcloud builds list --limit=5 gcloud builds log BUILD_ID ``` 3. **Verify the deployed service:** ```bash gcloud run services describe xtablo-api --region=europe-west1 ``` 4. **Check environment variables:** ```bash 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: ```bash 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: ```yaml - '--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: ```bash # 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: - [Cloud Build Documentation](https://cloud.google.com/build/docs) - [Cloud Run Documentation](https://cloud.google.com/run/docs) - [Secret Manager Documentation](https://cloud.google.com/secret-manager/docs)