diff --git a/.github/workflows/build-deploy.yml b/.github/workflows/build-deploy.yml index 598a75ab..041c7adc 100644 --- a/.github/workflows/build-deploy.yml +++ b/.github/workflows/build-deploy.yml @@ -257,20 +257,22 @@ jobs: name: web-build path: build/web - - name: Deploy Preview to Cloudflare Pages + - name: Deploy to Cloudflare Pages (Staging/PR Previews) id: deploy uses: cloudflare/wrangler-action@v3 with: apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - command: pages deploy build/web --project-name=cambeerfestival --branch=${{ github.head_ref || github.ref_name }} + command: pages deploy build/web --project-name=staging-cambeerfestival --branch=${{ github.head_ref || github.ref_name }} - name: Comment PR with Preview URL if: github.event_name == 'pull_request' uses: actions/github-script@v7 with: script: | - const previewUrl = '${{ steps.deploy.outputs.deployment-url }}'; + const aliasUrl = '${{ steps.deploy.outputs.pages-deployment-alias-url }}'; + const deployUrl = '${{ steps.deploy.outputs.deployment-url }}'; + const previewUrl = aliasUrl || deployUrl; const comment = `## 🚀 Cloudflare Pages Preview Your preview deployment is ready! diff --git a/.github/workflows/release-web.yml b/.github/workflows/release-web.yml index 1ea4aa13..921a2f2a 100644 --- a/.github/workflows/release-web.yml +++ b/.github/workflows/release-web.yml @@ -82,4 +82,4 @@ jobs: with: apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} - command: pages deploy build/web --project-name=cambeerfestival --branch=main + command: pages deploy build/web --project-name=cambeerfestival --branch=release diff --git a/README.md b/README.md index 7a8db57d..6b0dbde8 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ A Flutter app for browsing beers, ciders, meads, and more at the Cambridge Beer Festival. **Production**: [https://cambeerfestival.app](https://cambeerfestival.app) -**Staging**: [https://richardthe3rd.github.io/cambridge-beer-festival-app/](https://richardthe3rd.github.io/cambridge-beer-festival-app/) +**Staging**: [https://staging.cambeerfestival.app](https://staging.cambeerfestival.app) +**Development**: [https://richardthe3rd.github.io/cambridge-beer-festival-app/](https://richardthe3rd.github.io/cambridge-beer-festival-app/) ## Features @@ -140,17 +141,19 @@ The app is deployed to multiple environments: - **Production** (Cloudflare Pages): [cambeerfestival.app](https://cambeerfestival.app) - Deployed on version tags (e.g., `v2025.12.0`) + - Uses Cloudflare Pages project `cambeerfestival`, branch `release` - Workflow: `.github/workflows/release-web.yml` -- **Staging** (Cloudflare Pages): `main.cambeerfestival.pages.dev` - - Stable preview environment +- **Staging** (Cloudflare Pages): [staging.cambeerfestival.app](https://staging.cambeerfestival.app) + - Stable staging environment - Deployed automatically on push to `main` + - Uses Cloudflare Pages project `cambeerfestival-staging`, branch `main` - Workflow: `.github/workflows/build-deploy.yml` (deploy-web-preview job) - **Development** (GitHub Pages): [richardthe3rd.github.io/cambridge-beer-festival-app](https://richardthe3rd.github.io/cambridge-beer-festival-app/) - Alternative development environment - Deployed automatically on push to `main` - Workflow: `.github/workflows/build-deploy.yml` (deploy-web job) - **PR Previews** (Cloudflare Pages): Unique URL per pull request - - Each PR gets its own preview environment + - Each PR gets its own preview environment (e.g., `.cambeerfestival-staging.pages.dev`) - Preview URL posted as comment on the PR - Workflow: `.github/workflows/build-deploy.yml` (deploy-web-preview job) diff --git a/cloudflare-worker/worker.js b/cloudflare-worker/worker.js index 67dd5206..940c96aa 100644 --- a/cloudflare-worker/worker.js +++ b/cloudflare-worker/worker.js @@ -22,6 +22,7 @@ const FESTIVALS_CACHE_CONTROL = 'no-cache, must-revalidate'; const ALLOWED_ORIGINS = [ 'https://richardthe3rd.github.io', 'https://cambeerfestival.app', + 'https://staging.cambeerfestival.app', 'http://localhost:8080', 'http://localhost:3000', 'http://127.0.0.1:8080', diff --git a/docs/CLOUDFLARE_PAGES_SETUP.md b/docs/CLOUDFLARE_PAGES_SETUP.md index d5e6b89f..1cc970d2 100644 --- a/docs/CLOUDFLARE_PAGES_SETUP.md +++ b/docs/CLOUDFLARE_PAGES_SETUP.md @@ -8,10 +8,29 @@ This document explains how to set up Cloudflare Pages deployment for the Cambrid ## Overview -The app has two deployment targets: +The app uses **two separate Cloudflare Pages projects** for clean separation between production and staging: -1. **GitHub Pages** (Development/Staging): `richardthe3rd.github.io/cambridge-beer-festival-app/` -2. **Cloudflare Pages** (Production): `cambeerfestival.app` +### Cloudflare Pages Projects + +**Project 1: `cambeerfestival`** (Production only) +- Production branch: `release` +- Deploys: Git tags (e.g., `v2025.12.0`) +- Custom domain: `cambeerfestival.app` + +**Project 2: `cambeerfestival-staging`** (Staging + PR previews) +- Production branch: `main` (serves staging) +- Preview branches: PR branches (serve PR previews) +- Deploys: Git main + all PRs +- Custom domain: `staging.cambeerfestival.app` + +### Deployment Architecture + +| Git Event | CF Project | CF Branch | URL | Purpose | +|-----------|------------|-----------|-----|---------| +| Version tag | `cambeerfestival` | `release` | `cambeerfestival.app` | Production | +| Push to `main` | `cambeerfestival-staging` | `main` | `staging.cambeerfestival.app` | Staging | +| Pull Request | `cambeerfestival-staging` | `` | `.cambeerfestival-staging.pages.dev` | PR previews | +| Push to `main` | GitHub Pages | N/A | `richardthe3rd.github.io/...` | Development | ## Prerequisites @@ -21,32 +40,36 @@ The app has two deployment targets: ## Cloudflare Configuration -### 1. Create Cloudflare Pages Project +### 1. Create Cloudflare Pages Projects + +You need **two separate Cloudflare Pages projects**: +- `cambeerfestival` (production) +- `cambeerfestival-staging` (staging/previews) -**Option A: Let GitHub Actions Create the Project (Easiest)** +**Option A: Let GitHub Actions Create the Projects (Easiest)** -The GitHub Actions workflow will automatically create the Cloudflare Pages project on the first deployment. You can skip this step and jump to step 2 (Get Account ID) and step 3 (Create API Token). +Both projects will be automatically created on their first deployment. You can skip this step and jump to step 2 (Get Account ID) and step 3 (Create API Token). -When the workflow runs, it will create a project named `cambeerfestival` automatically. +- First push to `main` will create `cambeerfestival-staging` +- First git tag will create `cambeerfestival` -**Option B: Create Project Manually** +**Option B: Create Projects Manually** -If you prefer to create the project manually first: +If you prefer to create the projects manually first: +**For Production Project:** 1. Log in to [Cloudflare Dashboard](https://dash.cloudflare.com/) 2. Navigate to **Workers & Pages** -3. Click **Create application** or **Create** -4. Choose **Pages** tab -5. Click **Connect to Git** (you can set this up or skip automatic deployments) - - OR use **Upload assets** if available -6. If using Connect to Git: - - You can connect to your repository but disable automatic deployments - - GitHub Actions will handle deployments instead -7. Set **Project name**: `cambeerfestival` +3. Click **Create application** → **Pages** +4. Set **Project name**: `cambeerfestival` +5. Disable automatic deployments (GitHub Actions will handle deployments) -**Important**: The project name must be `cambeerfestival` to match the workflow configuration. +**For Staging Project:** +1. In **Workers & Pages**, click **Create application** → **Pages** +2. Set **Project name**: `cambeerfestival-staging` +3. Disable automatic deployments -**Note**: With GitHub Actions using `cloudflare/pages-action@v1`, the project will be created automatically on first deployment if it doesn't exist. Manual creation is optional. +**Important**: Project names must match the workflow configuration (`cambeerfestival` and `cambeerfestival-staging`). ### 2. Get Cloudflare Account ID @@ -92,24 +115,51 @@ You can reuse the same token by adding Pages permissions to it: **Note**: Using a single token with both Workers and Pages permissions is simpler and follows the principle of consolidating CI/CD credentials for the same application. -### 4. Configure Custom Domain +### 4. Configure Custom Domains + +You need to configure **one custom domain per project**: + +#### 4a. Production Project Domain 1. In Cloudflare Dashboard, go to **Workers & Pages** → **Pages** -2. Select your `cambeerfestival` project -3. Go to **Custom domains** tab -4. Click **Set up a custom domain** -5. Enter: `cambeerfestival.app` -6. Click **Continue** -7. Cloudflare will automatically configure the DNS records -8. Optionally add `www.cambeerfestival.app` as well +2. Select the **`cambeerfestival`** project +3. Go to **Settings** → **Builds & deployments** +4. Set **Production branch** to: `release` +5. Go to **Custom domains** tab +6. Click **Set up a custom domain** +7. Enter: `cambeerfestival.app` +8. Click **Continue** +9. Cloudflare will automatically configure the DNS records + +#### 4b. Staging Project Domain + +1. In Cloudflare Dashboard, go to **Workers & Pages** → **Pages** +2. Create or select the **`cambeerfestival-staging`** project +3. Go to **Settings** → **Builds & deployments** +4. Set **Production branch** to: `main` +5. Go to **Custom domains** tab +6. Click **Set up a custom domain** +7. Enter: `staging.cambeerfestival.app` +8. Click **Continue** +9. Cloudflare will automatically configure the DNS records + +**Note**: The `cambeerfestival-staging` project will be automatically created by GitHub Actions on the first deployment if it doesn't exist. + +#### 4c. Optional: WWW Redirect + +If you want `www.cambeerfestival.app` to redirect to the apex domain: +1. In the `cambeerfestival` project, add `www.cambeerfestival.app` as a custom domain **DNS Records Created** (automatic): - `CNAME cambeerfestival.app` → `cambeerfestival.pages.dev` -- `CNAME www.cambeerfestival.app` → `cambeerfestival.pages.dev` (if www added) +- `CNAME staging.cambeerfestival.app` → `cambeerfestival-staging.pages.dev` +- `CNAME www.cambeerfestival.app` → `cambeerfestival.pages.dev` (optional) ### 5. Update Cloudflare Worker -The Cloudflare Worker for API proxy has already been updated to allow `https://cambeerfestival.app` in CORS origins. +The Cloudflare Worker for API proxy has already been updated to allow both custom domains in CORS origins: +- `https://cambeerfestival.app` (production) +- `https://staging.cambeerfestival.app` (staging) When you deploy worker changes: @@ -404,16 +454,25 @@ Both should remain in free tier unless app sees very high traffic. ## Summary Checklist -- [ ] Cloudflare Pages project `cambeerfestival` created +**Cloudflare Setup:** +- [ ] Cloudflare Pages project `cambeerfestival` created (production) +- [ ] Cloudflare Pages project `cambeerfestival-staging` created (staging/previews) +- [ ] Production project `cambeerfestival` → Production branch set to `release` +- [ ] Staging project `cambeerfestival-staging` → Production branch set to `main` +- [ ] Custom domain `cambeerfestival.app` configured on `cambeerfestival` project +- [ ] Custom domain `staging.cambeerfestival.app` configured on `cambeerfestival-staging` project +- [ ] DNS records configured (automatic via Cloudflare) - [ ] Cloudflare Account ID obtained - [ ] Cloudflare API Token updated with **both** Workers Scripts + Pages permissions -- [ ] Custom domain `cambeerfestival.app` configured in Cloudflare Pages -- [ ] DNS records configured (automatic via Cloudflare) + +**GitHub Setup:** - [ ] GitHub Secret `CLOUDFLARE_API_TOKEN` verified (should work for both Workers and Pages) - [ ] GitHub Secret `CLOUDFLARE_ACCOUNT_ID` added - [ ] GitHub Secret `GOOGLE_SERVICES_JSON` verified - [ ] Workflow files committed (`.github/workflows/release-web.yml` and `build-deploy.yml`) -- [ ] Cloudflare Worker updated with `cambeerfestival.app` CORS origin and wildcard for Pages previews -- [ ] Push to `main` triggers successful deployment to staging + +**Verification:** +- [ ] Cloudflare Worker updated with both custom domains in CORS origins +- [ ] Push to `main` triggers successful deployment to `https://staging.cambeerfestival.app` - [ ] Create tag triggers production deployment to `https://cambeerfestival.app` - [ ] API calls work without CORS errors on all environments