Deployment Pipeline
This document provides technical details about the deployment pipelines used in the Aitana frontend project.
Overview
The project uses Google Cloud Build with separate pipelines for frontend and backend components, deploying to Google Cloud Run with multi-container architecture.
Pipeline Architecture
GitHub → Cloud Build → Artifact Registry → Cloud Run
↘ Firebase ↗ GCS
Components
- Frontend Pipeline:
cloudbuild.yaml - Backend Pipeline:
backend/cloudbuild.yaml - Container Registry: Google Artifact Registry
- Deployment Target: Google Cloud Run (multi-container)
- Storage: Firebase, Google Cloud Storage
- Configuration: GCS-mounted volumes
Frontend Pipeline (cloudbuild.yaml)
Pipeline Steps
1. Firebase Rules Deployment
- name: 'firebase:${BRANCH_NAME}'
id: 'deploy-firebase-rules'
args:
- firebase -P ${_PROJECT_ID} deploy --only firestore:rules,firestore:indexes,storage --force
2. Firebase Configuration
- name: 'gcr.io/cloud-builders/gcloud'
id: 'get-firebase-config'
entrypoint: 'bash'
args: ['./get-firebase-config.sh']
3. UI Container Build
- name: 'gcr.io/cloud-builders/docker'
id: build-ui
args:
- docker build -t ${_ARTIFACT_REGISTRY_REPO_URL_CLIENT}/${_SERVICE_NAME}/ui:${BRANCH_NAME} .
4. Template Seeding
- name: 'firebase:${BRANCH_NAME}'
id: 'seed-templates'
args:
- npm install && node src/scripts/seed.mjs --project-id=${_PROJECT_ID} --force
5. Frontend Coverage Generation
- name: 'node:18'
id: frontend-coverage
args: ['scripts/generate-coverage.sh']
6. Coverage Badge Upload
- name: 'gcr.io/google.com/cloudsdktool/google-cloud-cli:stable'
id: upload-frontend-coverage-badge
args:
- gsutil cp frontend-coverage-badge.json gs://aitana-public-bucket/coverage-badges/
7. Documentation Generation
- name: 'gcr.io/google.com/cloudsdktool/google-cloud-cli:stable'
id: generate-codebase-docs
args: ['./scripts/generate-codebase-docs.sh']
Build Configuration
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
EXPOSE 8080
CMD ["npm", "start"]
Backend Pipeline (backend/cloudbuild.yaml)
Pipeline Steps
1. Dynamic Dockerfile Generation
- name: 'gcr.io/cloud-builders/docker'
id: backend-dockerfile
args:
- |
cat <<EOF >Dockerfile_cloudrun
FROM python:3.12-slim
RUN pip install uv
WORKDIR /app
COPY . .
RUN uv sync
EXPOSE 8080
CMD uv run --with gunicorn gunicorn --bind :8080 --workers 4 --timeout 0 app:app
EOF
2. Container Build & Push
- name: 'gcr.io/cloud-builders/docker'
id: build-backend
args: ['build', '-t', '${_ARTIFACT_REGISTRY_REPO_URL_CLIENT}/${_SERVICE_NAME}:${BRANCH_NAME}']
- name: 'gcr.io/cloud-builders/docker'
id: push-backend
args: ['push', '${_ARTIFACT_REGISTRY_REPO_URL_CLIENT}/${_SERVICE_NAME}:${BRANCH_NAME}']
3. Parallel Test Execution
# API Tests
- name: python:3.12-slim
id: run-api-tests
args:
- pytest tests/api_tests/ -v --cov=. --cov-report=json:coverage-api.json
# Tool Tests
- name: python:3.12-slim
id: run-tool-tests
args:
- pytest tests/tool_tests/ -v --cov=. --cov-report=json:coverage-tool.json
# Integration Tests
- name: python:3.12-slim
id: run-integration-tests
args:
- pytest tests/integration_tests/ -v --cov=. --cov-report=json:coverage-integration.json
# Model Tests
- name: python:3.12-slim
id: run-model-tests
args:
- pytest tests/model_tests/ -v --cov=. --cov-report=json:coverage-model.json
# Utility Tests
- name: python:3.12-slim
id: run-utility-tests
args:
- pytest tests/utility_tests/ -v --cov=. --cov-report=json:coverage-utility.json
4. Coverage Merging
- name: python:3.12-slim
id: merge-coverage
waitFor: ['run-api-tests', 'run-tool-tests', 'run-integration-tests', 'run-model-tests', 'run-utility-tests']
args:
- |
# Python script to merge coverage reports from all test suites
# Avoids double-counting files tested in multiple suites
Cloud Run Deployment
Multi-Container Architecture
gcloud run deploy ${_SERVICE_NAME} \
--region=${_REGION} \
--platform=managed \
--container=main \
--image=${_ARTIFACT_REGISTRY_REPO_URL_CLIENT}/${_SERVICE_NAME}/ui:${BRANCH_NAME} \
--port=8080 \
--memory=2Gi \
--cpu=1 \
--container=sidecar \
--image=${_ARTIFACT_REGISTRY_REPO_URL_CLIENT}/${_SERVICE_NAME}/backend:${BRANCH_NAME} \
--memory=4Gi \
--cpu=2
Resource Allocation
| Container | Memory | CPU | Port | Purpose | |———–|——–|—–|——|———| | UI (main) | 2Gi | 1 | 8080 | Next.js frontend, public traffic | | Backend (sidecar) | 4Gi | 2 | 1956 | Python API, internal communication |
Environment Variables
ENV GOOGLE_CLOUD_PROJECT=${_PROJECT_ID}
ENV GOOGLE_CLOUD_LOCATION=${_REGION}
ENV GOOGLE_GENAI_USE_VERTEXAI=true
ENV FIREBASE_BUCKET=${_FIREBASE_BUCKET}
ENV SERVICE_NAME=${_SERVICE_NAME}
ENV EVALS_URL=${_EVALS_URL}
Secret Management
--set-secrets=LANGFUSE_HOST=LANGFUSE_URL:latest
--set-secrets=LANGFUSE_SECRET_KEY=LANGFUSE_SECRET_KEY:latest
--set-secrets=LANGFUSE_PUBLIC_KEY=LANGFUSE_PUBLIC_KEY:latest
--set-secrets=GOOGLE_API_KEY=GOOGLE_API_KEY:latest
--set-secrets=ANTHROPIC_API_KEY=ANTHROPIC_API_KEY:latest
--set-secrets=MAILGUN_API_KEY=MAILGUN_API_KEY:latest
--set-secrets=MAILGUN_WEBHOOK_SECRET=MAILGUN_WEBHOOK_SECRET:latest
Volume Mounts
--add-volume name=gcs_config,type=cloud-storage,bucket=${_CONFIG_BUCKET},readonly=true
--add-volume-mount volume=gcs_config,mount-path=/gcs_config
Configuration Management
Substitution Variables
substitutions:
_SERVICE_NAME: frontend / backend-api
_REGION: terraform_managed
_PROJECT_ID: terraform_managed
_ARTIFACT_REGISTRY_REPO_URL_CLIENT: terraform_managed
_FIREBASE_BUCKET: '${_PROJECT_ID}.firebasestorage.app'
_CONFIG_BUCKET: terraform_managed
_EVALS_URL: terraform_managed
Terraform Integration
- Infrastructure managed via Terraform
- Substitution variables populated from Terraform outputs
- Consistent resource naming and tagging
Testing Pipeline
Test Environment Setup
env:
- "GOOGLE_CLOUD_PROJECT=$_PROJECT_ID"
- "GOOGLE_CLOUD_LOCATION=$_REGION"
- "VAC_CONFIG_FOLDER=/tmp/vac_config"
- "GOOGLE_CLOUD_LOGGING=1"
- "FIREBASE_BUCKET=$_FIREBASE_BUCKET"
- "SERVICE_NAME=${_SERVICE_NAME}"
Test Configuration
# Minimal test config for CI
kind: vacConfig
apiVersion: v1
gcp_config:
project_id: aitana-multivac-dev
location: europe-west1
vac:
test_vac:
llm: vertex
model: gemini-2.0-flash
agent: test-agent
display_name: Test Assistant
Parallel Test Execution
- API Tests: Core endpoints, authentication, assistant configuration
- Tool Tests: AI search, document search, tool orchestration, permissions
- Integration Tests: External services, file processing, email integration
- Model Tests: AI/LLM functionality, smart processing
- Utility Tests: Utils, monitoring, validation, caching
Coverage Reporting
Frontend Coverage
# Generate coverage with Vitest
npm run test:coverage
# Create shield.io badge JSON
{
"schemaVersion": 1,
"label": "${BRANCH_NAME}-coverage",
"message": "${COVERAGE}%",
"color": "${COLOR}"
}
Backend Coverage
# Merge coverage from parallel test runs
# Avoid double-counting files tested in multiple suites
all_files = {}
for file in coverage_files:
# Take highest coverage seen for each file
if new_coverage > existing_coverage:
all_files[filename] = {'covered': covered, 'total': total}
# Calculate combined totals
total_covered = sum(f['covered'] for f in all_files.values())
total_statements = sum(f['total'] for f in all_files.values())
percent_covered = (total_covered / total_statements) * 100
Documentation Pipeline
Codebase Documentation
# Generate comprehensive codebase documentation
./scripts/generate-codebase-docs.sh
# Upload to GCS for AI assistant access
gsutil cp aitana-codebase-complete.txt \
gs://${_CONFIG_BUCKET}/aitana-assistant-docs/
Documentation Bundle
# Generate documentation bundle
./scripts/generate-documentation-bundle.sh
# Upload complete documentation
gsutil cp aitana-documentation-complete.txt \
gs://${_CONFIG_BUCKET}/aitana-assistant-docs/
Monitoring & Logging
Build Logging
options:
logging: GCS_ONLY
logsBucket: gs://multivac-deploy-aitana-logging-bucket
Health Checks
- Cloud Run health endpoints
- Application performance monitoring
- Coverage trend tracking
- Build success/failure rates
Alert Configuration
- Build failure notifications
- Test coverage drops
- Deployment errors
- Performance degradation
Deployment Environments
Development
- Branch:
dev - Deployment: Manual trigger only
- Purpose: Development and integration testing
Staging/Test
- Branch:
test - URL: https://ai2.aitana.chat
- Deployment: Automatic on PR merge
- Purpose: Pre-production testing
Production
- Branch:
prod - URL: https://ai.aitana.chat
- Deployment: Manual approval required
- Purpose: Live production environment
Security Considerations
Secret Management
- All API keys stored in Google Secret Manager
- Secrets mounted at runtime, not in containers
- Least privilege access principles
Container Security
- Base images regularly updated
- Minimal attack surface
- Read-only filesystem where possible
Network Security
- Internal communication between containers
- External access only through Load Balancer
- HTTPS enforcement
Performance Optimization
Build Optimization
- Docker layer caching
- Parallel test execution
- Artifact caching where possible
Runtime Optimization
- Multi-container architecture for separation of concerns
- Appropriate resource allocation per container
- Session affinity for user experience
Monitoring
- Build time tracking
- Deployment duration monitoring
- Resource utilization alerts
Troubleshooting
Common Build Failures
- Test failures: Check individual test suite logs
- Docker build issues: Verify Dockerfile syntax and dependencies
- Coverage drops: Ensure new code has adequate test coverage
- Secret access: Verify Secret Manager permissions
Debugging Steps
# Local build simulation
npm run docker:check
# Check Cloud Build logs
gcloud builds list --limit=10
# View specific build
gcloud builds log BUILD_ID
# Check deployed service status
gcloud run services describe SERVICE_NAME --region=REGION
Related Documentation
- CI/CD Workflow - Complete workflow overview
- Branch Strategy - Git branching model
- Developer Contribution Guide - How to contribute
- Docker Consistency - Local/CI environment matching