Skip to Main Content
私有源码、公开部署与模板文档:GitHub Pages Artifact 实践Back to Top

私有源码、公开部署与模板文档:GitHub Pages Artifact 实践

14 minutes

当前这套结构最重要的设计不是“用了几个 Git 平台”,而是把源码所有权、构建过程、 部署产物和公开展示拆成独立层次:

Codeberg 私有仓库           GitHub 公开仓库               公网
博客源码(私有)    ──CI──>  静态部署产物 / Pages   ──CDN──> 博客网站

GitHub 公开仓库             GitHub Actions                 公网
模板源码(公开)    ──CI──>  Doxygen Pages Artifact  ──CDN──> 模板文档

这套方案可以做到:

  • 博客 Markdown、草稿、私有配置放在 Codeberg 私有仓库;
  • 博客最终 HTML 通过 GitHub Pages 公开访问;
  • C++ 模板源码保持公开,Doxygen 由模板仓库自己的 CI 自动更新;
  • 博客不复制模板源码,也不保存模板生成后的 HTML;
  • 博客菜单只保存一个相对路径,由 base_url 动态拼接完整 URL。

Stacks 业务层级与截图

下图中的红框标识了 Stacks 2 的工作区、项目、看板和任务位置:

Stacks 2 工作区、项目、看板和任务层级

Stacks 页面标题可能显示“项目 - Projekt”。本文和持久化模型统一称它为 Project, 避免与看板中的任务 Task 混淆;Task 只表示最小任务卡片。

这张图应从“一个任务向哪里归属”来理解。一个普通任务从自身向上查找,最多会有三个 上级节点:

Workspace 工作区
└── Project 项目(左侧 Documents / File)
    └── Stack 看板
        └── Task 任务

Workspace 工作区

截图左上角的“工作区 - Space”是 Stacks 的工作区根目录,例如 /System/Volumes/Data/data。

在插件中:

  • 一个工作区路径对应 Project.json.library_paths 中的一项;
  • 工作区可以包含多个项目、一个 Inbox、tasks/ 和 people;
  • 路径重载扫描的就是工作区及其下级文件。

Project 项目

截图左下方 Documents/File 区域中的“项目 - Projekt”以及右侧页面标题,都是 Stacks 项目入口。

在文件层:

  • 项目对应工作区中的 .project 文件;
  • 插件索引保存 projectid 和 project_path;
  • 项目可以有多个 backlog 任务和多个 stacks。

项目中未放入具体看板的任务通常进入 backlog。调试时可以把它理解成一个虚拟的 Backlog 看板,但 Stacks 文件里它位于项目顶层的 backlog 数组。

Stack 看板

截图右侧灰色标题栏“0% 看板 - Stack”对应项目下的一个 Stack。

在文件层:

  • Stack 保存在 .project 文件的 stacks[] 数组中;
  • 每个 Stack 有独立 ID、标题和 tasks 数组;
  • 一个项目可以有多个 Stack;
  • 插件 Stack.json 中的 kind=stack 节点用于扁平查询和导航。

Task 任务

截图看板中的“任务 - Task”卡片是 Stack 下的一个任务。

在文件层:

  • 每个任务保存为 tasks/<task-id>.task;
  • .project 文件只保存任务 ID,不保存任务完整正文;
  • 任务正文包含标题、描述、开始日期、截止日期、优先级、完成和计时字段;
  • 从 Stack 下的任务向上查找,三层上级依次是 Stack → Project → Workspace。

Inbox 收件箱

Inbox 是工作区级特殊入口,不属于普通项目,也不经过 Stack。

在文件层:

  • Inbox 任务的 project 固定为 inbox;
  • 任务文件仍然位于当前工作区的 tasks/ 下;
  • 不写入项目 backlog 或 stacks[].tasks;
  • Inbox 任务向上查找只有 Workspace → Inbox 两层。

位置和关系对照

术语截图位置文件或字段上级关系
Workspace 工作区左上角“工作区 - Space”Project.json.library_paths根节点
Project 项目左侧 Documents/File 和页面标题<project-id>.project、Project.json.nodesWorkspace
Stack 看板右侧灰色看板标题.project 的 stacks[]、Stack.json.nodesProject → Workspace
Task 任务看板中的任务卡片tasks/<task-id>.task、backlog 或 stacks[].tasksStack → Project → Workspace
Inbox 收件箱左侧 General → Inbox任务文件 project="inbox"Workspace(特殊两层结构)

