Cloudflare Pages · site branch

Open the station
from anywhere

Merge to main, GitHub Actions publishes the static tree to the site branch, Cloudflare hosts it. The English catalog is /en/.

site
branch
CF
Pages
en/
English
7400
local

The learning station, lecture HTML, and catalogs ship as one static tree. After a merge to main, GitHub Actions builds _site/ and publishes it to Cloudflare Pages (the Git repository may stay private). Later edits to a lecture or a lab land on the same URL. You do not need ports 7200 / 7300 / 7400 on the public site.

Recommended URL (Cloudflare; a private repo can still serve a public site):

https://kaiyuan-xiangmu-jiexi-zixi.pages.dev/

English catalog: https://kaiyuan-xiangmu-jiexi-zixi.pages.dev/en/

(If the first deploy finds the name taken, Cloudflare appends a suffix. Trust the URL in the Actions log.)

Fallback: GitHub Pages https://mikayiyang.github.io/kaiyuan-xiangmu-jiexi-zixi/ (the free plan requires a public repository).

The site root redirects to the Chinese catalog. Interactive stations live on the same origin:

Local port Public path
:7300 Claude Code v1 /learn/claude-v1/
:7301 Claude Code v2 /learn/claude-v2/
:7200 OpenManus × Suna /learn/manus/
:7200/build-guide.html /learn/manus/build-guide.html
:7397 Build-an-agent guide /learn/agent-guide/
:7380 DeepSeek Harness (if that station is merged) /learn/dsh/
:7400/….html lectures (including Easel) same filename at the site root
English lectures /en/

Source catalogs still write 127.0.0.1 ports so local multi-process preview keeps working. Only the build product _site/ rewrites those URLs to relative paths. Do not hand-edit the AUTO-CATALOG region in the Chinese catalog.

The build runs in GitHub Actions. By default it force-pushes _site/ to the site branch. Cloudflare should host that branch; it should not compile from main (that environment is a poor fit for Python plus four Vite apps).


Part 1

Which hosting option

Option Domain Can the repo stay private? Do you need a server?
A. Cloudflare Pages on the site branch (recommended) *.pages.dev Yes; the site itself is public No
A2. Wrangler direct upload same Yes No; the token must be allowed to manage Pages
B. GitHub Pages mikayiyang.github.io/kaiyuan-xiangmu-jiexi-zixi/ Free plan needs Public No
C. Your own small VPS IP or your domain Yes Yes; rsync _site/

github.io does not point at your machine and does not point at Cloudflare. They are independent.


Part 2

A. Cloudflare Pages via Git (stable; no API token required)

Wrangler direct upload has returned error 7003 even when the Account ID length was correct and the secret reached Actions. That usually means the token lacks Pages permission, or the token and the account are not the same Cloudflare account—not a mistyped ID.

So the pipeline is: on merge to main, push the static tree to the site branch. You connect Git in Cloudflare once.

1. Merge the PR so Actions can push site

When Actions → Deploy site is green, the repository should show branch site:

https://github.com/mikayiyang/kaiyuan-xiangmu-jiexi-zixi/tree/site

2. Attach that branch in Cloudflare

  1. Open Workers & Pages.
  2. In the 2026 dashboard, Create opens Workers, not Pages. Use the small link Looking to deploy Pages? Get started, or open Create Pages directly. Do not click the large Connect-to-GitHub box on the Workers screen.
  3. Import an existing Git repository. Authorize GitHub. Select kaiyuan-xiangmu-jiexi-zixi (private is fine).
  4. Settings:
  5. Production branch: site (not main)
  6. Framework preset: None
  7. Build command: empty
  8. Build output directory: /
  9. Save / Deploy.

After that, every merge to main overwrites site, and Cloudflare publishes. The URL remains *.pages.dev.

The red “Cloudflare Pages — Building” check on a pull request

If GitHub build (Actions · Deploy site) is green, you can merge. The Cloudflare red X is a Git preview deploy: Cloudflare checks out the PR’s source branch, looks for _site/ (gitignored, not on that branch), and reports Build failed. That is not “the English station failed to build,” and it does not block publishing.

What actually ships: merge to main → Actions pushes the site branch → Cloudflare hosts site.

To make the red X go away (optional):

  1. Cloudflare → this Pages project → Settings → Builds & deployments
  2. Turn Preview deployments off, or include only branch site
  3. Keep production branch site, empty build command, output /

wrangler.toml in this repo no longer sets pages_build_output_dir = "_site", so Cloudflare will not keep looking for that folder on PRs.


Part 3

A2. Wrangler direct upload (optional)

If the token’s permissions are correct, the same workflow still attempts wrangler pages deploy. Failure does not block the site branch.

Create the token from the Edit Cloudflare Workers template (it includes Pages). Do not grant only one custom line:

  1. Account API tokens → Create Token → Edit Cloudflare Workers → Use template.
  2. Account Resources: the same account.
  3. Copy the Account ID from the Workers & Pages overview (right-hand side). Do not copy a Zone ID under a domain.
  4. Repository Repository secrets:
  5. CLOUDFLARE_API_TOKEN
  6. CLOUDFLARE_ACCOUNT_ID

Local one-shot upload (optional)

export CLOUDFLARE_API_TOKEN='…'
export CLOUDFLARE_ACCOUNT_ID='…'
./scripts/deploy-cloudflare.sh

Part 4

B. GitHub Pages (optional fallback; the current workflow does not push it)

This repository is private. GitHub’s free plan does not serve public Pages from a private repo. Actions currently pushes the site branch for Cloudflare, not a gh-pages branch for github.io.

If the repo later becomes Public and you want github.io:

  1. GitHub repo → Settings → General → Danger Zone → Change repository visibility → Public.
  2. Run python3 scripts/build-site.py, then place _site/ on the branch / official Pages workflow GitHub requires.
  3. Settings → Pages as in GitHub’s docs.

A private repo with GitHub Pro / Team can serve Pages only to people who can read the repo. Day-to-day study should use pages.dev above.


Part 5

C. Mirror the same tree on a small VPS

Useful if you already have a public IP or a domain. Independent of Cloudflare.

python3 scripts/build-site.py
rsync -a --delete _site/ user@your-server:/var/www/learn/

Nginx example:

server {
    listen 80;
    server_name _;
    root /var/www/learn;
    index 总目录.html index.html;
    location / {
        try_files $uri $uri/ /总目录.html;
    }
}

Chinese filenames need a UTF-8 locale on the server.


Part 6

Preview the same tree locally

python3 scripts/build-site.py          # emit _site/
python3 -m http.server 7400 --directory _site --bind 127.0.0.1
# open ../总目录.html
# English: ./

./start-html-previews.sh assembles _site/ by default and points 7400 at that unified tree. Skip assembly with UNIFIED=0 ./start-html-previews.sh.


Part 7

How later edits reach the public site

Edit Markdown / a learning station as usual → python3 build-html.py (if you touched a Markdown file that script indexes) → python3 scripts/build-en-html.py (if you touched English lectures) → python3 update-catalog.py → commit and merge to main. Actions rebuilds _site and publishes. You do not need 7200 / 7300 in order to read the public site.

↑