Negentropy Wiki 独立部署与内容同步指引
适用对象:负责把
negentropy-wiki部署到远程环境、并把主站 Catalog 内容发布上去的工程师 / 运营。一句话:wiki 是纯静态站点(
output: export),运行时无 Node/后端/数据库;内容来自仓库内content/静态内容包,任何已发布内容都必须先「导出」到content/、再「重建」才能上线。相关文档:Wiki 运维指引 · Wiki 知识发布(UI 操作) · Docker 发布流水线 · Wiki README
#1. 架构前提
negentropy-wiki 在 #931 完成纯静态解耦:
- 运行时零依赖:无 Node 服务端、无后端 API 调用、无数据库访问。产物是纯静态 HTML/CSS/JS(
apps/negentropy-wiki/out/),可由任意静态托管(nginx / static-web-server / CDN / GitHub Pages)提供服务。 - 内容来自「内容根」:构建期
next build读取内容根的静态内容包烘焙为静态 HTML;运行时不再回源任何后端。内容根三级解析(content-source.ts的resolveContentDir):WIKI_CONTENT_DIR(显式覆盖)>content/(真实导出落点,整体 gitignored,存在index.json时采用)>content.fixture/(仓库内开发种子 fixture,入 git,构建兜底)。- 真实导出内容环境相关、不入 git;fixture 与真实物理隔离——导出工具覆盖式
_reset只动content/,不波及content.fixture/。schema 见content.fixture/README.md。
- 关键不变量:内容更新 = 重建。ISR 已退役——不存在「运行时 5 分钟自动刷新」。要让新发布的内容上线,必须重新导出
content/并重建站点(Docker 镜像或静态产物)。
#1.1 数据流(发布 → 上线)
边界:导出动作由主站职责承担(合法持有 DB 访问),产出静态文件;wiki 端构建/运行时不直接或间接依赖主站数据库。内容包即「发布边界」。
#2. 独立部署 negentropy-wiki
#2.1 前置:构建产物形态
| 产物 | 路径 | 说明 |
|---|---|---|
| 静态产物 | apps/negentropy-wiki/out/ | next build(output: export)输出,含全部预渲染 HTML |
| 搜索索引 | out/pagefind/ | postbuild(pagefind --site out)生成,必须随 out/ 一起部署,否则全文搜索失效 |
| 真实内容(构建输入) | apps/negentropy-wiki/content/ | 真实导出落点,整体 gitignored;由 sync-wiki-content.sh / CI 写入 |
| fixture(构建兜底) | apps/negentropy-wiki/content.fixture/ | 开发种子,入 git;content/ 缺失时构建自动回退(见 §1 内容根解析) |
内容根三级解析(
content-source.ts的resolveContentDir):WIKI_CONTENT_DIR>content/(存在index.json时)>content.fixture/。即有真实导出用真实、否则回退 fixture, 二者物理隔离——导出工具覆盖式_reset只动content/,不波及 fixture。
构建命令(仓库根目录):
pnpm install --frozen-lockfile
pnpm --filter negentropy-wiki build # next build(export)+ postbuild pagefind
#2.2 方式 A:Docker 全链路(推荐)
独立 Dockerfile:docker/wiki/Dockerfile(多阶段:deps → builder 产出 out/ → runtime 用 static-web-server 托管,无 Node 运行时)。
① 本地构建镜像
# 仓库根目录;<tag> 例如 latest / 日期 / commit
docker build -f docker/wiki/Dockerfile -t threefishai/negentropy-wiki:<tag> .
② 运行(单容器)
docker run -d --name negentropy-wiki \
-p 8080:80 --restart unless-stopped \
threefishai/negentropy-wiki:<tag>
# 访问 http://<host>:8080/
③ 独立 compose(验证独立性,无 backend/ui/postgres)
docker compose -f docker-compose.wiki.yml up -d --build
# 见 docker-compose.wiki.yml:仅 wiki 服务,端口 3092:80
④ 完整栈:根 docker-compose.yml 中 wiki 服务已无 depends_on,可与 backend/ui 同栈部署,也可独立拉起。
配置要点:
| 项 | 值 |
|---|---|
| 容器端口 | 80(static-web-server),宿主机任意(如 8080/3092) |
| 环境变量 | 无需任何 env(纯静态) |
| 健康检查 | HTTP GET / 返回 200 |
| 重启策略 | unless-stopped |
镜像发布通道(CI):reusable-negentropy-docker.yml 在 release 流水线构建并推送多架构镜像到 docker.io/threefishai/negentropy-wiki。详见 Docker 发布流水线。发布后任意远程主机可直接 docker pull threefishai/negentropy-wiki:<tag>。
#2.3 方式 B:通用静态托管(直接托管 out/)
不想用 Docker 时,把 out/ 作为纯静态资源托管到任意静态服务器/CDN。
① 构建产物:pnpm --filter negentropy-wiki build → apps/negentropy-wiki/out/。
② nginx 示例(trailingSlash:true 已生成目录式 HTML,无需 SPA fallback):
server {
listen 80;
server_name wiki.example.com;
root /var/www/negentropy-wiki/out; # 指向 out/
index index.html; # 显式声明:使 $uri/ 能解析到目录 index.html
# 目录式 HTML:/pub/entry/ → /pub/entry/index.html。
# try_files 的 $uri/ 分支使「无尾斜杠」请求(如 /pub/entry)也能内部命中目录 index,
# 故无论链接是否带尾斜杠均可访问(静态导出不产出 .html,无需 $uri.html)。
location / {
try_files $uri $uri/ =404;
# 可选 SEO 规范化:把无尾斜杠的目录型 URL 301 到尾斜杠(单一规范 URL)。
# 启用后上述 try_files 的无尾斜杠兜底可移除。
# rewrite ^([^.]*[^/])$ $1/ permanent;
}
# pagefind 搜索索引(随 out/ 一起部署)
location /pagefind/ { add_header Cache-Control "public, max-age=3600"; }
# 静态资源长缓存
location /_next/static/ { add_header Cache-Control "public, max-age=31536000, immutable"; }
gzip on;
gzip_types text/css application/javascript application/json image/svg+xml;
}
③ CDN / 对象存储(Cloudflare / CloudFront / OSS):上传 out/ 全量(务必包含 out/pagefind/)。设置默认根对象为 index.html。
④ GitHub Pages:把 out/ 推到 gh-pages 分支(或用 peaceiris/actions-gh-pages)。注意 base path:若部署在子路径(user.github.io/repo/),需在 next.config.ts 配 basePath 后重新构建。
#2.4 反向代理 / HTTPS(可选)
前置 Caddy / Traefik / nginx 终止 TLS,反代到 wiki 容器的 80:
server {
listen 443 ssl http2;
server_name wiki.example.com;
# ... ssl 证书 ...
location / { proxy_pass http://negentropy-wiki:80; }
}
#3. 内容同步与发布
#3.1 原理
wiki 只读 content/。任何已发布内容必须经两步才能上线:
- 导出:从主站 DB 把已发布 publication 序列化为
content/静态内容包(WikiExportService+export_wiki_content.py)。 - 重建:
next build把content/烘焙进out/(并据此构建 Docker 镜像或上传静态产物)。
这两步分别由
sync-wiki-content.sh(本地)/wiki-content-export.yml(CI)+ 构建/部署流水线承担。
#3.2 路径选型矩阵
| 场景 | 推荐路径 | 触发 | 详见 |
|---|---|---|---|
| 本地主站 → 远程 wiki(一次性/快照) | 手动快照部署 | 人工 | §3.3 |
| 主站 DB 在 CI 可达 + publish 自动化 | CI 自动 | publish webhook | §3.4 |
| 本地开发联调(本地 wiki) | 本地刷新 | cli.sh restart | §3.5 / README |
#3.3 路径 A(重点):手动快照部署 —— 本地主站 → 远程 wiki
最适合「我在本地主站编排好 Catalog 并发布,想把这份内容部署到一台远程 wiki」的场景。本质:把本地 DB 的已发布内容烘焙进 Docker 镜像,推到 registry,远程拉取运行。
前置:本地已能跑通主站(postgres + backend),且至少有一个 status=published 的 Wiki publication。
Step 1 — 本地发布:浏览器打开主站 /knowledge/wiki,编排 Catalog 树后点「同步并发布」(UI 操作详见 Wiki 知识发布)。发布成功后内容写入本地 DB。
Step 2 — 导出本地 DB → content/:
# 仓库根目录;自动连本地 DB(NE_DB_URL 或默认 localhost:5432/negentropy),
# 把已发布内容导出到 apps/negentropy-wiki/content/
./scripts/sync-wiki-content.sh
等价手动命令(便于排错):
cd apps/negentropy
NE_SVC_ARTIFACT_BACKEND=inmemory uv run python scripts/export_wiki_content.py \
--out ../negentropy-wiki/content
校验导出结果:
cat apps/negentropy-wiki/content/publications.json | python3 -m json.tool
# 期望:items 含你刚发布的 publication,entries_count > 0
sync-wiki-content.sh自动把NE_SVC_ARTIFACT_BACKEND置inmemory,以容忍用户级~/.negentropy/config.yaml中可能与本分支枚举(inmemory|gcs)不符的取值。本地生成的content/是环境相关数据,请勿提交(仓库保留 fixture 种子)。
Step 3 — 构建含内容的镜像(content/ 在构建期烘焙进 out/):
docker build -f docker/wiki/Dockerfile -t threefishai/negentropy-wiki:<tag> .
Step 4 — 推送 registry:
docker login # 首次需登录 Docker Hub(或私有 registry)
docker push threefishai/negentropy-wiki:<tag>
Step 5 — 远程拉取运行(在远程主机):
docker pull threefishai/negentropy-wiki:<tag>
docker run -d --name negentropy-wiki -p 80:80 --restart unless-stopped \
threefishai/negentropy-wiki:<tag>
# 或:docker compose -f docker-compose.wiki.yml up -d (需把 NEGENTROPY_IMAGE_TAG 设为 <tag>)
Step 6 — 验证:
curl -s https://<remote>/ | grep -o "<publication 名称>"
curl -s -o /dev/null -w "%{http_code}\n" https://<remote>/<pubSlug>/ # 期望 200
变体:静态托管(不走 Docker):Step 3 换成 pnpm --filter negentropy-wiki build,把 apps/negentropy-wiki/out/ 上传到远程 nginx/CDN(见 §2.3)。
#3.4 路径 B:CI 自动(publish 触发)
主站 DB 在 CI 可达时,可配置「publish 自动导出 + 提交 + 重建」闭环。
① 配置后端(WikiRedeploySettings env):
| 环境变量 | 值 |
|---|---|
NE_KNOWLEDGE_WIKI_REDEPLOY__URL | https://api.github.com/repos/<owner>/<repo>/dispatches |
NE_KNOWLEDGE_WIKI_REDEPLOY__TOKEN | PAT(需 repo + workflow 权限) |
NE_KNOWLEDGE_WIKI_REDEPLOY__EVENT_TYPE | wiki_content_export |
② 配置仓库 Secrets(GitHub Settings → Secrets):NE_DB_URL(主站 DB 只读连接串)、WIKI_CONTENT_BOT_TOKEN(有 push 权限的 PAT)。
③ 流程:主站 publish → 后端 trigger_wiki_redeploy(WARN-only,不阻塞发布)→ GitHub repository_dispatch → wiki-content-export.yml 导出 content/ 并提交 → push 触发 wiki 重建校验。
⚠️ 重要限制(如实说明):
content/提交后,wiki 镜像的重建当前并非全自动——需要 release 流水线发版,或手动 dispatchreusable-negentropy-docker.yml重建negentropy-wiki镜像(workflow 注释已标注「按需扩展」)。即:自动闭环目前覆盖到「内容进仓库」,镜像重发布仍需一次手动/发版动作。
#3.5 路径 C:本地开发同步
本地开发联调(本地 wiki + 本地主站),cli.sh restart 已内置「导出 → 重建」:
./scripts/cli.sh restart # 自动 sync-wiki-content.sh + pnpm build
在
/knowledge/wiki选择**「测试环境」**目标「发布」时,后端会 fire-and-forget spawnscripts/build-wiki-local.sh(复用sync-wiki-content.sh+next build), 等价于上述本地刷新的「导出 + 重建」步骤——无需手动cli.sh restart即可在:3092看到新内容。
本路径(
sync-wiki-content.sh)默认bake_assets=true:图片烘焙为content/assets/自包含静态文件(与 §3.5b Pages 路径一致),经 wikiprebuild/predev钩子(sync-assets.mjs)同步进public/assets/→out/assets/,pnpm dev/serve out均可直接显示,无需主站运行时供图。
#3.5b 路径 D:发布到 GitHub Pages(本地主站 → threefish-ai.github.io)
适用:主站纯本地(DB 不公网暴露),希望在 /knowledge/wiki 选择**「生产环境」目标「发布」后自动**把站点上线到独立的 GitHub Pages 仓库(如 threefish-ai.github.io)。
为什么不是云端 CI:wiki-content-export.yml(§3.4)在 GitHub 云端 runner 跑,需 NE_DB_URL 连主站 DB——本地 DB 云端连不上。故由后端在 publish 后后台 spawn 本地脚本完成「导出 → 构建 → 推 Pages」,全程在本地。
入口:
publish(target=production)→ spawnpublish-wiki-pages.sh(与「测试环境」目标的build-wiki-local.sh共享「导出 + 构建」前两步,生产目标额外 rsync + git push)。 显式目标即授权——不再依赖ENABLED开关(该开关保留为遗留自动发布兼容)。
图片自包含:本路径用 bake_assets=true 把图片字节烘焙进 content/assets/,经 wiki prebuild 钩子(sync-assets.mjs)同步到 public/assets/、next build 复制进 out/assets/(见 §4.4),站点零主站运行时依赖——即使主站不公网可达,公网 Pages 上图片照常显示。
一次性配置:
-
目标仓库 Pages:
threefish-ai.github.io→ Settings → Pages → Source 选Deploy from a branch,分支master/ root(user/org pages 根域默认分支即master,无需basePath)。 -
推送凭证:本地对该仓库有 push 权限。脚本支持三种凭证(优先级从高到低):①
WIKI_PAGES_TOKEN环境变量(GitHub PAT);②gh auth token(装了 gh CLI 且已登录则自动取);③ SSH key(WIKI_PAGES_REPO用git@URL 时)。零配置最快:gh auth login后直接选「生产环境」发布即可。仅当需覆盖默认仓库/分支或禁用
gh auth回退时,才需下列 env:hljs bashNE_KNOWLEDGE_WIKI_PAGES_PUBLISH__REPO=https://github.com/ThreeFish-AI/threefish-ai.github.io.git NE_KNOWLEDGE_WIKI_PAGES_PUBLISH__BRANCH=master # 可选:HTTPS 推送 token(缺省则脚本回退 gh auth token / SSH) NE_KNOWLEDGE_WIKI_PAGES_PUBLISH__TOKEN=ghp_xxx # 遗留:True 时任意 publish 即推 Pages(新流程已由显式 target 触发,通常无需开启) # NE_KNOWLEDGE_WIKI_PAGES_PUBLISH__ENABLED=true
之后:在主站点选择**「生产环境」**目标 → 点 「发布」(destructive 二次确认)→ 后端后台跑 scripts/publish-wiki-pages.sh →(导出含图片 → 构建 → 备份目标分支 → rsync out/ + push)→ 数十秒后 https://threefish-ai.github.io/ 更新。spawn 为 fire-and-forget,不阻塞 publish 接口(失败仅后端 WARN 日志)。
手动 fallback / 首次验证:
# 手动跑(用 gh CLI 的 token 推送 master)
WIKI_PAGES_TOKEN=$(gh auth token) ./scripts/publish-wiki-pages.sh
# 演练(导出+构建+同步到 .temp 工作副本,不 commit/push)
WIKI_PAGES_DRY_RUN=1 ./scripts/publish-wiki-pages.sh
目标仓库已被占用时(重要实操):若 Pages 目标分支原本是另一个站点(如 Docusaurus),全量覆盖会清掉它。脚本默认 WIKI_PAGES_BACKUP=1,覆盖前自动把目标分支当前内容推到 <branch>-archive-<时间戳> 分支兜底(可随时恢复)。确认无需保留旧站后再发布;如需保留双站,改用 §2.3 的子路径/独立仓库方案。
关键细节:
- 脚本自动写
.nojekyll(GitHub Pages 即使build_type: legacy(Jekyll)也读它;否则_next/等下划线目录被忽略,致 JS/CSS 404)。 out/用rsync --delete全量覆盖目标分支(保留.git与CNAME),旧站点文件一并清理。- 幂等:buildId 绑定内容版本(见 §4.5),内容未变则跳过 commit,避免 noise 历史。
- 并发安全:多次「同步并发布」触发的 spawn 经
.temp/wiki-pages-publish.lock(mkdir 原子锁 + PID 陈旧检测,portable,macOS/Linux 通用)串行化,避免目标 Pages 仓库 push 竞争(后写覆盖 / 分支冲突 / 备份分支污染);锁被持有时跳过本次,下次 publish 会带上最新内容。 - 全流程日志:导出 / build / push 各步与失败原因落盘
.temp/wiki-pages-publish.log(后端 spawn 与手动运行同一入口;手动运行同时输出终端)。发布未生效时首选查此日志。 - token 安全:token 仅注入 git remote URL(工作副本在 gitignored
.temp/),上述日志不记录 token。
#3.6 内容刷新语义
- 纯静态 = 必须重建才更新:不存在运行时 ISR。主站 publish 后,内容要上线必须走 §3.3 / §3.4 的「导出 + 重建」。
- 延迟:手动快照 = 人工触发(即时);CI 自动 = 一次 CI 构建(数分钟)。
- 回退:重新部署旧 tag 镜像 / 旧
out/快照即可(静态产物天然可回滚)。
#4. 配置参考
#4.1 后端 WikiRedeploySettings(仅 CI 自动路径需要)
| Env | 默认 | 说明 |
|---|---|---|
NE_KNOWLEDGE_WIKI_REDEPLOY__URL | None | 触发端点;未配置则跳过(等价被动,等下一次手动/定时重建) |
NE_KNOWLEDGE_WIKI_REDEPLOY__TOKEN | None | GitHub dispatch 鉴权 PAT(Bearer) |
NE_KNOWLEDGE_WIKI_REDEPLOY__EVENT_TYPE | wiki_content_export | dispatch 事件类型 |
NE_KNOWLEDGE_WIKI_REDEPLOY__SECRET | None | 可选 HMAC 签名(自建 webhook 鉴权) |
#4.2 CI Secrets(仅 CI 自动路径需要)
| Secret | 用途 |
|---|---|
NE_DB_URL | 导出时只读访问主站 DB |
WIKI_CONTENT_BOT_TOKEN | 提交 content/ 的 push 权限 PAT |
NE_KNOWLEDGE_WIKI_EXPORT_ASSET_BASE_URL | 可选;主站可达前缀,把图片重写为 {base}/knowledge/wiki/documents/{doc}/assets/{file} 绝对 URL(wiki 与主站分域部署时配置;同源反代可省略) |
#4.3 通用注意
artifact_backend:#932(GCS 退役)后枚举仅inmemory|postgres,默认postgres(制品持久化到adk_artifacts表)。若~/.negentropy/config.yaml仍保留services.artifact_backend: gcs等失效值,请改为合法值,否则主站启动校验报错。- 资产/图片:#932 起 GCS 全量退役、资产转 PostgreSQL bytea。两种处理见 §4.4。
#4.4 图片烘焙(bake_assets)
markdown 内的图片(/api/documents/{doc}/assets/{file})有两条互斥处理路径,由 NE_KNOWLEDGE_WIKI_EXPORT__BAKE_ASSETS 控制:
| 模式 | 配置 | 行为 | 适用 |
|---|---|---|---|
| 烘焙(自包含) | BAKE_ASSETS=true | 导出期下载图片字节写入 content/assets/{doc}/{file},markdown 改相对路径 /assets/{doc}/{file};由 wiki prebuild 钩子 sync-assets.mjs 同步到 public/assets/,next build 再复制进 out/assets/ | 公网 Pages / 本地主站——零主站运行时依赖,主站不可达也能显示图片 |
| URL 重写 | BAKE_ASSETS=false(默认)+ ASSET_BASE_URL | 重写为 {base}/knowledge/wiki/documents/{doc}/assets/{file},运行期由主站 bytea 端点供图 | 分域反代部署——要求主站对访客可达 |
publish-wiki-pages.sh(§3.5b)与sync-wiki-content.sh(§3.5 路径 C)均默认置BAKE_ASSETS=true(本地自包含烘焙;可显式置false走 URL 重写)。烘焙图片进静态产物由 wiki 端prebuild/predev钩子(sync-assets.mjs)统一完成,无需改动 publish/sync 脚本。烘焙失败的单张图仅 WARN 并保留原引用,不阻断正文导出。
#4.5 发布幂等(buildId)
next.config.ts 的 generateBuildId 把 Next buildId 绑定到内容包各 publication 的 (slug,id,version) 哈希(剔除导出时间戳)。内容未变(version 不递增)则 buildId 不变 → publish-wiki-pages.sh 的 rsync 无差异 → 跳过 commit,避免每次构建因随机 buildId 产生 noise 历史。
#4.6 本地 Pages 自动发布(WikiPagesPublishSettings)
| Env | 默认 | 说明 |
|---|---|---|
NE_KNOWLEDGE_WIKI_PAGES_PUBLISH__ENABLED | false | True 时 publish 后后台 spawn 脚本自动发布到 Pages |
NE_KNOWLEDGE_WIKI_PAGES_PUBLISH__SCRIPT | scripts/publish-wiki-pages.sh | 发布脚本(相对仓库根) |
NE_KNOWLEDGE_WIKI_PAGES_PUBLISH__REPO | None | 目标 Pages 仓库(透传脚本 WIKI_PAGES_REPO),SSH 或 HTTPS URL |
NE_KNOWLEDGE_WIKI_PAGES_PUBLISH__BRANCH | master | 目标分支(透传脚本 WIKI_PAGES_BRANCH;user/org pages 默认 master) |
NE_KNOWLEDGE_WIKI_PAGES_PUBLISH__TOKEN | None | 可选 GitHub PAT(透传脚本 WIKI_PAGES_TOKEN);HTTPS 推送免 SSH key,缺省回退 gh auth token/SSH |
与
WikiRedeploySettings(webhook → 云端 CI)正交:本地纯本地主站用本块、分域/云端用 webhook。默认关闭,不影响现有流程。 脚本另支持WIKI_PAGES_BACKUP=1(默认)——覆盖前把目标分支备份为<branch>-archive-<ts>。
#5. 验证与故障排除
#5.1 独立性铁证
# 完全断网运行,仍能渲染内容(证明零运行时后端/DB 依赖)
docker run --rm -p 8080:80 --network none threefishai/negentropy-wiki:<tag> &
curl -s http://localhost:8080/ # 200 + 含真实文章
#5.2 内容为空排查
| 现象 | 排查 |
|---|---|
| 首页「暂无已发布的 Wiki」 | content/publications.json 是否有 status=published 项;未导出则跑 §3.3 Step 2 |
| 详情页 404 | 镜像内是否烘焙:docker run --rm <img> ls /public/<pubSlug>/ 应有 index.html |
| 搜索框无效 | out/pagefind/ 是否一起部署(Docker 镜像已含;静态托管需手动上传) |
#5.3 其他
- 端口/反代 404:
trailingSlash:true生成目录式 HTML(/pub/entry/),静态层try_files $uri $uri/ =404+index index.html即可,无尾斜杠请求亦能命中目录 index,无需 SPA fallback。 - 图片不显示:markdown 图片重写为主站资产端点
/knowledge/wiki/documents/{doc}/assets/{file}(PostgreSQL bytea 流式提供,#935)。wiki 与主站分域部署时须配置NE_KNOWLEDGE_WIKI_EXPORT__ASSET_BASE_URL为主站可达前缀并确保网络可达;同源反代可省略。 - 导出报
artifact_backend校验错:用sync-wiki-content.sh(已置 inmemory)或手动NE_SVC_ARTIFACT_BACKEND=inmemory。
编辑本文档时,同步检查
ops.md§5/§6 与publishing.md§8 的指针是否一致(本文档为部署 + 内容同步的单一事实源)。