这张图也会作为业务术语参考图,被模板仓库的 Doxygen 业务文档引用。

三个仓库分别负责什么

Codeberg 私有博客源码

Codeberg 仓库保存 Zola 的真源:

00BlogZola/
├── content/          Markdown 文章
├── templates/        站点级模板覆盖
├── sass/             样式源码
├── static/           公开静态资源
├── scripts/          加密、搜索索引、部署前处理
├── build-site.sh     正式构建入口
└── zola.toml         全站配置

私有源码不等于私有网站。Markdown 可以是私有资产,但构建出来的 public/ 会发布为 公开静态站点。

需要注意:公开部署产物会暴露其中所有内容。即使源码仓库私有,只要错误地把密钥、 日志、真实用户数据放进 public/,访问者仍可以下载。因此构建产物的白名单和敏感文件 清理必须由 CI 保证。

GitHub 公开部署仓库

GitHub 仓库承担两类职责:

  1. 保存或者接收博客构建产物;
  2. 使用 GitHub Pages 托管静态网站。

采用 GitHub Pages Artifact 时,部署仓库自身不需要保存生成后的 HTML 历史。Actions 可以 checkout Codeberg 源码,构建 public/,然后上传为 Pages artifact。

公开模板仓库

CXX-CMake-template 是独立的公开源码仓库。模板的 Doxygen 由模板仓库自己维护:

CXX-CMake-template/
├── single-exe-classic/
├── single-exe-modules/
├── single-exe-static-modules/
├── ci/build-pages-docs.sh
├── ci/pages-templates.tsv
└── .github/workflows/docs-pages.yml

每次模板、文档构建脚本或说明变化时,模板仓库重新生成全部 Doxygen,再一次性发布到:

https://suchaharcan.github.io/CXX-CMake-template/

博客不需要知道 Doxygen 是怎样构建的,只链接这个公开入口。

GitHub Pages Artifact 的原理

传统 Pages 经常把生成文件推送到 gh-pages 分支,再由 Pages 从该分支发布。 GitHub 当前更推荐的方式:

flowchart LR
  A[源码仓库] --> B[checkout]
  B --> C[安装依赖]
  C --> D[zola build / doxygen]
  D --> E[upload-pages-artifact]
  E --> F[deploy-pages]
  F --> G[GitHub Pages CDN]

1. GitHub Actions 构建静态目录

Zola 或 Doxygen 的输出本质上都是静态文件:

public/
├── index.html
├── style.css
├── script.js
├── 03Share_blog/
└── ...

Pages 不需要数据库或应用服务器,只需要完整、可直接访问的静态文件树。

2. upload-pages-artifact 打包

Action 会把输出目录打成 GitHub Pages 专用 artifact。artifact 中包括:

  • HTML、CSS、JavaScript;
  • 图片、字体和 Doxygen SVG;
  • 根路径下的 .nojekyll;
  • 所有需要公开下载的静态资源。

.nojekyll 用于避免 GitHub Pages 再经过 Jekyll 处理,尤其适合 Doxygen、Zola 或包含 下划线目录的静态站点。

3. deploy-pages 创建 deployment

部署 Job 使用 GitHub OIDC 身份和临时 token,请求 GitHub Pages 发布刚才上传的 artifact。成功后得到官方 page_url。

部署过程不依赖长期保存的仓库写 token,也不需要把 HTML commit 到分支。

4. Pages CDN 原子切换

GitHub Pages 将 artifact 作为完整站点发布。对外表现为一次原子切换:

  • 新版本构建成功,整站切换;
  • 构建或上传失败,旧站点继续在线;
  • 不需要“先生成 HTML,再逐个推送文件”的中间状态。

这也是为什么模板 Pages CI 每次构建全部模板,而不是只上传发生变化的一个子目录。 artifact 是整站快照,如果只上传一个模板,其他模板就会消失。

最小 Actions 结构

博客和模板仓库可以使用同一套 Pages 模型。核心 YAML 结构如下:

permissions:
  contents: read
  pages: write
  id-token: write

