@minonl/dia 0.0.2-beta.0 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/cli.js +763 -398
- package/docs/cli.md +1 -0
- package/docs/deps.jsonl +7 -7
- package/docs/index.md +4 -2
- package/docs/kit-install.md +3 -3
- package/docs/migrations/data-root-2026-11.md +112 -0
- package/docs/research/h0/report.md +2 -2
- package/docs/research/h0/sample.json +2 -2
- package/docs/security/audits/2026-11-04.json +4 -4
- package/docs/security/audits/2026-11-05-independent.json +45 -0
- package/docs/security/credentials.md +1 -1
- package/docs/security/public-release.md +7 -1
- package/docs/security/snapshot-isolation.md +50 -0
- package/docs/status.md +8 -0
- package/package.json +3 -2
- package/packages/wt/bin/dia-wt +418 -9
- package/packages/wt/tests/adopt.test.sh +180 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# adopt / doctor / gate 回归测试(2026-11-05)
|
|
3
|
+
# 覆盖:dry-run 计划、apply 幂等、check 通过、主 checkout 守卫、doctor、revert。
|
|
4
|
+
set -uo pipefail
|
|
5
|
+
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
+
WT="$HERE/../bin/dia-wt"
|
|
7
|
+
TD="$(mktemp -d)"; trap 'rm -rf "$TD"' EXIT
|
|
8
|
+
PASS=0; FAIL=0
|
|
9
|
+
ok(){ echo " ✓ $1"; PASS=$((PASS+1)); }; bad(){ echo " ✗ $1"; FAIL=$((FAIL+1)); }
|
|
10
|
+
|
|
11
|
+
R="$TD/repo"; mkdir -p "$R"; git -C "$R" init -q -b main
|
|
12
|
+
git -C "$R" config user.email t@example.com; git -C "$R" config user.name t
|
|
13
|
+
echo a > "$R/a.txt"; git -C "$R" add -A; git -C "$R" commit -qm init --no-verify
|
|
14
|
+
|
|
15
|
+
out="$(bash "$WT" adopt "$R" --json)"
|
|
16
|
+
printf '%s' "$out" | grep -q '"defaultBranch":"main"' && ok "dry-run 识别默认分支" || bad "dry-run: $out"
|
|
17
|
+
[ ! -f "$R/.git/dia/config.json" ] && ok "dry-run 不落盘" || bad "dry-run 落盘了"
|
|
18
|
+
|
|
19
|
+
bash "$WT" adopt "$R" --apply >/dev/null 2>&1 || bad "apply 失败"
|
|
20
|
+
[ -f "$R/.git/dia/config.json" ] && ok "config 已写" || bad "缺 config"
|
|
21
|
+
hp="$(git -C "$R" config --get core.hooksPath)"
|
|
22
|
+
case "$hp" in /*) ok "hooksPath 是绝对路径" ;; *) bad "hooksPath=$hp" ;; esac
|
|
23
|
+
[ -x "$R/.git/dia/hooks/pre-commit" ] && ok "pre-commit 可执行" || bad "缺 pre-commit"
|
|
24
|
+
|
|
25
|
+
md5_before="$(md5 -q "$R/.git/dia/config.json" 2>/dev/null || md5sum "$R/.git/dia/config.json" | cut -d' ' -f1)"
|
|
26
|
+
sleep 1
|
|
27
|
+
bash "$WT" adopt "$R" --apply >/dev/null 2>&1
|
|
28
|
+
md5_after="$(md5 -q "$R/.git/dia/config.json" 2>/dev/null || md5sum "$R/.git/dia/config.json" | cut -d' ' -f1)"
|
|
29
|
+
[ "$md5_before" != "$md5_after" ] && ok "重复 apply 会刷新时间戳(幂等语义:不重复建目录/钩子)" || bad "重复 apply 无变化"
|
|
30
|
+
|
|
31
|
+
bash "$WT" adopt "$R" --check >/dev/null 2>&1 && ok "check 通过" || bad "check 失败"
|
|
32
|
+
bash "$WT" doctor "$R" >/dev/null 2>&1 && ok "doctor 通过" || bad "doctor 失败"
|
|
33
|
+
|
|
34
|
+
echo b > "$R/b.txt"; git -C "$R" add b.txt
|
|
35
|
+
if git -C "$R" commit -qm should-block 2>/dev/null; then bad "主 checkout main 提交未被拦截"; else ok "主 checkout main 提交被拦截"; fi
|
|
36
|
+
git -C "$R" reset -q
|
|
37
|
+
|
|
38
|
+
# ── 推送前敏感扫描(PII/凭据形态)──
|
|
39
|
+
BR="$TD/remote.git"; git init -q --bare "$BR"
|
|
40
|
+
git -C "$R" remote add origin "$BR" 2>/dev/null || true
|
|
41
|
+
git -C "$R" push -q --no-verify -u origin main 2>/dev/null || true
|
|
42
|
+
git -C "$R" worktree add -q "$TD/wt" -b feat/scan 2>/dev/null || true
|
|
43
|
+
echo "/Users/alice/secret" > "$TD/wt/bad.txt"
|
|
44
|
+
git -C "$TD/wt" add -A && git -C "$TD/wt" commit -qm bad --no-verify
|
|
45
|
+
if git -C "$TD/wt" push -q origin feat/scan 2>/dev/null; then bad "敏感推送未被拦截"; else ok "敏感推送被拦截"; fi
|
|
46
|
+
echo ok > "$TD/wt/bad.txt" && git -C "$TD/wt" add -A && git -C "$TD/wt" commit -qm fix --no-verify
|
|
47
|
+
git -C "$TD/wt" push -q origin feat/scan 2>/dev/null && ok "干净推送放行" || bad "干净推送被拒"
|
|
48
|
+
|
|
49
|
+
bash "$WT" adopt "$R" --revert >/dev/null 2>&1 || bad "revert 失败"
|
|
50
|
+
[ -z "$(git -C "$R" config --get core.hooksPath || true)" ] && ok "revert 还原 hooksPath" || bad "revert 未还原"
|
|
51
|
+
[ ! -d "$R/.git/dia" ] && ok "revert 删除 dia 目录" || bad "dia 目录残留"
|
|
52
|
+
|
|
53
|
+
# ── 原 hooksPath 保留 + 二次 apply 不改写 hooksPrevious(review P2-7)──────────
|
|
54
|
+
R2="$TD/repo2"; mkdir -p "$R2" "$TD/orighooks"; git -C "$R2" init -q -b main
|
|
55
|
+
git -C "$R2" config user.email t@example.com; git -C "$R2" config user.name t
|
|
56
|
+
echo a > "$R2/a.txt"; git -C "$R2" add -A; git -C "$R2" commit -qm init --no-verify
|
|
57
|
+
printf '#!/bin/sh\nexit 0\n' > "$TD/orighooks/pre-commit"; chmod +x "$TD/orighooks/pre-commit"
|
|
58
|
+
git -C "$R2" config --local core.hooksPath "$TD/orighooks"
|
|
59
|
+
bash "$WT" adopt "$R2" --apply >/dev/null 2>&1 || bad "R2 apply 失败"
|
|
60
|
+
hp1="$(python3 -c 'import json,sys;print(json.load(open(sys.argv[1])).get("hooksPrevious") or "")' "$R2/.git/dia/config.json")"
|
|
61
|
+
[ "$hp1" = "$TD/orighooks" ] && ok "首次 apply 记录 hooksPrevious" || bad "hooksPrevious=$hp1"
|
|
62
|
+
bash "$WT" adopt "$R2" --apply >/dev/null 2>&1
|
|
63
|
+
hp2="$(python3 -c 'import json,sys;print(json.load(open(sys.argv[1])).get("hooksPrevious") or "")' "$R2/.git/dia/config.json")"
|
|
64
|
+
[ "$hp2" = "$TD/orighooks" ] && ok "二次 apply 不覆盖 hooksPrevious" || bad "hooksPrevious 被改成 $hp2"
|
|
65
|
+
bash "$WT" adopt "$R2" --revert >/dev/null 2>&1 || bad "R2 revert 失败"
|
|
66
|
+
[ "$(git -C "$R2" config --get core.hooksPath)" = "$TD/orighooks" ] && ok "revert 还原原 hooksPath" || bad "revert 未还原原 hooksPath"
|
|
67
|
+
# hooksPath 被第三方改动 → revert 拒绝(不抢配置)
|
|
68
|
+
bash "$WT" adopt "$R2" --apply >/dev/null 2>&1
|
|
69
|
+
git -C "$R2" config --local core.hooksPath "$TD/orighooks"
|
|
70
|
+
if bash "$WT" adopt "$R2" --revert >/dev/null 2>&1; then bad "hooksPath 被改动后 revert 仍执行"; else ok "hooksPath 被改动后 revert 拒绝"; fi
|
|
71
|
+
[ -f "$R2/.git/dia/config.json" ] && ok "拒绝 revert 时保留 config 备份" || bad "拒绝 revert 却删了 config"
|
|
72
|
+
git -C "$R2" config --local core.hooksPath "$R2/.git/dia/hooks"; bash "$WT" adopt "$R2" --revert >/dev/null 2>&1
|
|
73
|
+
|
|
74
|
+
# ── 自定义默认分支受保护(review P1-1 + P1-2)────────────────────────────────
|
|
75
|
+
R3="$TD/repo3"; mkdir -p "$R3"; git -C "$R3" init -q -b trunk
|
|
76
|
+
git -C "$R3" config user.email t@example.com; git -C "$R3" config user.name t
|
|
77
|
+
echo a > "$R3/a.txt"; git -C "$R3" add -A; git -C "$R3" commit -qm init --no-verify
|
|
78
|
+
bash "$WT" adopt "$R3" --apply --default-branch trunk >/dev/null 2>&1 || bad "自定义分支 apply 失败"
|
|
79
|
+
bash "$WT" adopt "$R3" --check >/dev/null 2>&1 && ok "自定义默认分支 check 通过" || bad "自定义默认分支 check 失败"
|
|
80
|
+
bash "$WT" doctor "$R3" >/dev/null 2>&1 && ok "自定义默认分支 doctor 通过" || bad "自定义默认分支 doctor 失败"
|
|
81
|
+
echo b > "$R3/b.txt"; git -C "$R3" add b.txt
|
|
82
|
+
if git -C "$R3" commit -qm should-block 2>/dev/null; then bad "trunk 提交未被拦截"; else ok "自定义默认分支 trunk 提交被拦截"; fi
|
|
83
|
+
git -C "$R3" reset -q
|
|
84
|
+
|
|
85
|
+
# ── doctor:未 adopt / 无参数(review P1-4)──────────────────────────────────
|
|
86
|
+
bash "$WT" doctor "$R3" >/dev/null 2>&1 || true
|
|
87
|
+
R4="$TD/repo4"; mkdir -p "$R4"; git -C "$R4" init -q -b main
|
|
88
|
+
git -C "$R4" config user.email t@example.com; git -C "$R4" config user.name t
|
|
89
|
+
echo a > "$R4/a.txt"; git -C "$R4" add -A; git -C "$R4" commit -qm init --no-verify
|
|
90
|
+
if bash "$WT" doctor "$R4" >/dev/null 2>&1; then bad "未 adopt 的仓 doctor 误报通过"; else ok "未 adopt 的仓 doctor 失败"; fi
|
|
91
|
+
bash "$WT" doctor >/dev/null 2>&1; [ $? = 2 ] && ok "doctor 无参数 exit 2" || bad "doctor 无参数未 exit 2"
|
|
92
|
+
|
|
93
|
+
# ── pre-push 守卫(主 checkout 拒绝 / worktree 放行)──────────────────────────
|
|
94
|
+
R5="$TD/repo5"; ORIGIN5="$TD/origin5.git"; git init -q --bare "$ORIGIN5"
|
|
95
|
+
mkdir -p "$R5"; git -C "$R5" init -q -b main
|
|
96
|
+
git -C "$R5" config user.email t@example.com; git -C "$R5" config user.name t
|
|
97
|
+
echo a > "$R5/a.txt"; git -C "$R5" add -A; git -C "$R5" commit -qm init --no-verify
|
|
98
|
+
git -C "$R5" remote add origin "$ORIGIN5"; git -C "$R5" push -q --no-verify origin main
|
|
99
|
+
git -C "$R5" fetch -q origin
|
|
100
|
+
git -C "$R5" symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/main
|
|
101
|
+
bash "$WT" adopt "$R5" --apply >/dev/null 2>&1 || bad "R5 apply 失败"
|
|
102
|
+
echo x >> "$R5/a.txt"; git -C "$R5" commit -qam dirty-main --no-verify 2>/dev/null
|
|
103
|
+
if git -C "$R5" push origin main >/dev/null 2>&1; then bad "主 checkout push 未被拦截"; else ok "主 checkout push 被拦截"; fi
|
|
104
|
+
git -C "$R5" reset -q --hard origin/main
|
|
105
|
+
git -C "$R5" worktree add -q -b feat/x "$TD/repo5-worktree--x" main
|
|
106
|
+
echo b > "$TD/repo5-worktree--x/b.txt"; git -C "$TD/repo5-worktree--x" add b.txt
|
|
107
|
+
git -C "$TD/repo5-worktree--x" commit -qm feat-x --no-verify 2>/dev/null
|
|
108
|
+
if git -C "$TD/repo5-worktree--x" push -q origin feat/x >/dev/null 2>&1; then ok "worktree push 放行"; else bad "worktree push 被误拦"; fi
|
|
109
|
+
|
|
110
|
+
# ── template 生效(P2-6):adopt 后 create 按 config.template 推导路径 ─────────
|
|
111
|
+
out="$(bash "$WT" create "$R5" t1 --dry-run 2>&1 || true)"
|
|
112
|
+
printf '%s' "$out" | grep -q "repo5-worktree--t1" && ok "默认模板 create 路径" || bad "默认模板 create: $out"
|
|
113
|
+
bash "$WT" adopt "$R5" --apply --template '{{parent}}/{{repo}}.wt.{{feature}}' >/dev/null 2>&1 || bad "template apply 失败"
|
|
114
|
+
out="$(bash "$WT" create "$R5" t1 --dry-run 2>&1 || true)"
|
|
115
|
+
printf '%s' "$out" | grep -q "repo5.wt.t1" && ok "自定义模板 create 路径生效" || bad "自定义模板: $out"
|
|
116
|
+
bash "$WT" adopt "$R5" --apply >/dev/null 2>&1
|
|
117
|
+
|
|
118
|
+
# ── 过期 origin/HEAD(远端分支已删)不误判默认分支 ──────────────────────────
|
|
119
|
+
R6="$TD/repo6"; mkdir -p "$R6"; git -C "$R6" init -q -b main
|
|
120
|
+
git -C "$R6" config user.email t@example.com; git -C "$R6" config user.name t
|
|
121
|
+
echo a > "$R6/a.txt"; git -C "$R6" add -A; git -C "$R6" commit -qm init --no-verify
|
|
122
|
+
git -C "$R6" symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/gone
|
|
123
|
+
out="$(bash "$WT" adopt "$R6" --json)"
|
|
124
|
+
printf '%s' "$out" | grep -q '"defaultBranch":"main"' && ok "过期 origin/HEAD 回落到 main" || bad "过期 origin/HEAD: $out"
|
|
125
|
+
|
|
126
|
+
# ── 钩子链:原 hooksPath(.githooks / husky)不被吞掉 ─────────────────────────
|
|
127
|
+
R7="$TD/repo7"; mkdir -p "$R7/.githooks"; git -C "$R7" init -q -b main
|
|
128
|
+
git -C "$R7" config user.email t@example.com; git -C "$R7" config user.name t
|
|
129
|
+
printf '#!/bin/sh\necho chained >> "$(git rev-parse --show-toplevel)/chain.log"\n' > "$R7/.githooks/pre-commit"
|
|
130
|
+
chmod +x "$R7/.githooks/pre-commit"
|
|
131
|
+
echo a > "$R7/a.txt"; git -C "$R7" add -A; git -C "$R7" commit -qm init --no-verify
|
|
132
|
+
git -C "$R7" config --local core.hooksPath .githooks
|
|
133
|
+
bash "$WT" adopt "$R7" --apply >/dev/null 2>&1 || bad "R7 apply 失败"
|
|
134
|
+
python3 -c 'import json,sys;d=json.load(open(sys.argv[1]));sys.exit(0 if d.get("hooksChain")==".githooks" else 1)' "$R7/.git/dia/config.json" \
|
|
135
|
+
&& ok "config 记录 hooksChain" || bad "hooksChain 未记录"
|
|
136
|
+
git -C "$R7" worktree add -q -b feat/y "$TD/repo7-worktree--y" main
|
|
137
|
+
echo b > "$TD/repo7-worktree--y/b.txt"; git -C "$TD/repo7-worktree--y" add b.txt
|
|
138
|
+
git -C "$TD/repo7-worktree--y" commit -qm feat-y >/dev/null 2>&1
|
|
139
|
+
[ -f "$TD/repo7-worktree--y/chain.log" ] && ok "原 pre-commit 被链式执行" || bad "原 pre-commit 未执行(钩子被吞)"
|
|
140
|
+
bash "$WT" adopt "$R7" --revert >/dev/null 2>&1
|
|
141
|
+
[ "$(git -C "$R7" config --get core.hooksPath)" = ".githooks" ] && ok "revert 还原原 hooksPath(相对)" || bad "revert 未还原 .githooks"
|
|
142
|
+
|
|
143
|
+
# ── adopt --all:批量主 checkout,跳过 worktree 目录 ─────────────────────────
|
|
144
|
+
AR="$TD/allroot"; mkdir -p "$AR/a" "$AR/b"
|
|
145
|
+
for r in a b; do
|
|
146
|
+
git -C "$AR/$r" init -q -b main
|
|
147
|
+
git -C "$AR/$r" config user.email t@example.com; git -C "$AR/$r" config user.name t
|
|
148
|
+
echo x > "$AR/$r/x.txt"; git -C "$AR/$r" add -A; git -C "$AR/$r" commit -qm init --no-verify
|
|
149
|
+
done
|
|
150
|
+
git -C "$AR/a" worktree add -q -b feat/w "$AR/a-worktree--w" main
|
|
151
|
+
out="$(DIA_WT_REPOS_ROOT="$AR" DIA_WT_DATA_ROOT="$TD/dr2" bash "$WT" adopt --all --apply --json 2>&1)"
|
|
152
|
+
printf '%s' "$out" | grep -q '"repos":2,"ok":2,"fail":0' && ok "adopt --all 覆盖 2 个主 checkout" || bad "adopt --all: $out"
|
|
153
|
+
for r in a b; do
|
|
154
|
+
[ -n "$(git -C "$AR/$r" config --get core.hooksPath || true)" ] && ok "adopt --all: $r 已配 hooksPath" || bad "adopt --all: $r 未配置"
|
|
155
|
+
done
|
|
156
|
+
[ ! -d "$AR/a-worktree--w/.git/dia" ] && ok "adopt --all 跳过 worktree 目录" || bad "adopt --all 误改 worktree"
|
|
157
|
+
DIA_WT_REPOS_ROOT="$TD/empty-root" DIA_WT_DATA_ROOT="$TD/dr3" bash "$WT" adopt --all >/dev/null 2>&1
|
|
158
|
+
[ $? = 2 ] && ok "adopt --all 空结果 exit 2" || bad "adopt --all 空结果未 exit 2"
|
|
159
|
+
|
|
160
|
+
# ── doctor --fix:钩子被覆盖后能无损重建 ────────────────────────────────────
|
|
161
|
+
R8="$TD/repo8"; mkdir -p "$R8"; git -C "$R8" init -q -b main
|
|
162
|
+
git -C "$R8" config user.email t@example.com; git -C "$R8" config user.name t
|
|
163
|
+
echo a > "$R8/a.txt"; git -C "$R8" add -A; git -C "$R8" commit -qm init --no-verify
|
|
164
|
+
bash "$WT" adopt "$R8" --apply >/dev/null 2>&1
|
|
165
|
+
rm -f "$R8/.git/dia/hooks/post-checkout"
|
|
166
|
+
printf '#!/bin/sh\nexit 0\n' > "$R8/.git/dia/hooks/pre-commit"; chmod +x "$R8/.git/dia/hooks/pre-commit"
|
|
167
|
+
if bash "$WT" doctor "$R8" >/dev/null 2>&1; then bad "钩子被覆盖后 doctor 仍通过"; else ok "钩子被覆盖后 doctor 报失败"; fi
|
|
168
|
+
bash "$WT" doctor "$R8" --fix >/dev/null 2>&1 || bad "doctor --fix 失败"
|
|
169
|
+
bash "$WT" doctor "$R8" >/dev/null 2>&1 && ok "doctor --fix 后恢复 ok" || bad "doctor --fix 后仍失败"
|
|
170
|
+
[ -x "$R8/.git/dia/hooks/post-checkout" ] && ok "doctor --fix 补回 post-checkout" || bad "post-checkout 未补回"
|
|
171
|
+
|
|
172
|
+
# ── gate:成功 / 失败(P1-3 精确匹配)────────────────────────────────────────
|
|
173
|
+
bash "$WT" gate "$R5" x >/dev/null 2>&1 && ok "gate 成功" || bad "gate 失败"
|
|
174
|
+
if bash "$WT" gate "$R5" xb >/dev/null 2>&1; then bad "gate 子串误匹配(x 命中 xb)"; else ok "gate 精确匹配 feature"; fi
|
|
175
|
+
echo dirty >> "$TD/repo5-worktree--x/b.txt"
|
|
176
|
+
if bash "$WT" gate "$R5" x >/dev/null 2>&1; then bad "worktree 脏时 gate 通过"; else ok "worktree 脏时 gate 失败"; fi
|
|
177
|
+
git -C "$TD/repo5-worktree--x" checkout -q -- b.txt
|
|
178
|
+
|
|
179
|
+
echo; echo "── PASS $PASS · FAIL $FAIL ──"
|
|
180
|
+
[ "$FAIL" = 0 ]
|