Files
姚章浩 ea6e56b304 feat(release): 文档发布会带上知识库 wiki 快照并支持撤权剥离
发布通道原来只有三条:PostgreSQL 关系行、Elasticsearch 分片、OSS 对象。
WeKnora 的 wiki 图谱是 wiki_pages 一张纯 PG 表(in_links/out_links 存 slug),
所以它不在任何通道里——撤权后生产 wiki 会一直留着已撤权文档的页面与引用。

本次新增第 5 条通道:库级 wiki 快照,挂在现有文档发布包里,检索与 wiki
同一个 release 单元(同一次执行、同一次校验、同一次回滚):

- 导出:ExportWiki 按 knowledge_base_id 全量读 wiki_pages/wiki_folders;
  收敛门禁(库内无解析中/合成中文档)不满足直接失败并给中文原因;
  未启用 wiki 且无 wiki 行的库不参与同步(生产 wiki 保持原样)。
- 撤权:item.Action == "delete" 时在导出端剥离该文档的 wiki 贡献
  (source_refs/chunk_refs 去条目、无引用页面整页不带、摘死链、清 parent_slug、
  无页面引用的空文件夹及其祖先一并去掉),使“源端仍在只停止发布”和
  “源端删除”两种情况收敛到同一结果。
- 导入:库级 replace-by-kb(同一事务内 DELETE by kb 后灌入快照,文件夹先于页面),
  新增 ReplaceWiki 供撤权路径使用(文档行只 disable,wiki 整体替换)。
- 校验:VerifyWiki 用生产 WeKnora wiki API 比对页面 slug 集合,撤权时额外断言
  生产 wiki 不再引用被撤权文档。

驱动与验收:新增 cmd/release-channel-e2e(复用发布 worker 真实代码路径,
支持 publish/withdraw/rollback),scripts/wiki_e2e_test5.sh、
scripts/wiki_e2e_newkb.sh 两套端到端脚本;证据见
docs/WIKI_CO_TRANSPORT_ACCEPTANCE.md。
2026-09-30 10:54:07 +08:00