jobs:
  build:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v4
      - run: ./build-site.sh
      - uses: actions/configure-pages@v5
      - uses: actions/upload-pages-artifact@v3
        with:
          path: public

  deploy:
    needs: build
    runs-on: ubuntu-24.04
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - id: deployment
        uses: actions/deploy-pages@v4

如果博客源码在 Codeberg,代码检出步骤可以改成公开的 Codeberg 拉取,或者由你在本地/ 中间 CI 构建后把 public/ 传到 GitHub artifact 流程。关键是:GitHub 发布动作只接收 静态目录,不需要博客源码长期存放在 GitHub。

博客源码私有、站点公开的关键边界

私有的是源码访问权限

Codeberg 私有仓库可以阻止别人直接读取:

  • Markdown 原稿;
  • 草稿;
  • 模板和 Sass;
  • 构建脚本;
  • Git 历史。

公开的是最终 HTML

GitHub Pages 是公开静态托管。发布后,浏览器必须能够下载:

  • index.html;
  • CSS、JS、图片;
  • 文章正文;
  • Doxygen HTML;
  • 任何你明确放入 artifact 的文件。

因此不要把这些内容放入 Pages artifact:

  • .env、token、私钥;
  • 构建机日志;
  • 用户数据库;
  • 本地绝对路径索引;
  • 未准备公开的草稿;
  • 加密密钥。

真正的敏感数据应该留在私有服务端,不能靠“HTML 不好找”来保密。

博客菜单如何动态链接模板文档

{ url = "CXX-CMake-template/", name = "模板文档", icon = "../simple/doxygen", base_relative = true }

站点级 templates/partials/sidebar.html 对 base_relative = true 的菜单项使用:

link_url = config.base_url ~ link.url

因此结果是:

config.base_url + "CXX-CMake-template/"

当前结果是:

https://suchaharcan.github.io/CXX-CMake-template/

如果以后把站点迁移到:

base_url = "https://new.example.com/"

菜单会自动变成:

https://new.example.com/CXX-CMake-template/

博客正文和菜单中都不需要硬编码 GitHub Pages 完整域名。

这里没有修改 themes/ametrine-pre 子模块,而是使用 Zola 的模板覆盖机制,在博客 仓库自己的 templates/partials/sidebar.html 中增加 base_relative 规则。普通站内 链接仍然沿用主题原有的 get_url 行为。

Doxygen 图标

主题自带 Simple Icons 图标集,其中包含:

themes/ametrine-pre/icons/simple/doxygen.svg

主题图标组件支持相对图标路径,因此:

icon = "../simple/doxygen"

会解析为 Doxygen 官方图标,而不是继续使用报纸图标。

模板仓库的 Pages 汇总流程

模板仓库的 ci/build-pages-docs.sh 做四件事:

  1. 读取 ci/pages-templates.tsv;
  2. 为每个模板配置独立的 Doxygen 构建目录;
  3. 把 build/docs/doxygen/html 复制到 pages-site/<template>/;
  4. 生成根首页和 .nojekyll。

输出结构:

pages-site/
├── .nojekyll
├── index.html
├── single-exe-classic/
├── single-exe-modules/
└── single-exe-static-modules/

模板仓库工作流只运行 Doxygen,不运行测试和真实数据写操作:

CXX_ENABLE_DOCS=ON
CXX_DOC_CLANGDOC=OFF
CXX_ENABLE_GTEST=OFF

这能让文档发布与业务 CI 解耦。业务测试仍由模板矩阵工作流负责,Pages 工作流只关心 “能否生成一个可公开访问的静态站点”。

除文档外还能链接什么?

GitHub Pages Artifact 不限于 API 文档。只要内容可以表达为静态文件,就能统一发布和 从博客链接。

1. 项目主页和作品集

每个项目可以有独立入口:

/CXX-CMake-template/
/Omni-Stacks/
/AlfredWorkflows/

页面可以包含项目截图、功能对比、安装引导、更新日志和演示视频。

2. Release 页面和安装说明

Pages 可以承载:

  • 版本说明;
  • 安装教程;
  • 系统兼容矩阵;
  • SHA-256 校验值;
  • 链接到 GitHub Releases 的下载地址。

