Files
linearvoid/docs/operations/gitea-cloudflare-pages.md
T

6.3 KiB

Gitea to Cloudflare Pages — Linear Void

Goal

Deploy the Linear Void frontend (Vite + TypeScript SPA) from Gitea to Cloudflare Pages using Gitea Actions.

Prerequisites

Important: The Pages project must exist before the API deploy call works. You'll get error 8000007 ("Project not found") if you skip this step.

  1. Cloudflare Pages project created first:
    • Dashboard → Workers & Pages → Create → Pages → Direct Upload
    • Name it linearvoid (or a different name if linearvoid is already taken by a Worker — see note below)
    • See Cloudflare Pages Setup for full details.
  2. Project name: note the exact name (e.g., linearvoid).
  3. API token with Pages permissions:
  4. Account ID from Cloudflare dashboard overview → store as CLOUDFLARE_ACCOUNT_ID

Naming conflict: If a Worker already exists with the name linearvoid, the Pages project must use a different name (e.g., linearvoid-site). Update the projectName in the workflow accordingly. Workers and Pages share a namespace under "Workers & Pages".

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:

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: 22

      - 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 Cloudflare Pages API (multipart/form-data with manifest):

Snippet to generate a manifest in format -F manifest='{"index.html": "abc123", "style.css": "def456"}'

# Build manifest form argument from all files in frontend/dist
form_args="-F manifest={"
for file in $(find frontend/dist -type f | sed 's|^frontend/dist/||'); do
  hash=$(sha256sum "frontend/dist/$file" | awk '{print $1}')
  form_args="$form_args\"$file\":\"$hash\","
done
form_args="${form_args%,}}" # remove trailing comma and close JSON
- name: Generate deployment manifest
  run: |
    cd frontend/dist
    form_args="-F manifest={"
    for file in $(find . -type f ! -name manifest.json | sed 's|^\./||'); do
      hash=$(sha256sum "$file" | awk '{print $1}')
      form_args="$form_args\"$file\":\"$hash\","
    done
    form_args="${form_args%,}}" # remove trailing comma and close JSON
    echo "Form arguments: $form_args"
    echo "FORM_ARGS=$form_args" >> $GITHUB_ENV

- name: Deploy to Cloudflare Pages
  env:
    CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
    CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
  run: |
    cd frontend/dist
    form_args="$FORM_ARGS" # use form arguments generated in previous step
    COMMIT_HASH=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
    curl -sS -X POST \
      "https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/pages/projects/linearvoid/deployments" \
      -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
      -F "commit_hash=${COMMIT_HASH}" \
      $form_args

This matches the Cloudflare Pages Direct Upload API: Content-Type: multipart/form-data, each file as a separate form field, and a manifest JSON mapping filenames to SHA-256 hashes.

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.
  • If you get error 8000007 ("Project not found"), the Pages project hasn't been created yet. Create it via the Cloudflare dashboard (Workers & Pages → Create → Pages → Direct Upload) first, then re-run.
  • If linearvoid is already taken by a Worker, use a different project name like linearvoid-site and update the projectName and PROJECT_NAME values in the workflow.
  • Cloudflare Workers dashboard: https://dash.cloudflare.com/deb11491c54ec53a24772e98ac6d8128/workers/services/view/linearvoid/production/settings — for managing the infrastructure/cloudflare/worker.js deployment
  • Cloudflare Pages project: https://dash.cloudflare.com/deb11491c54ec53a24772e98ac6d8128/pages/view/linearvoid — deployment history and custom domain settings