ea6e56b304
发布通道原来只有三条: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。
92 lines
5.3 KiB
Bash
92 lines
5.3 KiB
Bash
#!/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'))" |