From 31882f6b73ca051f8f238b80877e888a2c48e17f Mon Sep 17 00:00:00 2001 From: zwitschi Date: Mon, 10 Aug 2026 11:31:52 +0200 Subject: [PATCH] docs: add Cloudflare Pages deploy workflow and update pipeline documentation --- .gitea/workflows/pages-deploy.yml | 33 +++++++ docs/DEPLOYMENT.md | 15 ++-- docs/SETUP.md | 4 + docs/operations/gitea-cloudflare-pages.md | 104 ++++++++++++++++++++++ 4 files changed, 149 insertions(+), 7 deletions(-) create mode 100644 .gitea/workflows/pages-deploy.yml create mode 100644 docs/operations/gitea-cloudflare-pages.md diff --git a/.gitea/workflows/pages-deploy.yml b/.gitea/workflows/pages-deploy.yml new file mode 100644 index 0000000..9087896 --- /dev/null +++ b/.gitea/workflows/pages-deploy.yml @@ -0,0 +1,33 @@ +name: Deploy to Cloudflare Pages + +on: + push: + branches: ["main"] + workflow_dispatch: {} + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Install dependencies + run: cd frontend && npm ci + + - name: Build + run: cd frontend && npm run build + + - name: Deploy to Cloudflare Pages + uses: cloudflare/pages-action@v1 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + projectName: linearvoid + directory: frontend/dist + gitHubToken: ${{ secrets.GITEA_TOKEN }} diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index a874a8e..6d0acd2 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -72,7 +72,7 @@ Before the cutover in the plan above, treat the following as the minimum readine 1. Verify the Hetzner host is reachable over SSH and Docker remote API TLS is working for the Gitea runner. 2. Confirm all required production secrets are present under the `CI_PROD_*` prefix. 3. Confirm `linearvoid.tv`, `www.linearvoid.tv`, and `media.linearvoid.tv` DNS targets are correct for the intended phase. -4. Complete the manual Cloudflare/UptimeRobot checklist and attach evidence in `docs/operations/cloudflare-uptimerobot-checklist.md`. +4. Complete the manual Cloudflare/UptimeRobot checklist and attach evidence in [Cloudflare Uptimerobot checklist](../docs/operations/cloudflare-uptimerobot-checklist.md). 5. Run the production smoke path end to end: viewer SPA, HLS playback, chat connect, admin login, upload, playlist save, and playout health. 6. Verify rollback is still possible before the final DNS switch. @@ -93,11 +93,12 @@ Readiness is not complete until the checklist above and the manual evidence trac Three Gitea Actions workflows drive the full pipeline: -| Workflow | Trigger | Purpose | -| ------------- | ---------------- | ---------------------------------------------------------------------------------------- | -| `ci.yml` | Push to `main` | Lint, build frontend, vet Go, build & push Docker images (`:latest`, `:main`) | -| `cd.yml` | Push to `main` | Deploy to test lab (`192.168.88.40:2375`) — runs **in parallel** with CI | -| `release.yml` | Tag push `*.*.*` | Build & push versioned Docker images (`:X.Y.Z`, `:X.Y`, `:latest`), create Gitea Release | +| Workflow | Trigger | Purpose | +| ------------------ | ---------------- | ---------------------------------------------------------------------------------------- | +| `ci.yml` | Push to `main` | Lint, build frontend, vet Go, build & push Docker images (`:latest`, `:main`) | +| `cd.yml` | Push to `main` | Deploy to test lab (`192.168.88.40:2375`) — runs **in parallel** with CI | +| `release.yml` | Tag push `*.*.*` | Build & push versioned Docker images (`:X.Y.Z`, `:X.Y`, `:latest`), create Gitea Release | +| `pages-deploy.yml` | Push to `main` | Deploy frontend to Cloudflare Pages (see `docs/operations/gitea-cloudflare-pages.md`) | CI and CD run simultaneously on every push to `main`. The deploy pulls whatever `:latest` images are available in the registry at that moment. @@ -265,7 +266,7 @@ docker compose -f docker-compose.prod.yml down -v ## Nginx and Proxy Considerations -- If an external proxy (for example Nginx Proxy Manager) fronts the stack, align request body limits with internal nginx. +- If an external proxy (for example Nginx Proxy Manager) fronts the stack, align request body limits with internal nginx, see [nginx.conf](../infrastructure/nginx/nginx.conf). - For resumable admin uploads, ensure client_max_body_size supports chunk requests in both proxy layers. ## Security Baseline diff --git a/docs/SETUP.md b/docs/SETUP.md index 7be92b0..805a693 100644 --- a/docs/SETUP.md +++ b/docs/SETUP.md @@ -276,6 +276,10 @@ Reference: `infrastructure/cloudflare/cache-rules.md`. ### 4. Cloudflare Pages +Also see [the automated Gitea Actions deployment guide](./operations/gitea-cloudflare-pages.md). + +Manual setup: + 1. Connect Gitea repository to Cloudflare Pages. 2. Build command: diff --git a/docs/operations/gitea-cloudflare-pages.md b/docs/operations/gitea-cloudflare-pages.md new file mode 100644 index 0000000..023060d --- /dev/null +++ b/docs/operations/gitea-cloudflare-pages.md @@ -0,0 +1,104 @@ +# Gitea to Cloudflare Pages — Linear Void + +## Goal + +Deploy the Linear Void frontend (Vite + TypeScript SPA) from Gitea to Cloudflare Pages using Gitea Actions. + +## Prerequisites + +1. **Cloudflare Pages project** created (Dashboard → Workers & Pages → Create → Pages), see [Cloudflare Pages Setup](./cloudflare-pages-setup.md). +2. **Project name**: note the exact name (e.g., `linearvoid`). +3. **API token** with Pages permissions: + - Create at + - Permission: `Account → Cloudflare Pages → Edit` + - Store in Gitea → Settings → Secrets as `CLOUDFLARE_API_TOKEN` +4. **Account ID** from Cloudflare dashboard overview → store as `CLOUDFLARE_ACCOUNT_ID` + +## Project-specific values + +| Setting | Value | +| ------------------ | ---------------------------------------------------------- | +| Framework | Vite + TypeScript | +| Build command | `cd frontend && npm install && npm run build` | +| Output directory | `frontend/dist` | +| Node version | 20 | +| Multi-page entries | `index.html`, `admin/index.html`, `admin/login/index.html` | + +## Gitea Actions workflow + +Create `.gitea/workflows/pages-deploy.yml`: + +```yaml +name: Deploy to Cloudflare Pages + +on: + push: + branches: ["main"] + workflow_dispatch: {} + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Install dependencies + run: cd frontend && npm ci + + - name: Build + run: cd frontend && npm run build + + - name: Deploy to Cloudflare Pages + uses: cloudflare/pages-action@v1 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + projectName: linearvoid + directory: frontend/dist + gitHubToken: ${{ secrets.GITEA_TOKEN }} +``` + +If the `cloudflare/pages-action@v1` action is unavailable from Gitea, use the direct API approach: + +```yaml +- name: Create deployment artifact + run: tar -czf site.tgz -C frontend/dist . + +- name: Deploy to Cloudflare Pages + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + run: | + curl -sS -X POST \ + "https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/pages/projects/linearvoid/deployments" \ + -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \ + -H "Content-Type: application/octet-stream" \ + --data-binary @site.tgz +``` + +## Secrets to add in Gitea + +| Secret | Value | +| ----------------------- | ------------------------------------ | +| `CLOUDFLARE_API_TOKEN` | API token with Pages:Edit permission | +| `CLOUDFLARE_ACCOUNT_ID` | Cloudflare account ID from dashboard | + +## Verify deployment + +1. Push to `main` or trigger manually from Gitea Actions. +2. Check **Gitea Actions** → job logs for the deploy step. +3. Check **Cloudflare Pages** → Deployments for the new deployment. +4. Visit the `.pages.dev` preview URL, then promote to `linearvoid.tv` when ready. + +## Notes + +- The existing `ci.yml` already builds the frontend; the Pages deploy workflow is additive and independent. +- Multi-page SPA (`vite.config.ts`) produces three entry points — Cloudflare Pages serves them correctly. +- For SPA fallback routing, add a `frontend/public/_redirects` file with `/* /index.html 200` before building. +- If deployments fail with 403, verify the API token has `Account → Cloudflare Pages → Edit` permission.