目标很朴素:一次 git push,三端全部自动更新,国内用户走 Gitee、海外用户走 GitHub / Cloudflare,谁都能打开官网。这篇记录完整的落地过程与踩过的坑。

整体架构

端地址职责
主域(GitHub Pages)gjsvip.github.io官网唯一主入口,国内外可达,干净无路径
Cloudflare Workersdujiecode.dujie-nb.workers.dev完整版:API + 静态资源,边缘直服,不做跳转
Cloudflare Pagesdujiecode.pages.dev静态镜像 + 备用入口
Gitee 镜像dujie_nb_admin.gitee.io/dujiecode/国内加速镜像
代码仓库GitHub + Gitee 双库双向可访问,Issue 集中

工作流拆分

deploy.yml 负责部署,mirror-to-gitee.yml 负责镜像,互不阻塞:

push to main
 ├─ deploy.yml
 │   ├─ build & test(tsc + vitest 64 项)
 │   ├─ D1 幂等初始化
 │   ├─ wrangler deploy → Cloudflare Workers
 │   ├─ wrangler pages deploy → Cloudflare Pages
 │   └─ peaceiris/actions-gh-pages → gjsvip.github.io 主域
 └─ mirror-to-gitee.yml
     └─ git push --mirror → Gitee(main + tags)

踩坑记录

1. peaceiris 跨仓库部署必须用 personal_token

最初给 github_token 传入 PAT,action 直接报错「does not support to push to an external repository」。读源码发现:github_token 输入仅用于同仓库,跨 external_repository 必须用 personal_token 或 deploy_key。

2. Gitee 拒绝 --mirror 推送

deny updating a hidden ref——镜像前先 git remote remove origin,只推 refs/heads/* 与 tags 即可。

3. Job 级 if: secrets.X 导致零任务静默失败

密钥缺失时整个 job 不生成任务、不报错。改为步骤内 shell 判断,缺失时明确打印提示。

4. 主域与博客的目录冲突

官网主域要「不带 /」的干净地址,博客又要在同一域名下。最终方案:官网占根路径,博客独立仓库 gjsvip/blog 以项目页形式挂载到 /blog/,两套 Pages 构建互不覆盖。

国内外可达性

国内网络对 workers.dev 与 pages.dev 的 SNI 时有干扰,因此:

  • 主域 gjsvip.github.io 由 GitHub Pages 承载,国内直连稳定,作为唯一主入口
  • Gitee Pages 镜像作为国内加速备份
  • Cloudflare Workers 侧移除地域跳转,任何访客直达服务,国内访客仅在落地页顶部看到镜像推荐条(软提示,不强制)

收录(SEO)

  • robots.txt 使用相对路径 Sitemap 指令,各托管端各自解析
  • sitemap.xml 覆盖主域各页、博客三篇文章、workers.dev 与 pages.dev
  • 通过 IndexNow 协议主动推送收录(key 托管于主域根路径)

小结

三端同步的核心是「职责分离」:GitHub Actions 管部署,Gitee 只做代码与 Pages 镜像,Cloudflare 管边缘运行时。一次推送,全链路生效——剩下的,交给自动化。