aws-builds.yml11.2 KBView on GitHub
name: AWS Builds

on:
  pull_request:

permissions:
  contents: read

concurrency:
  group: aws-builds-${{ github.event.pull_request.number || github.ref }}
  cancel-in-progress: true

jobs:
  cedar-app-staging:
    name: cedar-app-staging-aws
    timeout-minutes: 15
    runs-on: ${{ vars.RUNNER_IMAGE || 'ubuntu-latest' }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Determine AWS build scope
        id: plan
        env:
          CEDAR_COMPARE_FROM_SHA: ${{ github.event.pull_request.base.sha }}
          CEDAR_COMPARE_TO_SHA: ${{ github.event.pull_request.head.sha }}
        run: bash ./aws/scripts/plan-surface-scope.sh

      - name: Skip staging app build when not needed
        if: steps.plan.outputs.check_frontend != 'true'
        run: echo 'No frontend-affecting changes; skipping AWS staging app build.'

      - name: Setup pnpm
        if: steps.plan.outputs.check_frontend == 'true'
        uses: pnpm/action-setup@v4

      - name: Setup Node
        if: steps.plan.outputs.check_frontend == 'true'
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: pnpm

      - name: Install workspace dependencies
        if: steps.plan.outputs.check_frontend == 'true'
        run: pnpm install --frozen-lockfile

      - name: Build staging app bundle
        if: steps.plan.outputs.check_frontend == 'true'
        env:
          NODE_OPTIONS: --max-old-space-size=4096
          CEDAR_AWS_STAGING_APP_DOMAIN: ${{ vars.CEDAR_AWS_STAGING_APP_DOMAIN }}
          CEDAR_AWS_STAGING_API_DOMAIN: ${{ vars.CEDAR_AWS_STAGING_API_DOMAIN }}
          CEDAR_AWS_STAGING_PUBLIC_APP_URL: ${{ vars.CEDAR_AWS_STAGING_PUBLIC_APP_URL }}
          CEDAR_AWS_STAGING_PUBLIC_BACKEND_URL: ${{ vars.CEDAR_AWS_STAGING_PUBLIC_BACKEND_URL }}
          VITE_PUBLIC_POSTHOG_KEY: ${{ vars.VITE_PUBLIC_POSTHOG_KEY }}
          VITE_PUBLIC_POSTHOG_HOST: ${{ vars.VITE_PUBLIC_POSTHOG_HOST }}
        run: |
          set -euo pipefail

          export VITE_PUBLIC_APP_URL="${CEDAR_AWS_STAGING_PUBLIC_APP_URL:-https://${CEDAR_AWS_STAGING_APP_DOMAIN:-mail-staging.cedarcopilot.com}}"
          export VITE_PUBLIC_BACKEND_URL="${CEDAR_AWS_STAGING_PUBLIC_BACKEND_URL:-https://${CEDAR_AWS_STAGING_API_DOMAIN:-api.mail-staging.cedarcopilot.com}}"
          # Same-origin /ingest reverse proxy (see aws/lib/stacks/app-stack.ts) so tracker blockers don't drop events.
          export VITE_PUBLIC_POSTHOG_HOST="${VITE_PUBLIC_POSTHOG_HOST:-${VITE_PUBLIC_APP_URL%/}/ingest}"

          pnpm --filter @zero/mail build

  cedar-api-staging:
    name: cedar-api-staging-aws
    timeout-minutes: 20
    runs-on: ${{ vars.RUNNER_IMAGE || 'ubuntu-latest' }}
    env:
      AWS_REGION: us-east-1
      AWS_DEFAULT_REGION: us-east-1
      CDK_DEFAULT_ACCOUNT: '619071350399'
      CDK_DEFAULT_REGION: us-east-1
      CEDAR_AWS_REGION: us-east-1
      CEDAR_AWS_VPC_ID: ${{ vars.CEDAR_AWS_VPC_ID || 'vpc-placeholder' }}
      CEDAR_AWS_STAGING_APP_DOMAIN: ${{ vars.CEDAR_AWS_STAGING_APP_DOMAIN || 'mail-staging.cedarcopilot.com' }}
      CEDAR_AWS_STAGING_API_DOMAIN: ${{ vars.CEDAR_AWS_STAGING_API_DOMAIN || 'api.mail-staging.cedarcopilot.com' }}
      CEDAR_AWS_STAGING_CERTIFICATE_ARN: ${{ vars.CEDAR_AWS_STAGING_CERTIFICATE_ARN || 'arn:aws:acm:us-east-1:000000000000:certificate/placeholder' }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Determine AWS build scope
        id: plan
        env:
          CEDAR_COMPARE_FROM_SHA: ${{ github.event.pull_request.base.sha }}
          CEDAR_COMPARE_TO_SHA: ${{ github.event.pull_request.head.sha }}
        run: bash ./aws/scripts/plan-surface-scope.sh

      - name: Skip staging backend build when not needed
        if: steps.plan.outputs.check_backend != 'true'
        run: echo 'No backend-affecting changes; skipping AWS staging API build.'

      - name: Setup pnpm
        if: steps.plan.outputs.check_backend == 'true'
        uses: pnpm/action-setup@v4

      - name: Setup Node
        if: steps.plan.outputs.check_backend == 'true'
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: pnpm

      - name: Install workspace dependencies
        if: steps.plan.outputs.check_backend == 'true'
        run: pnpm install --frozen-lockfile

      - name: Install AWS deployment dependencies
        if: steps.plan.outputs.check_backend == 'true'
        run: pnpm --ignore-workspace --dir aws install --no-frozen-lockfile

      - name: Check staging readiness configuration
        if: steps.plan.outputs.check_backend == 'true'
        run: |
          set -euo pipefail

          required_vars=(
            CEDAR_AWS_VPC_ID
            CEDAR_AWS_STAGING_APP_DOMAIN
            CEDAR_AWS_STAGING_API_DOMAIN
            CEDAR_AWS_STAGING_CERTIFICATE_ARN
          )

          for var_name in "${required_vars[@]}"; do
            val="${!var_name:-}"
            if [[ -z "$val" || "$val" == *placeholder* ]]; then
              echo "::warning::Staging readiness variable ${var_name} is using a placeholder — build-only check, not deploy-ready"
            fi
          done

      - name: Build server runtimes and typecheck
        if: steps.plan.outputs.check_backend == 'true'
        run: |
          set -euo pipefail

          pnpm --filter @zero/server build:api-service
          pnpm --filter @zero/server build:chat-service
          pnpm --filter @zero/server build:worker-service
          pnpm aws:typecheck

  cedar-app:
    name: cedar-app-aws
    timeout-minutes: 15
    runs-on: ${{ vars.RUNNER_IMAGE || 'ubuntu-latest' }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Determine AWS build scope
        id: plan
        env:
          CEDAR_COMPARE_FROM_SHA: ${{ github.event.pull_request.base.sha }}
          CEDAR_COMPARE_TO_SHA: ${{ github.event.pull_request.head.sha }}
        run: bash ./aws/scripts/plan-surface-scope.sh

      - name: Skip prod app build when not needed
        if: steps.plan.outputs.check_frontend != 'true'
        run: echo 'No frontend-affecting changes; skipping AWS prod app build.'

      - name: Setup pnpm
        if: steps.plan.outputs.check_frontend == 'true'
        uses: pnpm/action-setup@v4

      - name: Setup Node
        if: steps.plan.outputs.check_frontend == 'true'
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: pnpm

      - name: Install workspace dependencies
        if: steps.plan.outputs.check_frontend == 'true'
        run: pnpm install --frozen-lockfile

      - name: Build prod app bundle
        if: steps.plan.outputs.check_frontend == 'true'
        env:
          NODE_OPTIONS: --max-old-space-size=4096
          CEDAR_AWS_PROD_APP_DOMAIN: ${{ vars.CEDAR_AWS_PROD_APP_DOMAIN }}
          CEDAR_AWS_PROD_API_DOMAIN: ${{ vars.CEDAR_AWS_PROD_API_DOMAIN }}
          CEDAR_AWS_PROD_PUBLIC_APP_URL: ${{ vars.CEDAR_AWS_PROD_PUBLIC_APP_URL }}
          CEDAR_AWS_PROD_PUBLIC_BACKEND_URL: ${{ vars.CEDAR_AWS_PROD_PUBLIC_BACKEND_URL }}
          VITE_PUBLIC_POSTHOG_KEY: ${{ vars.VITE_PUBLIC_POSTHOG_KEY }}
          VITE_PUBLIC_POSTHOG_HOST: ${{ vars.VITE_PUBLIC_POSTHOG_HOST }}
        run: |
          set -euo pipefail

          # Resolve the marketing origin through the same helper the prod publisher uses, so
          # this validation build cannot drift from what actually ships.
          source ./aws/scripts/deploy-utils.sh
          marketing_url="$(cedar_marketing_url_for_env prod)"
          if [[ -n "$marketing_url" ]]; then
            export VITE_PUBLIC_MARKETING_URL="$marketing_url"
          fi

          export VITE_PUBLIC_APP_URL="${CEDAR_AWS_PROD_PUBLIC_APP_URL:-https://${CEDAR_AWS_PROD_APP_DOMAIN:-mail.cedarcopilot.com}}"
          export VITE_PUBLIC_BACKEND_URL="${CEDAR_AWS_PROD_PUBLIC_BACKEND_URL:-https://${CEDAR_AWS_PROD_API_DOMAIN:-api.mail.cedarcopilot.com}}"
          export VITE_PUBLIC_POSTHOG_KEY=[redacted]
          # Same-origin /ingest reverse proxy (see aws/lib/stacks/app-stack.ts) so tracker blockers don't drop events.
          export VITE_PUBLIC_POSTHOG_HOST="${VITE_PUBLIC_POSTHOG_HOST:-${VITE_PUBLIC_APP_URL%/}/ingest}"

          pnpm --filter @zero/mail build

  cedar-api:
    name: cedar-api-aws
    timeout-minutes: 20
    runs-on: ${{ vars.RUNNER_IMAGE || 'ubuntu-latest' }}
    env:
      AWS_REGION: us-east-1
      AWS_DEFAULT_REGION: us-east-1
      CDK_DEFAULT_ACCOUNT: '619071350399'
      CDK_DEFAULT_REGION: us-east-1
      CEDAR_AWS_REGION: us-east-1
      CEDAR_AWS_VPC_ID: ${{ vars.CEDAR_AWS_VPC_ID || 'vpc-placeholder' }}
      CEDAR_AWS_PROD_APP_DOMAIN: ${{ vars.CEDAR_AWS_PROD_APP_DOMAIN || 'mail.cedarcopilot.com' }}
      CEDAR_AWS_PROD_API_DOMAIN: ${{ vars.CEDAR_AWS_PROD_API_DOMAIN || 'api.mail.cedarcopilot.com' }}
      CEDAR_AWS_PROD_CERTIFICATE_ARN: ${{ vars.CEDAR_AWS_PROD_CERTIFICATE_ARN || 'arn:aws:acm:us-east-1:000000000000:certificate/placeholder' }}
      CEDAR_AWS_PROD_MARKETING_DOMAINS: ${{ vars.CEDAR_AWS_PROD_MARKETING_DOMAINS || 'cedarcopilot.com,www.cedarcopilot.com' }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Determine AWS build scope
        id: plan
        env:
          CEDAR_COMPARE_FROM_SHA: ${{ github.event.pull_request.base.sha }}
          CEDAR_COMPARE_TO_SHA: ${{ github.event.pull_request.head.sha }}
        run: bash ./aws/scripts/plan-surface-scope.sh

      - name: Skip prod backend build when not needed
        if: steps.plan.outputs.check_backend != 'true'
        run: echo 'No backend-affecting changes; skipping AWS prod API build.'

      - name: Setup pnpm
        if: steps.plan.outputs.check_backend == 'true'
        uses: pnpm/action-setup@v4

      - name: Setup Node
        if: steps.plan.outputs.check_backend == 'true'
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: pnpm

      - name: Install workspace dependencies
        if: steps.plan.outputs.check_backend == 'true'
        run: pnpm install --frozen-lockfile

      - name: Install AWS deployment dependencies
        if: steps.plan.outputs.check_backend == 'true'
        run: pnpm --ignore-workspace --dir aws install --no-frozen-lockfile

      - name: Check prod readiness configuration
        if: steps.plan.outputs.check_backend == 'true'
        run: |
          set -euo pipefail

          required_vars=(
            CEDAR_AWS_VPC_ID
            CEDAR_AWS_PROD_APP_DOMAIN
            CEDAR_AWS_PROD_API_DOMAIN
            CEDAR_AWS_PROD_CERTIFICATE_ARN
          )

          for var_name in "${required_vars[@]}"; do
            val="${!var_name:-}"
            if [[ -z "$val" || "$val" == *placeholder* ]]; then
              echo "::warning::Prod readiness variable ${var_name} is using a placeholder — build-only check, not deploy-ready"
            fi
          done

      - name: Build server runtimes and typecheck
        if: steps.plan.outputs.check_backend == 'true'
        run: |
          set -euo pipefail

          pnpm --filter @zero/server build:api-service
          pnpm --filter @zero/server build:chat-service
          pnpm --filter @zero/server build:worker-service
          pnpm aws:typecheck