Skip to content

Deploy

Every deployable Astro / Vite app in the monorepo ships the same deploy script shape and lands on a Cloudflare Pages project. Long-running Node services (weiqi-server, auth-app, billing-indexer, recognition-service, katago-service) deploy via Docker images to a container platform; their docs live in the respective service pages.

This page covers the Cloudflare Pages convention used by the static / SSR frontends.

// apps/<name>/wrangler.jsonc
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "<project-name>",
"pages_build_output_dir": "./dist",
"compatibility_date": "2025-04-01",
"compatibility_flags": ["nodejs_compat"]
}
// apps/<name>/package.json — scripts block
{
"scripts": {
"deploy:cf": "pnpm dlx wrangler pages deploy ./dist --project-name <name> --branch main --commit-dirty=true",
"deploy:cf:preview": "pnpm dlx wrangler pages deploy ./dist --project-name <name> --branch dev --commit-dirty=true",
"cf:login": "pnpm dlx wrangler login"
}
}
ScriptBranchEffect
deploy:cfmainProduction deploy. Overwrites the live URL.
deploy:cf:previewdevPreview deploy to the dev branch alias (see below).
cf:loginOpens the Cloudflare OAuth flow to refresh your local token.

Both scripts pass --commit-dirty=true so a dirty working tree can still be deployed in emergencies. CI uses the same scripts.

Cloudflare Pages assigns each branch a stable URL of the form https://<commit-hash>.<project>.pages.dev. For a stable URL that follows the branch, attach a custom domain:

BranchCNAME targetURL
main<project>.pages.dev<project>.pages.dev
devdev.<project>.pages.devdev.<project>.pages.dev

The dev branch does not get the bare <project>.pages.dev URL — that belongs to main. CNAME dev.<your-domain> to dev.<project>.pages.dev to serve the dev branch under a stable hostname.

The reference workflow is .github/workflows/main-app-astro-pages.yaml. Each Astro / Vite app has its own workflow file with the same shape:

name: deploy <app-name>
on:
push:
branches: [main, dev]
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ github.ref == 'refs/heads/main' && 'prod' || 'dev' }}
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with: { version: 9.15.0 }
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
# Pipe per-environment env into the app's .env at deploy time.
- name: Write env
run: |
cat <<EOF > apps/<app>/.env
${{ secrets.<APP>_ENV }}
EOF
- run: pnpm --dir apps/<app> build
- run: pnpm --dir apps/<app> deploy:cf:${{ github.ref == 'refs/heads/main' && '' || 'preview' }}
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}

secrets.<APP>_ENV holds the raw env-file contents for that environment. It is base64-decoded (or used directly, depending on the workflow) by the Write env step.

Appwrangler.jsonc name
apps/main-app-astrosharp-go-main
apps/weiqi-docssharp-go-docs
apps/game-webgame-web
apps/admin-app(uses default — wrangler name)
apps/comunity-appweiqi-comunity-app

For a one-off deploy from your laptop:

Terminal window
pnpm --dir apps/weiqi-docs cf:login
# Browser opens, you grant Pages:Edit on the account.
pnpm --dir apps/weiqi-docs deploy:cf:preview

cf:login writes the OAuth credentials to ~/.config/.wrangler/. They expire after 30 days; re-run when wrangler complains.

The adapter is wired up even for output: "static" sites because the deploy infra assumes it. With output: "static", no _worker.js is emitted, but the functions/ directory (if present) still deploys as Pages Functions. Keep the adapter; do not remove it just because the site is fully static.