#!/usr/bin/env bash # 共用模块必须逐字节相同 —— 见 docs/PLUGIN-CONTRACT.md 第六节。 # # 一侧改了另一侧没改,几个平台的行为就会悄悄分叉:同一封邮件在 opencode 那边 # 标了已读、在 DSH 那边没标,而两处代码看起来都"对"。 # # 比对一律以 opencode 为基准逐个对比,而不是两两对比:后者在三方都不同时 # 会打出三条互相矛盾的差异,读的人无从判断谁是对的。 # # # 为什么每个 peer 的清单不同 # # 四个插件共用的是**协议无关**的那几个模块(地址解析、收件箱渲染、附件 id 归一…), # 但各自用到的子集不同: # # - pi / dsh / opencode 是邮件驱动的**桥**,需要会话快照、中继去重、SSE 客户端等; # - zcode-mail-bridge 目前是 **MCP 工具服务器**(模型侧的工具面), # 不订阅 SSE、不管会话生命周期,所以只需要那 5 个协议无关模块。 # # 用「对等清单」会逼它拷一份用不到的 relay/session 模块进来 —— 那些文件 # 永远不会被执行,却要跟着一起同步,反而让「哪些是真的共用」变得看不清。 # 于是改成显式列举:**声明共用就必须逐字节相同**,声明之外的模块不检查 # (因为根本没拷过来,结构上就不可能分叉)。 set -euo pipefail BASE=plugins/opencode-mail-bridge PEERS=(plugins/dsh-mail-bridge plugins/pi-mail-bridge plugins/zcode-mail-bridge) ALL_LIBS="relay-dedup relay-policy relay-key permission-mode bounded inbox-format session-snapshot workspace model-scope catchup addressing discovery rename-proposal permission-grants adopt sse-client user-question attachment-ids" ALL_TESTS="relay-policy relay-key permission-mode bounded inbox-format session-snapshot workspace model-scope catchup addressing discovery rename-proposal permission-grants adopt sse-client user-question attachment-ids" # MCP 工具服务器 + 授权钩子 + 邮件驱动用到的子集(实现与测试都同步)。 # # 钩子与驱动这一层额外需要: # permission-mode / relay-key / permission-grants / sse-client # —— 档位语义、relay_key 截断、决策判定、SSE 帧解析 # catchup / relay-policy / workspace / relay-dedup # (relay-dedup 在基准那边也没有独立测试,故只比对实现) # —— 停机补投、主动发信去重、回信策略、工作目录解析 # # 这些都不能各平台自己写一份:「同意」的含义、谁该收到回信、同一封邮件 # 会不会被转两次 —— 分叉的后果都是只在单平台复现的行为差异。 MCP_LIBS="addressing inbox-format bounded discovery attachment-ids permission-mode relay-key permission-grants sse-client catchup relay-dedup relay-policy workspace" MCP_TESTS="addressing inbox-format bounded discovery attachment-ids permission-mode relay-key permission-grants sse-client catchup relay-policy workspace" # ── 判据:用 cmp,不用 diff ───────────────────────────────────────── # # 本机 PATH 上的 `diff` **不是 GNU diff**,而是鸿蒙 SDK 工具链里的 # /opt/huawei/harmonyos/ohos-sdk/linux/toolchains/diff。它不认 `-q`, # 却对**内容不同的文件照样返回 0** —— 于是 `if ! diff -q ... >/dev/null` # 恒为假,整支脚本变成永真输出(本检查器曾长期如此,实测才发现)。 # cmp 的语义明确:相同 0、不同 1。 same() { cmp -s "$1" "$2"; } # 判据本身必须先被验证。否则「比较永远说相同」与「真的都相同」 # 在结果上完全一样,而这正是上面那个坑的形状。 selfcheck() { local a b a=$(mktemp) && b=$(mktemp) printf 'x\n' >"$a" printf 'y\n' >"$b" if same "$a" "$b"; then echo "检查器自检失败:比较函数无法发现差异(别用 PATH 上的 diff)" >&2 exit 1 fi printf 'y\n' >"$a" if ! same "$a" "$b"; then echo "检查器自检失败:相同内容的两个文件被判为不同" >&2 exit 1 fi rm -f "$a" "$b" } selfcheck libs_for() { case "$1" in *zcode-mail-bridge) echo "$MCP_LIBS" ;; *) echo "$ALL_LIBS" ;; esac } tests_for() { case "$1" in *zcode-mail-bridge) echo "$MCP_TESTS" ;; *) echo "$ALL_TESTS" ;; esac } # 差异明细也走 cmp,不借 diff。 show_diff() { cmp "$1" "$2" 2>&1 | head -3 } fail=0 for peer in "${PEERS[@]}"; do for f in $(libs_for "$peer"); do if [[ ! -f "$peer/lib/$f.js" ]]; then echo "共用模块缺失:$peer/lib/$f.js" >&2 fail=1 continue fi if ! same "$BASE/lib/$f.js" "$peer/lib/$f.js"; then echo "共用模块已分叉:lib/$f.js($BASE vs $peer)" >&2 show_diff "$BASE/lib/$f.js" "$peer/lib/$f.js" >&2 fail=1 fi done # 测试同样要同源:共用模块的行为约定写在测试里, # 只同步实现不同步测试,等于允许一侧偷偷放宽约定。 for f in $(tests_for "$peer"); do if [[ ! -f "$peer/test/$f.test.mjs" ]]; then echo "共用测试缺失:$peer/test/$f.test.mjs" >&2 fail=1 continue fi if ! same "$BASE/test/$f.test.mjs" "$peer/test/$f.test.mjs"; then echo "共用测试已分叉:test/$f.test.mjs($BASE vs $peer)" >&2 fail=1 fi done done [[ $fail -eq 0 ]] && echo " 共用模块四方同源(opencode 基准 vs dsh / pi / zcode)(判据已自检)" || exit 1