Deploy demo #89
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Deploy demo | |
| on: | |
| workflow_dispatch: | |
| concurrency: | |
| group: deploy-demo | |
| cancel-in-progress: false | |
| env: | |
| NODE_VERSION: '22' | |
| DEMO_WEB_DOMAIN: https://demo.tonymenu.app | |
| DEMO_PAGES_DOMAIN: https://risto-menu-demo.pages.dev | |
| DEMO_API_DOMAIN: https://api.tonymenu.app | |
| DEMO_CHAT_DOMAIN: https://risto-menu-demo-chat.vekexasia.workers.dev | |
| DEMO_CHAT_NAME: risto-menu-demo-chat | |
| DEMO_CHAT_KV_NAMESPACE_ID: 705f0360cf374bb2b67f4f76b2937dea | |
| DEMO_BACKEND_NAME: risto-menu-demo-api | |
| DEMO_PAGES_PROJECT: risto-menu-demo | |
| DEMO_D1_DATABASE_NAME: risto-menu-demo-db | |
| DEMO_R2_BUCKET_NAME: ${{ vars.DEMO_R2_BUCKET_NAME }} | |
| DEMO_R2_PUBLIC_URL: ${{ vars.DEMO_R2_PUBLIC_URL }} | |
| jobs: | |
| deploy-demo: | |
| name: Deploy demo stack | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| cache: npm | |
| cache-dependency-path: package-lock.json | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Install chat worker dependencies | |
| working-directory: web/workers/chat | |
| run: npm ci | |
| - name: Backend checks | |
| run: | | |
| npm --workspace backend run check | |
| npm --workspace backend run test:run | |
| npm --workspace backend run test:migration-sql | |
| - name: Web checks | |
| run: | | |
| npm --workspace web run lint | |
| npm --workspace web run test:run | |
| - name: Validate demo R2 config | |
| env: | |
| DEMO_R2_BUCKET_NAME: ${{ env.DEMO_R2_BUCKET_NAME }} | |
| DEMO_R2_PUBLIC_URL: ${{ env.DEMO_R2_PUBLIC_URL }} | |
| run: | | |
| test -n "$DEMO_R2_BUCKET_NAME" || (echo "Missing repo/org variable DEMO_R2_BUCKET_NAME" && exit 1) | |
| test -n "$DEMO_R2_PUBLIC_URL" || (echo "Missing repo/org variable DEMO_R2_PUBLIC_URL" && exit 1) | |
| - name: Write demo backend config | |
| working-directory: backend | |
| run: | | |
| cat > wrangler.demo.toml <<EOF | |
| name = "${DEMO_BACKEND_NAME}" | |
| main = "src/index.ts" | |
| compatibility_date = "2025-04-11" | |
| routes = [ | |
| { pattern = "api.tonymenu.app", custom_domain = true }, | |
| { pattern = "risto-menu-api.andreabaccega.com", custom_domain = true } | |
| ] | |
| [vars] | |
| APP_ENV = "staging" | |
| API_VERSION = "v1" | |
| SERVICE_NAME = "risto-menu-demo-api" | |
| COMMIT_SHA = "${GITHUB_SHA}" | |
| DEMO_MODE = "true" | |
| ALLOWED_ORIGINS = "${DEMO_WEB_DOMAIN}" | |
| ALLOWED_HOST_SUFFIXES = ".pages.dev" | |
| R2_PUBLIC_URL = "${DEMO_R2_PUBLIC_URL}" | |
| [triggers] | |
| crons = ["0 * * * *"] | |
| [[d1_databases]] | |
| binding = "DB" | |
| database_name = "${DEMO_D1_DATABASE_NAME}" | |
| database_id = "${{ vars.DEMO_D1_DATABASE_ID }}" | |
| migrations_dir = "drizzle" | |
| [[r2_buckets]] | |
| binding = "PUBLIC_MENU_BUCKET" | |
| bucket_name = "${DEMO_R2_BUCKET_NAME}" | |
| preview_bucket_name = "${DEMO_R2_BUCKET_NAME}" | |
| EOF | |
| - name: Apply demo D1 migrations | |
| working-directory: backend | |
| env: | |
| CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| run: npx wrangler d1 migrations apply "${DEMO_D1_DATABASE_NAME}" --remote --config wrangler.demo.toml | |
| - name: Deploy demo backend | |
| working-directory: backend | |
| env: | |
| CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| run: npx wrangler deploy --config wrangler.demo.toml | |
| - name: Write demo chat config | |
| working-directory: web/workers/chat | |
| run: | | |
| cat > wrangler.demo.toml <<EOF | |
| name = "${DEMO_CHAT_NAME}" | |
| main = "src/index.ts" | |
| compatibility_date = "2024-12-01" | |
| [[kv_namespaces]] | |
| binding = "MENU_CACHE" | |
| id = "${DEMO_CHAT_KV_NAMESPACE_ID}" | |
| [[d1_databases]] | |
| binding = "DB" | |
| database_name = "${DEMO_D1_DATABASE_NAME}" | |
| database_id = "${{ vars.DEMO_D1_DATABASE_ID }}" | |
| migrations_dir = "../../../backend/drizzle" | |
| [ai] | |
| binding = "AI" | |
| [vars] | |
| CHAT_SESSION_SECRET = "${{ secrets.DEMO_CHAT_SESSION_SECRET }}" | |
| LLM_PROVIDER = "workers-ai" | |
| LLM_MODEL = "@cf/meta/llama-3.3-70b-instruct-fp8-fast" | |
| DAILY_AI_REQUEST_LIMIT = "200" | |
| ALLOWED_ORIGINS = "${DEMO_WEB_DOMAIN},${DEMO_PAGES_DOMAIN}" | |
| ALLOWED_HOST_SUFFIXES = ".pages.dev" | |
| EOF | |
| - name: Deploy demo chat worker | |
| working-directory: web/workers/chat | |
| env: | |
| CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| run: npx wrangler deploy --config wrangler.demo.toml | |
| - name: Build demo frontend | |
| working-directory: web | |
| env: | |
| NEXT_PUBLIC_API_URL: ${{ env.DEMO_API_DOMAIN }} | |
| NEXT_PUBLIC_CHAT_WORKER_URL: ${{ env.DEMO_CHAT_DOMAIN }} | |
| NEXT_PUBLIC_DEFAULT_LOCALE: en | |
| NEXT_PUBLIC_COMMIT_SHA: ${{ github.sha }} | |
| run: npm run build:cf | |
| - name: Deploy demo frontend | |
| working-directory: web | |
| env: | |
| CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | |
| run: npx wrangler pages deploy out --project-name="${DEMO_PAGES_PROJECT}" --branch=main | |
| - name: Seed demo data | |
| env: | |
| DEMO_API_DOMAIN: ${{ env.DEMO_API_DOMAIN }} | |
| run: | | |
| curl --fail --retry 5 --retry-delay 5 -X POST "${DEMO_API_DOMAIN}/admin/demo/reset" | |
| - name: Smoke test demo API | |
| env: | |
| DEMO_API_DOMAIN: ${{ env.DEMO_API_DOMAIN }} | |
| run: | | |
| curl --fail --retry 8 --retry-all-errors --retry-delay 5 --max-time 20 "${DEMO_API_DOMAIN}/health" -o /tmp/health.json | |
| node -e "const h=require('/tmp/health.json'); if (h.commitSha !== process.env.GITHUB_SHA) throw new Error('API SHA mismatch: ' + h.commitSha)" | |
| curl --fail --retry 8 --retry-all-errors --retry-delay 5 --max-time 20 "${DEMO_API_DOMAIN}/catalog" -o /tmp/catalog.json | |
| node -e "const c=require('/tmp/catalog.json'); const entries=(c.categories||[]).flatMap(cat => cat.entries || []); if (c.restaurant?.name !== 'Trattoria Demo') throw new Error('demo fixture not loaded: ' + c.restaurant?.name); if (entries.length < 10) throw new Error('demo catalog too small'); if (!Array.isArray(c.menus) || c.menus.length === 0) throw new Error('demo has no menus')" | |
| - name: Smoke test demo frontend | |
| env: | |
| DEMO_PAGES_DOMAIN: ${{ env.DEMO_PAGES_DOMAIN }} | |
| DEMO_API_DOMAIN: ${{ env.DEMO_API_DOMAIN }} | |
| run: | | |
| curl --fail --retry 10 --retry-delay 6 "${DEMO_PAGES_DOMAIN}/it/menu/" -o /tmp/menu.html | |
| grep -q "_next/static" /tmp/menu.html | |
| grep -R "${DEMO_API_DOMAIN}" web/out/_next/static | |
| grep -R "${DEMO_CHAT_DOMAIN}" web/out/_next/static | |
| grep -R "${GITHUB_SHA}" web/out/_next/static | |
| ! grep -R "http://localhost\|http://127\.0\.0\.1\|:8787\|:8788" web/out/_next/static | |
| grep -R "Tony" web/out/_next/static | |
| - name: Demo URLs | |
| run: | | |
| echo "Public menu: ${DEMO_WEB_DOMAIN}/it/menu/" | |
| echo "Pages fallback: ${DEMO_PAGES_DOMAIN}/it/menu/" | |
| echo "Admin: ${DEMO_WEB_DOMAIN}/admin" | |
| echo "API: ${DEMO_API_DOMAIN}" |