Git-backed Pages 分支部署
这篇文档记录 ChatTea Pages 的 GitHub-like 分支部署模型:runner 不直接写 Pages Host 的文件系统,Pages Host 只从配置好的 Git 分支 checkout 静态文件并 serve。
一句话结论
runner node != Pages Host
runner build static site -> git push pages branch -> Pages Host git fetch/checkout -> static serve
Pages Host 不运行文档构建工具链,不要求 runner 挂载 Host 目录,也不要求 runner SSH/rsync 到 Host。两者唯一需要共享的是 Gitea/Git:
外部 runner 节点 --git push--> Gitea Pages 分支 --git fetch--> Pages Host
角色边界
| 角色 | 负责 | 需要访问 | 不应该需要 |
|---|---|---|---|
| Gitea | Git 仓库、Actions 调度、runner registry | runner / Pages Host 都能访问的 Git endpoint | Pages 静态文件 serve root |
| Runner node | 构建 docs、生成静态目录、push Pages 分支 | Gitea、写 Pages 分支的 credential | Pages Host filesystem、SSH、mount、rsync |
| Pages Host syncer | fetch/checkout Pages 分支、原子发布到 serve root | Gitea、读 Pages 分支的 credential | mkdocs/npm/sphinx/Lean/mathlib 等构建工具链 |
| Pages service | serve 已同步的静态文件 | 本机 pages/sites |
Git 写权限、runner token、构建环境 |
这样部署后,heavy docs build 可以放在资源更强的 runner 节点上,Gitea/Pages Host 只作为控制面和静态托管面。
推荐流程
Stable Pages:
push main
-> Gitea Actions workflow
-> 外部 runner build site/
-> runner commit site/ 到 gh-pages 分支
-> runner push gh-pages 到 Gitea
-> Pages Host syncer fetch gh-pages
-> checkout source path
-> atomic replace pages/sites/<owner>/<repo>/
-> Pages URL 返回新内容
PR preview 可以用同一模型扩展:
pull_request
-> 外部 runner build site/
-> runner push dev/pr-<number> 分支,或 push 到 gh-pages 内的 dev/pr-<number>/ 目录
-> Pages Host syncer 发布到 pages/sites/<owner>/<repo>/dev/pr-<number>/
-> Gitea bot comment preview URL
Git Pages 配置模型
第一版可以先不做 UI,用 repo-level config 表示 Git Pages 来源:
{
"repo": "ChatArch/ChatTea",
"enabled": true,
"mode": "git-branch",
"source": {
"repository": "ChatArch/ChatTea",
"remote": "http://127.0.0.1:3000/ChatArch/ChatTea.git",
"branch": "gh-pages",
"path": "/"
},
"channel": "stable",
"trigger": {
"mode": "manual"
},
"base_url": "http://127.0.0.1:3001"
}
产品 UI 可以后续做成:
Repository -> Settings -> Git Pages
页面字段对应:source repository、source branch、source path、publish channel、trigger mode、credential reference、last deploy status。
鉴权模型
这里有两个 credential,不要混用。
Runner 写权限:
runner -> push gh-pages / dev/pr-N
可用 Gitea bot token 或写权限 deploy key。该 credential 放在 Actions secret 中,例如:
CHATTEA_PAGES_PUSH_TOKEN = [REDACTED]
Pages Host 读权限:
Pages Host -> fetch gh-pages / dev/pr-N
可用只读 deploy key、只读 bot token,或 public repo 无 credential。Pages Host 只需要读取配置里的 Pages 分支,不应该持有 runner 的写权限 token。
Host-side syncer
Host syncer 只做文件同步,不执行仓库中的脚本:
load repo Git Pages config
-> validate repo / branch / source path / channel
-> git clone/fetch configured branch into cache
-> copy source path to staging
-> write .chattea-pages-sync.json
-> atomic replace pages/sites/<owner>/<repo>/<channel>
metadata 示例:
{
"repo": "ChatArch/ChatTea",
"source_repository": "ChatArch/ChatTea",
"branch": "gh-pages",
"source_path": "/",
"commit": "<pages-branch-commit>",
"channel": "stable",
"source": "git-branch",
"published_at": "<timestamp>"
}
安全边界:
- branch、repo、source path、channel 都必须校验;
.git目录不复制进 serve root;- syncer 不执行 Pages 分支里的任意命令;
- credential 只通过引用配置,不写入 metadata 或日志;
- 发布时先写 staging,再原子替换目标目录;
- 大型站点需要配置磁盘限制和清理策略。
Workflow 模板
下面是 branch deploy 的形状。真实项目把 Build static site 换成 MkDocs、Sphinx、Docusaurus、Lean/mathlib docs 等构建命令即可。
name: Git-backed Pages
on:
push:
branches:
- main
jobs:
build-and-push-pages:
runs-on: docs-builder
env:
GITEA_BASE_URL: http://127.0.0.1:3000
PAGES_BRANCH: gh-pages
CHATTEA_PAGES_PUSH_TOKEN: ${{ secrets.CHATTEA_PAGES_PUSH_TOKEN }}
steps:
- name: Clone source repository
run: |
set -euo pipefail
rm -rf source site
git clone "$GITEA_BASE_URL/$GITHUB_REPOSITORY.git" source
cd source
git checkout "$GITHUB_SHA"
- name: Build static site
run: |
set -euo pipefail
mkdir -p site
printf '<!doctype html><title>Pages</title><h1>%s</h1>\n' "$GITHUB_REPOSITORY" > site/index.html
- name: Push static site to gh-pages
run: |
set -euo pipefail
test -n "$CHATTEA_PAGES_PUSH_TOKEN"
cd site
git init
git checkout -B "$PAGES_BRANCH"
git config user.name "ChatTea Pages Bot"
git config user.email "chattea-pages@example.invalid"
git add .
git commit -m "pages: publish $GITHUB_SHA"
git remote add origin "$GITEA_BASE_URL/$GITHUB_REPOSITORY.git"
git \
-c "http.$GITEA_BASE_URL/.extraHeader=Authorization: token $CHATTEA_PAGES_PUSH_TOKEN" \
push --force origin "HEAD:$PAGES_BRANCH"
注意:这个 job 可以跑在外部 runner node 上。runner 只需要能访问 Gitea,并拥有写 Pages 分支的权限。
Hitk 实践记录
Hitk 上已经用 ChatTea 自己做过最小实践。这个实践证明了 branch deploy 数据通道可用:
ChatArch/ChatTea main push
-> Gitea Actions
-> runner job
-> push gh-pages
-> Pages Host sync gh-pages
-> Pages service 200
非敏感证据:
repo: ChatArch/ChatTea
workflow: .gitea/workflows/pages-gh-pages.yml
run: 7
job: 13
runner: hitk-pages-branch
run conclusion: success
generated branch: gh-pages
gh-pages commit: c15237a
Pages URL: http://127.0.0.1:3001/ChatArch/ChatTea/
HTTP status: 200
job log 关键行:
Job: publish-gh-pages
HEAD is now at 907a707 ci: publish Pages from gh-pages branch
Switched to a new branch 'gh-pages'
[gh-pages (root-commit) c15237a] pages: publish 907a707...
To http://127.0.0.1:3000/ChatArch/ChatTea.git
* [new branch] HEAD -> gh-pages
当前 Hitk runtime 里还有一个最小 config-driven slice,用来把一条 repo config 同步成 Pages:
pagesctl.py config-set
pagesctl.py list
pagesctl.py sync --repo ChatArch/ChatTea
pagesctl.py status --repo ChatArch/ChatTea
状态输出摘要:
repo: ChatArch/ChatTea
mode: git-branch
source: ChatArch/ChatTea#gh-pages:/
url: http://127.0.0.1:3001/ChatArch/ChatTea/
http_status: 200
last_commit: c15237ad169e...
这个 runner 是 validation runner;它证明 Gitea Actions 可以 push Pages branch。正式部署时 runner 可以移动到 Host 外的资源节点,Pages Host 机制不变。
当前支持状态
已经验证:
- Gitea Actions 可以生成并 push
gh-pages分支; - Pages Host 可以按配置 checkout/sync
gh-pages; - Pages service 可以 serve 同步后的静态站点;
- runner 不需要直接写
pages/sites。
仍需产品化:
- 把 runtime prototype 收进正式
chattea pages ...CLI; - 增加 webhook 或 polling 自动 sync;
- 增加 Git Pages 设置页面;
- 把 validation runner 替换成真正 Host 外部 runner 做一次 heavy-build 验收;
- 设计 preview branch / channel 清理策略;
- 私有仓库的 read-only deploy key / credential reference。