Skip to content

Add cloud-agent-web to feature-detection (#4574) #3025

Add cloud-agent-web to feature-detection (#4574)

Add cloud-agent-web to feature-detection (#4574) #3025

name: Deploy to Production
on:
push:
branches: [main]
permissions:
contents: read
actions: read
concurrency:
group: deploy-production
cancel-in-progress: false
env:
VERCEL_VERSION: '53.3.1'
VERCEL_ORG_ID: ${{ vars.VERCEL_ORG_ID }}
jobs:
check-production-db-startup:
runs-on: ${{ vars.RUNNER_DEFAULT_LABEL || 'ubuntu-latest' }}
timeout-minutes: 15
environment: production
env:
DATABASE_CA: ${{ secrets.DATABASE_CA }}
POSTGRES_URL: ${{ secrets.POSTGRES_URL }}
steps:
- name: Checkout code
uses: useblacksmith/checkout@41cdeedae8edb2e684ba22896a5fd2a3cb85db6b # v1
with:
lfs: true
- name: Setup pnpm
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: '.nvmrc'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Check production DB startup path
run: NODE_ENV=production pnpm exec tsx scripts/check-production-db-startup.ts
run-migrations:
runs-on: ${{ vars.RUNNER_DEFAULT_LABEL || 'ubuntu-latest' }}
timeout-minutes: 15
environment: production
env:
DATABASE_CA: ${{ secrets.DATABASE_CA }}
POSTGRES_URL: ${{ secrets.POSTGRES_URL }}
steps:
- name: Checkout code
uses: useblacksmith/checkout@41cdeedae8edb2e684ba22896a5fd2a3cb85db6b # v1
with:
lfs: true
- name: Setup pnpm
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: '.nvmrc'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run Drizzle migrations
run: NODE_ENV=production pnpm run drizzle migrate
stage-app:
uses: ./.github/workflows/stage-vercel-deployment.yml
with:
target_environment: production
vercel_project_id_var: VERCEL_PROJECT_ID_APP
secrets: inherit
stage-global-app:
uses: ./.github/workflows/stage-vercel-deployment.yml
with:
target_environment: production
vercel_project_id_var: VERCEL_PROJECT_ID_GLOBAL_APP
secrets: inherit
promote-app:
needs: [stage-app, check-production-db-startup, run-migrations]
uses: ./.github/workflows/promote-vercel-deployment.yml
with:
deployment_url: ${{ needs.stage-app.outputs.deployment_url }}
secrets: inherit
promote-global-app:
needs: [stage-global-app, check-production-db-startup, run-migrations]
uses: ./.github/workflows/promote-vercel-deployment.yml
with:
deployment_url: ${{ needs.stage-global-app.outputs.deployment_url }}
secrets: inherit
resolve-worker-base:
runs-on: ${{ vars.RUNNER_DEFAULT_LABEL || 'ubuntu-latest' }}
timeout-minutes: 5
outputs:
base_sha: ${{ steps.base.outputs.sha }}
steps:
- name: Resolve last successful production SHA
id: base
env:
GH_TOKEN: ${{ github.token }}
run: |
sha=$(gh run list \
--repo "$GITHUB_REPOSITORY" \
--workflow deploy-production.yml \
--branch main \
--status success \
--json headSha \
--limit 1 \
--jq '.[0].headSha')
if [ -z "$sha" ] || [ "$sha" = "null" ]; then
sha="${{ github.event.before }}"
fi
echo "sha=$sha" >> "$GITHUB_OUTPUT"
deploy-workers:
needs:
[
check-production-db-startup,
run-migrations,
resolve-worker-base,
stage-app,
stage-global-app,
promote-app,
promote-global-app,
]
if: >-
${{
!cancelled() &&
needs.check-production-db-startup.result == 'success' &&
needs.run-migrations.result == 'success' &&
needs.resolve-worker-base.result == 'success' &&
contains(fromJSON('["success","skipped"]'), needs.stage-app.result) &&
contains(fromJSON('["success","skipped"]'), needs.stage-global-app.result) &&
contains(fromJSON('["success","skipped"]'), needs.promote-app.result) &&
contains(fromJSON('["success","skipped"]'), needs.promote-global-app.result)
}}
uses: ./.github/workflows/deploy-workers.yml
with:
base_sha: ${{ needs.resolve-worker-base.outputs.base_sha }}
secrets: inherit
detect-gastown-wasteland-changes:
needs: resolve-worker-base
runs-on: ${{ vars.RUNNER_DEFAULT_LABEL || 'ubuntu-latest' }}
timeout-minutes: 5
outputs:
deploy_wasteland: ${{ steps.detect.outputs.deploy_wasteland }}
deploy_gastown: ${{ steps.detect.outputs.deploy_gastown }}
steps:
- name: Checkout code
uses: useblacksmith/checkout@41cdeedae8edb2e684ba22896a5fd2a3cb85db6b # v1
with:
fetch-depth: 0
- name: Detect Gastown and Wasteland changes
id: detect
run: |
BASE_SHA="${{ needs.resolve-worker-base.outputs.base_sha }}"
if [ -z "$BASE_SHA" ] || [ "$BASE_SHA" = "null" ]; then
BASE_SHA="${{ github.event.before }}"
fi
changed_any() {
git diff --name-only "$BASE_SHA" HEAD -- "$@" | grep -q .
}
SHARED_WORKER_INPUTS=(
pnpm-lock.yaml
pnpm-workspace.yaml
patches
packages/worker-utils
)
WASTELAND_INPUTS=(
services/wasteland
packages/wl-sdk
"${SHARED_WORKER_INPUTS[@]}"
)
GASTOWN_INPUTS=(
services/gastown
packages/db
"${SHARED_WORKER_INPUTS[@]}"
)
deploy_wasteland=false
deploy_gastown=false
if changed_any "${WASTELAND_INPUTS[@]}"; then
deploy_wasteland=true
fi
if changed_any "${GASTOWN_INPUTS[@]}"; then
deploy_gastown=true
fi
echo "deploy_wasteland=$deploy_wasteland" >> "$GITHUB_OUTPUT"
echo "deploy_gastown=$deploy_gastown" >> "$GITHUB_OUTPUT"
echo "deploy_wasteland=$deploy_wasteland"
echo "deploy_gastown=$deploy_gastown"
deploy-gastown-wasteland:
needs:
[
check-production-db-startup,
run-migrations,
resolve-worker-base,
detect-gastown-wasteland-changes,
stage-app,
stage-global-app,
promote-app,
promote-global-app,
]
if: >-
${{
!cancelled() &&
needs.check-production-db-startup.result == 'success' &&
needs.run-migrations.result == 'success' &&
needs.resolve-worker-base.result == 'success' &&
needs.detect-gastown-wasteland-changes.result == 'success' &&
contains(fromJSON('["success","skipped"]'), needs.stage-app.result) &&
contains(fromJSON('["success","skipped"]'), needs.stage-global-app.result) &&
contains(fromJSON('["success","skipped"]'), needs.promote-app.result) &&
contains(fromJSON('["success","skipped"]'), needs.promote-global-app.result) &&
(
needs.detect-gastown-wasteland-changes.outputs.deploy_wasteland == 'true' ||
needs.detect-gastown-wasteland-changes.outputs.deploy_gastown == 'true'
)
}}
runs-on: ${{ vars.RUNNER_DEFAULT_LABEL || 'ubuntu-latest' }}
timeout-minutes: 45
environment: production
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
steps:
- name: Checkout code
uses: useblacksmith/checkout@41cdeedae8edb2e684ba22896a5fd2a3cb85db6b # v1
- name: Setup pnpm
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: '.nvmrc'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Deploy Wasteland
if: needs.detect-gastown-wasteland-changes.outputs.deploy_wasteland == 'true'
run: pnpm --filter cloudflare-wasteland deploy:prod
- name: Upload Wasteland source maps
if: needs.detect-gastown-wasteland-changes.outputs.deploy_wasteland == 'true'
continue-on-error: true
run: pnpm --filter cloudflare-wasteland sentry:sourcemaps
- name: Deploy Gastown
if: needs.detect-gastown-wasteland-changes.outputs.deploy_gastown == 'true'
run: pnpm --filter cloudflare-gastown deploy:prod
- name: Upload Gastown source maps
if: needs.detect-gastown-wasteland-changes.outputs.deploy_gastown == 'true'
continue-on-error: true
run: pnpm --filter cloudflare-gastown sentry:sourcemaps
deploy-kiloclaw:
needs: [check-production-db-startup, run-migrations]
# Ceiling for the reusable workflow's GITHUB_TOKEN (a called workflow cannot
# exceed the caller). The reusable workflow restricts further per job — only
# its isolated attest-sbom job actually uses id-token/attestations.
permissions:
contents: read
packages: write
id-token: write
attestations: write
uses: ./.github/workflows/deploy-kiloclaw.yml
secrets: inherit