真正的二进制安装包建议放在 GitHub Releases;Pages 可以承担展示和引导,不必把 pages 当大文件中转站。

3. WebAssembly 演示

C++ 项目可以编译为 WASM,并将静态 HTML 作为 artifact 发布:

/demo/parser/
/demo/renderer/
/demo/task-core/

博客文章可以直接链接到可交互演示,而不是只放截图。

4. 覆盖率、测试和性能报告

CI 可以生成静态报告:

  • HTML coverage;
  • Doxygen call/caller graph;
  • benchmark 图表;
  • CTest 结果摘要;
  • clang-tidy 报告;
  • 二进制大小对比。

报告同样通过一个 Pages artifact 发布,博客链接到对应目录即可。

5. API Schema 和开发者门户

静态 Pages 可以发布:

  • OpenAPI JSON/YAML;
  • Swagger UI 静态页面;
  • JSON Schema;
  • protobuf 文档;
  • CLI 参数参考;
  • SDK 示例首页。

6. 演示文稿和课程页面

Reveal.js、Marp 导出的 HTML、教程目录、学习路线和实验说明都可以部署到 Pages。

7. RSS、JSON Feed 和机器可读索引

除了给人看的 HTML,还可以发布:

/feed.json
/index.json
/releases.json
/search-index.json

博客或其他程序可以消费这些公开静态数据。

8. PWA 和离线资源

Pages 可以托管 manifest.webmanifest、Service Worker、图标和离线缓存资源。需要注意 跨域 Service Worker 作用域限制;独立项目通常应部署在独立路径或域名下。

Artifact 与 Release 的区别

两者都保存构建产物,但用途不同:

能力Pages ArtifactGitHub Release
主要用途部署静态网站发布版本下载
是否有公网站点是否,需下载
适合 HTML是可以,但不适合浏览
适合大二进制不建议是
长期版本保存deployment 历史为主明确版本资产
CDN 浏览Pages CDNRelease 下载

常见组合是:

GitHub Release:保存安装包、校验文件、发布说明
GitHub Pages:展示安装步骤、文档、演示和下载入口
博客:链接到稳定的 Pages 页面

常见问题

只部署变更模板可以吗?

不建议直接用 deploy-pages 只上传一个子目录。它会把 artifact 当成整站。

可选方案:

  1. 每次构建全部模板,整站发布,最稳定;
  2. 维护一个额外的聚合仓库;
  3. 改用 gh-pages 分支并配合 keep_files,但要处理并发覆盖。

当前模板数量有限,采用“全部构建、整站发布”最简单。

私有源码会不会让 Pages 也不可见?

不会。Pages 只关心最终 artifact 是否可公开访问。源码权限和站点发布权限是两层配置。

但如果 Pages 站点被设置为私有访问,就需要符合 GitHub 套餐和组织策略;当前站点使用 公开 Pages。

base_url 改变后模板链接会失效吗?

不会。菜单使用相对路径,主题通过 get_url 拼接当前 base_url。

不要重新写回 @/CXX-CMake-template/。@/ 表示博客内部的 content 页面,而不是外部 GitHub Pages 路径。

能否在博客内直接嵌入 Doxygen?

可以直接超链接。使用 iframe 需要检查 Pages 和博客两侧的 CSP;Doxygen 内部链接很多, 独立打开通常体验更稳定。

文档仓库是否要包含在博客仓库?

不需要。模板源码、Doxygen 构建脚本和 Pages 工作流都保留在模板仓库。博客只保存链接。

最终结构

Codeberg 私有:
  博客 Markdown / Zola / Sass / 构建脚本
        │
        └── CI 构建静态 public/

GitHub 公开:
  博客 GitHub Pages Artifact
        └── https://suchaharcan.github.io/

GitHub 公开:
  CXX-CMake-template 源码
        │
        └── Doxygen Pages Artifact
              └── https://suchaharcan.github.io/CXX-CMake-template/

博客菜单:
  url = "CXX-CMake-template/"
  + config.base_url
  + ../simple/doxygen 图标

这套方案的核心价值是:

私有源码负责“如何构建”,公开部署负责“如何展示”,模板仓库负责“维护自己的文档生命周期”。

博客、Pages 部署和模板文档可以独立升级,不需要互相复制源码,也不需要每次迁移时重新 写死域名。