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

  1. Test failures: Check individual test suite logs
  2. Docker build issues: Verify Dockerfile syntax and dependencies
  3. Coverage drops: Ensure new code has adequate test coverage
  4. 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