92 lines
5.3 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# 新建 wiki 知识库 验收脚本:全新 KB(源/目标同 ID)+ 单文档导入 → 发布 → 撤权
# 用法:bash scripts/wiki_e2e_newkb.sh
set -uo pipefail
cd "$(dirname "$0")/.."
REPO=$PWD
SRC=/tmp/wk_curl.cfg
CLONE_FROM=c12ff4d5-dd87-423b-ab0f-af745b9d54a7
NEW_KB=$(python3 -c "import uuid;print(uuid.uuid4())")
DIST=$REPO/dist/release-channel-e2e
echo "新知识库 ID: $NEW_KB"
src_sql() { docker exec WKNFull-postgres psql -U weknora -d weknora_full -At -P pager=off -c "$1"; }
prod_sql() { docker run --rm --network host --env-file /tmp/target_pg.env postgres:16-alpine psql -At -P pager=off -c "$1"; }
echo "### 1. 源端新建 wiki 知识库(克隆同构 KB 行,开 wiki)"
src_sql "create temp table t as select * from knowledge_bases where id='$CLONE_FROM';
update t set id='$NEW_KB', name='wiki-e2e-fresh', created_at=now(), updated_at=now(),
indexing_strategy = indexing_strategy || '{\"wiki_enabled\": true}'::jsonb;
insert into knowledge_bases select * from t;"
echo "源端新库: $(src_sql "select id||' | '||name||' | wiki='||(indexing_strategy->>'wiki_enabled') from knowledge_bases where id='$NEW_KB'")"
echo "### 2. 目标端新建同 ID 库(克隆目标已有的同构 KB 行,开 wiki)"
prod_sql "create temp table t as select * from knowledge_bases where id='$CLONE_FROM';
update t set id='$NEW_KB', name='wiki-e2e-fresh', created_at=now(), updated_at=now(),
indexing_strategy = indexing_strategy || '{\"wiki_enabled\": true}'::jsonb;
insert into knowledge_bases select * from t;"
echo "目标端新库: $(prod_sql "select id||' | '||name||' | wiki='||coalesce(indexing_strategy->>'wiki_enabled','?') from knowledge_bases where id='$NEW_KB'")"
echo "### 3. 生成该库专用发布路由配置(同目标端点,仅换 kb)"
python3 - "$NEW_KB" <<'PY'
import json,sys
kb=sys.argv[1]
cfg=json.load(open('config/release.sim.json'))
cfg['routes'][0]['knowledge_base_id']=kb
json.dump(cfg,open('config/release.wiki-e2e.json','w'),ensure_ascii=False,indent=1)
print('写入 config/release.wiki-e2e.json kb='+kb)
PY
chmod 600 config/release.wiki-e2e.json
echo "### 4. 导入单文档并等 wiki 收敛"
cat > /tmp/wiki_e2e_fresh.md <<'EOF'
# 新建 wiki 知识库验证文档
本知识库用于验证“新建 wiki 知识库后,单文档的检索与 wiki 是否一起搬运到生产”。
知识治理平台把测试端 WeKnora 的文档发布到生产端 WeKnora。
本次验证关注两条轨道:检索(PostgreSQL 关系行 + Elasticsearch 分片)与
wiki 快照(wiki_pages 与 wiki_folders 两张库级表)。
撤销发布时,生产端应当同时去掉检索与 wiki 里这篇文档的贡献。
EOF
DOC=$(curl -s -K $SRC -F "file=@/tmp/wiki_e2e_fresh.md" -F "fileName=wiki-e2e-fresh.md" \
"http://127.0.0.1:8080/api/v1/knowledge-bases/$NEW_KB/knowledge/file" | python3 -c "import sys,json;print(json.load(sys.stdin)['data']['id'])")
echo "文档 ID: $DOC"; printf '%s' "$DOC" > /tmp/wiki_e2e_fresh_id
for i in $(seq 1 30); do
st=$(src_sql "select parse_status||'/'||pending_subtasks_count from knowledges where id='$DOC'")
pages=$(src_sql "select count(*) from wiki_pages where knowledge_base_id='$NEW_KB' and deleted_at is null")
echo " [$i] $st wiki_pages=$pages"
case "$st" in completed/0) break;; esac
sleep 15
done
release() {
docker run --rm --user "$(id -u):$(id -g)" \
--network knowledge-governance-p1_default --network weknora071full-network \
--add-host prod-pg:host-gateway --add-host prod-es:host-gateway --add-host prod-weknora:host-gateway \
-v "$REPO/secrets/release-sim":/run/release-secrets:ro \
-v "$REPO/config/release.wiki-e2e.json":/run/release-sim/release.wiki-e2e.json:ro \
-v knowledge-governance-release-bundles:/var/lib/knowledge-governance/release-bundles \
-v "$DIST":/release-channel-e2e:ro \
--entrypoint /release-channel-e2e knowledge-governance-p1-release-worker:latest \
-config /run/release-sim/release.wiki-e2e.json -knowledge "$1" -mode "$2" -revision "$3" 2>&1 | grep -E "STEP|bundle|wiki (before|after)"
}
echo "### 5. 发布(新建库)"
# 归档根目录必须预先存在且为 0700(NewBundleArchive 只校验不创建)
docker run --rm --user "$(id -u):$(id -g)" -v knowledge-governance-release-bundles:/bundles alpine:3.20 \
sh -c "mkdir -p /bundles/$NEW_KB && chmod 700 /bundles/$NEW_KB"
release "$DOC" publish wiki-e2e-newkb-v1
echo "--- 生产端证据"
prod_sql "select 'knowledges='||count(*) filter (where enable_status='enabled') from knowledges where knowledge_base_id='$NEW_KB'"
prod_sql "select 'wiki_pages='||count(*) filter (where deleted_at is null) from wiki_pages where knowledge_base_id='$NEW_KB'"
echo "### 6. 撤权(新建库)"
release "$DOC" withdraw wiki-e2e-newkb-v2
echo "--- 生产端证据(撤权后)"
prod_sql "select id||' enable='||enable_status from knowledges where knowledge_base_id='$NEW_KB'"
prod_sql "select 'wiki_pages='||count(*)||' refs_still_citing='||count(*) filter (where source_refs::text like '%$DOC%') from wiki_pages where knowledge_base_id='$NEW_KB'"
curl -s "http://172.17.0.1:29200/weknora_test_chunks/_count" -H 'Content-Type: application/json' \
-d "{\"query\":{\"bool\":{\"filter\":[{\"term\":{\"knowledge_id.keyword\":\"$DOC\"}},{\"term\":{\"is_enabled\":true}}]}}}" \
| python3 -c "import sys,json;print('ES enabled chunks:',json.load(sys.stdin).get('count'))"