@reddb-io/red-skills-dev 2.75.2
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/LICENSE +202 -0
- package/README.md +30 -0
- package/package.json +32 -0
- package/skills/engineering/README.md +36 -0
- package/skills/engineering/_report-runtime/WRAPPER.md +47 -0
- package/skills/engineering/afk/AGENT-PROMPT.md +295 -0
- package/skills/engineering/afk/SAFETY.md +103 -0
- package/skills/engineering/afk/SKILL.md +160 -0
- package/skills/engineering/afk/TROUBLESHOOTING.md +260 -0
- package/skills/engineering/afk/actions-lane.md +213 -0
- package/skills/engineering/afk/bin/README.md +29 -0
- package/skills/engineering/afk/bin/afk.mjs +51 -0
- package/skills/engineering/afk/defaults/cargo-pre-worktree.sh +37 -0
- package/skills/engineering/afk/defaults/envelope-post-attempt.sh +47 -0
- package/skills/engineering/afk/defaults/gradle-pre-worktree.sh +42 -0
- package/skills/engineering/afk/defaults/heartbeat-post-attempt.sh +43 -0
- package/skills/engineering/afk/defaults/validation-post-merge.sh +77 -0
- package/skills/engineering/afk/detectors/README.md +72 -0
- package/skills/engineering/afk/detectors/cargo.sh +27 -0
- package/skills/engineering/afk/detectors/gradle.sh +35 -0
- package/skills/engineering/afk/docs/BOOT-SWEEPS.md +81 -0
- package/skills/engineering/afk/docs/CONFIG.md +238 -0
- package/skills/engineering/afk/docs/ENVELOPE.md +177 -0
- package/skills/engineering/afk/docs/HANDOFF.md +70 -0
- package/skills/engineering/afk/docs/LIVENESS.md +56 -0
- package/skills/engineering/afk/docs/OPERATIONS.md +409 -0
- package/skills/engineering/afk/examples/only-mine.sh +32 -0
- package/skills/engineering/afk/examples/red-afk-attempt-action.yml +53 -0
- package/skills/engineering/afk/examples/rs-afk-attempt.yml +76 -0
- package/skills/engineering/afk/fleet.md +127 -0
- package/skills/engineering/afk/hooks/red-cargo +44 -0
- package/skills/engineering/afk/hooks/red-envelope +50 -0
- package/skills/engineering/afk/hooks/red-gradle +50 -0
- package/skills/engineering/afk/hooks/red-heartbeat +46 -0
- package/skills/engineering/afk/hooks/red-validation +79 -0
- package/skills/engineering/afk/monitor.md +170 -0
- package/skills/engineering/afk/runner-claude-minimax.md +71 -0
- package/skills/engineering/afk/runner-claude.md +67 -0
- package/skills/engineering/afk/runner-codex.md +138 -0
- package/skills/engineering/afk/runner-hermes.md +40 -0
- package/skills/engineering/afk/runner-opencode.md +142 -0
- package/skills/engineering/ask-red/SKILL.md +137 -0
- package/skills/engineering/audit-skills/SKILL.md +46 -0
- package/skills/engineering/code-review/SKILL.md +106 -0
- package/skills/engineering/context/SKILL.md +142 -0
- package/skills/engineering/curate/SKILL.md +133 -0
- package/skills/engineering/daily-review/SKILL.md +68 -0
- package/skills/engineering/dashboard/SKILL.md +44 -0
- package/skills/engineering/diagnose/SKILL.md +155 -0
- package/skills/engineering/diagnose/scripts/hitl-loop.template.sh +41 -0
- package/skills/engineering/go/SKILL.md +144 -0
- package/skills/engineering/go/TROUBLESHOOTING.md +81 -0
- package/skills/engineering/ground-truth/SKILL.md +84 -0
- package/skills/engineering/hitl/SKILL.md +171 -0
- package/skills/engineering/hitl/TROUBLESHOOTING.md +76 -0
- package/skills/engineering/implement/SKILL.md +69 -0
- package/skills/engineering/improve-codebase-architecture/DEEPENING.md +37 -0
- package/skills/engineering/improve-codebase-architecture/INTERFACE-DESIGN.md +44 -0
- package/skills/engineering/improve-codebase-architecture/LANGUAGE.md +53 -0
- package/skills/engineering/improve-codebase-architecture/SKILL.md +81 -0
- package/skills/engineering/model-tier-policy/SKILL.md +83 -0
- package/skills/engineering/prototype/LOGIC.md +79 -0
- package/skills/engineering/prototype/SKILL.md +30 -0
- package/skills/engineering/prototype/UI.md +112 -0
- package/skills/engineering/red-doctor/APPLY.md +35 -0
- package/skills/engineering/red-doctor/SKILL.md +172 -0
- package/skills/engineering/red-gains/SKILL.md +49 -0
- package/skills/engineering/red-setup/INTERVIEW.md +266 -0
- package/skills/engineering/red-setup/ISSUE-SWEEP.md +18 -0
- package/skills/engineering/red-setup/REFERENCE.md +35 -0
- package/skills/engineering/red-setup/SKILL.md +58 -0
- package/skills/engineering/red-setup/WORKFLOWS.md +74 -0
- package/skills/engineering/red-setup/WRITE-CONTRACT.md +151 -0
- package/skills/engineering/red-setup/config-template.yaml +174 -0
- package/skills/engineering/red-setup/domain.md +52 -0
- package/skills/engineering/red-setup/issue-tracker-github.md +88 -0
- package/skills/engineering/red-setup/rsp-retirement-runbook.md +35 -0
- package/skills/engineering/red-setup/scripts/install-runtime-shim.sh +189 -0
- package/skills/engineering/red-setup/triage-labels.md +176 -0
- package/skills/engineering/red-setup/workflows/red-issues-needs-triage.yml +36 -0
- package/skills/engineering/red-statusline/HOST-NOTES.md +180 -0
- package/skills/engineering/red-statusline/SKILL.md +58 -0
- package/skills/engineering/report-bug/SKILL.md +155 -0
- package/skills/engineering/resolving-merge-conflicts/SKILL.md +143 -0
- package/skills/engineering/retake/SKILL.md +184 -0
- package/skills/engineering/review-adrs/SKILL.md +125 -0
- package/skills/engineering/start/ADR-FORMAT.md +47 -0
- package/skills/engineering/start/CONTEXT-FORMAT.md +77 -0
- package/skills/engineering/start/DOC-LANDING-FINALIZER.md +9 -0
- package/skills/engineering/start/SKILL.md +179 -0
- package/skills/engineering/tdd/SKILL.md +96 -0
- package/skills/engineering/tdd/deep-modules.md +33 -0
- package/skills/engineering/tdd/interface-design.md +31 -0
- package/skills/engineering/tdd/mocking.md +59 -0
- package/skills/engineering/tdd/tests.md +85 -0
- package/skills/engineering/to-spec/SKILL.md +104 -0
- package/skills/engineering/to-tickets/SKILL.md +158 -0
- package/skills/engineering/triage/AGENT-BRIEF.md +207 -0
- package/skills/engineering/triage/OUT-OF-SCOPE.md +101 -0
- package/skills/engineering/triage/SKILL.md +176 -0
- package/skills/engineering/verify/SKILL.md +136 -0
- package/skills/engineering/wayfinder/SKILL.md +128 -0
- package/skills/engineering/zoom-out/SKILL.md +51 -0
- package/skills/engineering/zoom-out/scripts/graph-mode-plumbing.sh +36 -0
- package/skills/engineering/zoom-out/scripts/tests/contract.test.sh +168 -0
- package/skills/knowledge/README.md +5 -0
- package/skills/knowledge/research/SKILL.md +76 -0
- package/skills/misc/README.md +8 -0
- package/skills/misc/branch-lock/SKILL.md +187 -0
- package/skills/misc/branch-lock/scripts/branch-lock-hook.sh +113 -0
- package/skills/misc/branch-lock/scripts/branch-lock-session-start.sh +81 -0
- package/skills/misc/branch-lock/scripts/branch-lock.sh +68 -0
- package/skills/misc/branch-lock/scripts/lib/dev-config.sh +126 -0
- package/skills/misc/branch-lock/scripts/lib/git-command-classifier.sh +198 -0
- package/skills/misc/branch-lock/scripts/lib/lock-store.sh +67 -0
- package/skills/misc/branch-lock/scripts/lib/scope-resolver.sh +43 -0
- package/skills/misc/branch-lock/scripts/tests/branch-lock-cli.test.sh +114 -0
- package/skills/misc/branch-lock/scripts/tests/claude-plugin-hook.test.sh +117 -0
- package/skills/misc/branch-lock/scripts/tests/codex-hook.test.sh +229 -0
- package/skills/misc/branch-lock/scripts/tests/dev-config.test.sh +99 -0
- package/skills/misc/branch-lock/scripts/tests/git-command-classifier.test.sh +187 -0
- package/skills/misc/branch-lock/scripts/tests/lock-store.test.sh +90 -0
- package/skills/misc/branch-lock/scripts/tests/scope-resolver.test.sh +63 -0
- package/skills/misc/branch-lock/scripts/tests/session-start.test.sh +123 -0
- package/skills/misc/git-guardrails-claude-code/SKILL.md +105 -0
- package/skills/misc/git-guardrails-claude-code/scripts/block-dangerous-git.sh +231 -0
- package/skills/misc/git-guardrails-claude-code/scripts/tests/block-dangerous-git.test.sh +143 -0
- package/skills/misc/migrate-to-shoehorn/SKILL.md +96 -0
- package/skills/misc/setup-pre-commit/SKILL.md +94 -0
- package/skills/productivity/README.md +8 -0
- package/skills/productivity/ff/SKILL.md +116 -0
- package/skills/productivity/handoff/SKILL.md +37 -0
- package/skills/productivity/reflect/SKILL.md +18 -0
- package/skills/productivity/write-a-skill/SKILL.md +190 -0
- package/skills/productivity/write-a-skill/WRITING-STYLE.md +62 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Unit test for the Codex plugin PreToolUse branch-lock hook.
|
|
3
|
+
|
|
4
|
+
set -uo pipefail
|
|
5
|
+
|
|
6
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
+
PLUGIN_ROOT="$(cd "$HERE/../../../../.." && pwd)"
|
|
8
|
+
HOOK="$PLUGIN_ROOT/hooks/branch-lock-codex.sh"
|
|
9
|
+
MANIFEST="$PLUGIN_ROOT/hooks/codex.hooks.json"
|
|
10
|
+
|
|
11
|
+
pass=0
|
|
12
|
+
fail=0
|
|
13
|
+
|
|
14
|
+
ok() { echo "PASS $1"; pass=$((pass + 1)); }
|
|
15
|
+
bad() { echo "FAIL $1"; fail=$((fail + 1)); }
|
|
16
|
+
|
|
17
|
+
expect_eq() {
|
|
18
|
+
local label="$1" expected="$2" actual="$3"
|
|
19
|
+
if [[ "$expected" == "$actual" ]]; then
|
|
20
|
+
ok "$label"
|
|
21
|
+
else
|
|
22
|
+
bad "$label"
|
|
23
|
+
printf ' expected: %q\n actual: %q\n' "$expected" "$actual"
|
|
24
|
+
fi
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
expect_contains() {
|
|
28
|
+
local label="$1" needle="$2" haystack="$3"
|
|
29
|
+
if [[ "$haystack" == *"$needle"* ]]; then
|
|
30
|
+
ok "$label"
|
|
31
|
+
else
|
|
32
|
+
bad "$label"
|
|
33
|
+
printf ' missing: %q\n in: %q\n' "$needle" "$haystack"
|
|
34
|
+
fi
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
tmp="$(mktemp -d -t branch-lock-codex.XXXXXX)"
|
|
38
|
+
trap 'rm -rf "$tmp"' EXIT
|
|
39
|
+
|
|
40
|
+
primary="$tmp/red-skills"
|
|
41
|
+
mkdir -p "$primary/.red/tmp"
|
|
42
|
+
cat > "$primary/.red/config.yaml" <<'EOF'
|
|
43
|
+
plugins:
|
|
44
|
+
dev:
|
|
45
|
+
enabled: true
|
|
46
|
+
EOF
|
|
47
|
+
printf 'main\n' > "$primary/.red/tmp/branch-lock.yaml"
|
|
48
|
+
|
|
49
|
+
run_hook() {
|
|
50
|
+
local root="$1" payload="$2" out err rc
|
|
51
|
+
out="$tmp/out"
|
|
52
|
+
err="$tmp/err"
|
|
53
|
+
CODEX_PLUGIN_ROOT="$PLUGIN_ROOT" "$HOOK" >"$out" 2>"$err" <<<"$payload"
|
|
54
|
+
rc=$?
|
|
55
|
+
printf '%s\n---stdout---\n%s\n---stderr---\n%s\n' "$rc" "$(<"$out")" "$(<"$err")"
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
payload() {
|
|
59
|
+
local root="$1" cmd="$2"
|
|
60
|
+
jq -nc --arg cwd "$root" --arg cmd "$cmd" \
|
|
61
|
+
'{hook_event_name:"PreToolUse", cwd:$cwd, tool_input:{cmd:$cmd}}'
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
manifest_hook="$(jq -r '.hooks.PreToolUse[0].hooks[] | select(.command | contains("branch-lock-codex.sh")) | .command' "$MANIFEST")"
|
|
65
|
+
expect_contains "manifest: wires branch-lock-codex.sh" "branch-lock-codex.sh" "$manifest_hook"
|
|
66
|
+
expect_contains "manifest: wrapper drains stdin before hook" 'cat >"$tmp"' "$manifest_hook"
|
|
67
|
+
|
|
68
|
+
out="$tmp/manifest-out"
|
|
69
|
+
err="$tmp/manifest-err"
|
|
70
|
+
CODEX_PLUGIN_ROOT="$PLUGIN_ROOT" bash -lc "$manifest_hook" >"$out" 2>"$err" \
|
|
71
|
+
<<<"$(payload "$primary" "git status")"
|
|
72
|
+
rc=$?
|
|
73
|
+
expect_eq "manifest: command executes through shell" "0" "$rc"
|
|
74
|
+
expect_eq "manifest: command prints empty JSON" "{}" "$(<"$out")"
|
|
75
|
+
|
|
76
|
+
missing_root="$tmp/missing-plugin-root"
|
|
77
|
+
mkdir -p "$missing_root"
|
|
78
|
+
CODEX_PLUGIN_ROOT="$missing_root" bash -lc "$manifest_hook" >"$out" 2>"$err" \
|
|
79
|
+
<<<"$(payload "$primary" "git switch main")"
|
|
80
|
+
rc=$?
|
|
81
|
+
expect_eq "manifest: missing hook fails open" "0" "$rc"
|
|
82
|
+
expect_eq "manifest: missing hook prints empty JSON" "{}" "$(<"$out")"
|
|
83
|
+
|
|
84
|
+
early_root="$tmp/early-plugin-root"
|
|
85
|
+
mkdir -p "$early_root/hooks"
|
|
86
|
+
cat > "$early_root/hooks/branch-lock-codex.sh" <<'EOF'
|
|
87
|
+
#!/usr/bin/env sh
|
|
88
|
+
printf '{}'
|
|
89
|
+
exit 0
|
|
90
|
+
EOF
|
|
91
|
+
chmod +x "$early_root/hooks/branch-lock-codex.sh"
|
|
92
|
+
set -o pipefail
|
|
93
|
+
(head -c 1048576 /dev/zero | tr '\0' x) \
|
|
94
|
+
| CODEX_PLUGIN_ROOT="$early_root" bash -lc "$manifest_hook" >"$out" 2>"$err"
|
|
95
|
+
rc=$?
|
|
96
|
+
set +o pipefail
|
|
97
|
+
expect_eq "manifest: early hook still drains Codex stdin" "0" "$rc"
|
|
98
|
+
expect_eq "manifest: early hook prints empty JSON" "{}" "$(<"$out")"
|
|
99
|
+
|
|
100
|
+
CODEX_PLUGIN_ROOT="$missing_root" "$HOOK" >"$out" 2>"$err" \
|
|
101
|
+
<<<"$(payload "$primary" "git switch main")"
|
|
102
|
+
rc=$?
|
|
103
|
+
expect_eq "hook: incomplete plugin root fails open" "0" "$rc"
|
|
104
|
+
expect_eq "hook: incomplete plugin root prints empty JSON" "{}" "$(<"$out")"
|
|
105
|
+
|
|
106
|
+
# Branch movement in the primary is now blocked unconditionally by the
|
|
107
|
+
# untouchable-primary rule (ADR 0083) — it fires before the branch-lock path, so
|
|
108
|
+
# switching away, and even switching *back* to the locked branch, is refused.
|
|
109
|
+
result="$(run_hook "$primary" "$(payload "$primary" "git switch feature")")"
|
|
110
|
+
rc="$(sed -n '1p' <<<"$result")"
|
|
111
|
+
stderr="$(sed -n '/---stderr---/,$p' <<<"$result")"
|
|
112
|
+
expect_eq "locked: switch away is blocked" "0" "$rc"
|
|
113
|
+
expect_contains "locked: error names ADR 0083" "ADR 0083" "$stderr"
|
|
114
|
+
|
|
115
|
+
result="$(run_hook "$primary" "$(payload "$primary" "git switch main")")"
|
|
116
|
+
rc="$(sed -n '1p' <<<"$result")"
|
|
117
|
+
stderr="$(sed -n '/---stderr---/,$p' <<<"$result")"
|
|
118
|
+
expect_eq "locked: switch back is also blocked (untouchable primary)" "0" "$rc"
|
|
119
|
+
expect_contains "locked: switch-back error names ADR 0083" "ADR 0083" "$stderr"
|
|
120
|
+
|
|
121
|
+
# Work-loss commands are caught by the unconditional untouchable-primary guard
|
|
122
|
+
# (#1024 reset/stash family + #1025 unconditional structure) even while a lock
|
|
123
|
+
# is active — the guard runs before the lock read, so the refusal names
|
|
124
|
+
# ADR 0083, not the branch lock.
|
|
125
|
+
result="$(run_hook "$primary" "$(payload "$primary" "git reset --hard HEAD~1")")"
|
|
126
|
+
rc="$(sed -n '1p' <<<"$result")"
|
|
127
|
+
stderr="$(sed -n '/---stderr---/,$p' <<<"$result")"
|
|
128
|
+
expect_eq "locked: work-loss reset --hard is blocked" "0" "$rc"
|
|
129
|
+
expect_contains "locked: work-loss error names untouchable primary" "ADR 0083" "$stderr"
|
|
130
|
+
|
|
131
|
+
rm -f "$primary/.red/tmp/branch-lock.yaml"
|
|
132
|
+
result="$(run_hook "$primary" "$(payload "$primary" "git switch feature")")"
|
|
133
|
+
rc="$(sed -n '1p' <<<"$result")"
|
|
134
|
+
stderr="$(sed -n '/---stderr---/,$p' <<<"$result")"
|
|
135
|
+
expect_eq "unlocked: switch is still blocked (untouchable primary)" "0" "$rc"
|
|
136
|
+
expect_contains "unlocked: error names ADR 0083" "ADR 0083" "$stderr"
|
|
137
|
+
|
|
138
|
+
# With no lock file the untouchable-primary guard still blocks every branch
|
|
139
|
+
# move, regardless of the legacy `dev.lock.primary-branch` toggle value.
|
|
140
|
+
mkdir -p "$primary/.red"
|
|
141
|
+
cat > "$primary/.red/config.yaml" <<'EOF'
|
|
142
|
+
plugins:
|
|
143
|
+
dev:
|
|
144
|
+
enabled: true
|
|
145
|
+
EOF
|
|
146
|
+
|
|
147
|
+
result="$(run_hook "$primary" "$(payload "$primary" "git switch feature")")"
|
|
148
|
+
rc="$(sed -n '1p' <<<"$result")"
|
|
149
|
+
stderr="$(sed -n '/---stderr---/,$p' <<<"$result")"
|
|
150
|
+
expect_eq "primary guard: switch blocked with no lock and no config" "0" "$rc"
|
|
151
|
+
expect_contains "primary guard: error names ADR 0083" "ADR 0083" "$stderr"
|
|
152
|
+
|
|
153
|
+
result="$(run_hook "$primary" "$(payload "$primary" "git checkout feature")")"
|
|
154
|
+
rc="$(sed -n '1p' <<<"$result")"
|
|
155
|
+
expect_eq "primary guard: checkout branch is blocked" "0" "$rc"
|
|
156
|
+
|
|
157
|
+
# issue #1024 — git reset (any form) and git stash (all subcommands) blocked,
|
|
158
|
+
# and the refusal explains the reason + the sanctioned worktree alternative.
|
|
159
|
+
result="$(run_hook "$primary" "$(payload "$primary" "git reset --hard")")"
|
|
160
|
+
rc="$(sed -n '1p' <<<"$result")"
|
|
161
|
+
stderr="$(sed -n '/---stderr---/,$p' <<<"$result")"
|
|
162
|
+
expect_eq "primary guard: reset --hard is blocked when flag is on" "0" "$rc"
|
|
163
|
+
expect_contains "primary guard: reset refusal points to worktree" ".red/tmp/work-" "$stderr"
|
|
164
|
+
|
|
165
|
+
result="$(run_hook "$primary" "$(payload "$primary" "git reset --soft HEAD~1")")"
|
|
166
|
+
rc="$(sed -n '1p' <<<"$result")"
|
|
167
|
+
expect_eq "primary guard: reset --soft is blocked when flag is on" "0" "$rc"
|
|
168
|
+
|
|
169
|
+
result="$(run_hook "$primary" "$(payload "$primary" "git stash")")"
|
|
170
|
+
rc="$(sed -n '1p' <<<"$result")"
|
|
171
|
+
expect_eq "primary guard: stash is blocked when flag is on" "0" "$rc"
|
|
172
|
+
|
|
173
|
+
result="$(run_hook "$primary" "$(payload "$primary" "git rebase --autostash main")")"
|
|
174
|
+
rc="$(sed -n '1p' <<<"$result")"
|
|
175
|
+
expect_eq "primary guard: rebase --autostash is blocked when flag is on" "0" "$rc"
|
|
176
|
+
|
|
177
|
+
result="$(run_hook "$primary" "$(payload "$primary" "git switch -b new")")"
|
|
178
|
+
rc="$(sed -n '1p' <<<"$result")"
|
|
179
|
+
expect_eq "primary guard: switch -b is blocked" "0" "$rc"
|
|
180
|
+
|
|
181
|
+
result="$(run_hook "$primary" "$(payload "$primary" "git commit -m wip")")"
|
|
182
|
+
rc="$(sed -n '1p' <<<"$result")"
|
|
183
|
+
expect_eq "primary guard: commit is allowed" "0" "$rc"
|
|
184
|
+
|
|
185
|
+
result="$(run_hook "$primary" "$(payload "$primary" "git worktree add ../wt feature")")"
|
|
186
|
+
rc="$(sed -n '1p' <<<"$result")"
|
|
187
|
+
expect_eq "primary guard: worktree add is allowed" "0" "$rc"
|
|
188
|
+
|
|
189
|
+
result="$(run_hook "$primary" "$(payload "$primary" "git status")")"
|
|
190
|
+
rc="$(sed -n '1p' <<<"$result")"
|
|
191
|
+
expect_eq "primary guard: read-only git is allowed" "0" "$rc"
|
|
192
|
+
|
|
193
|
+
# The legacy toggle is still readable but can no longer *enable* switching:
|
|
194
|
+
# explicitly false must not re-open primary branch movement (ADR 0083 §2).
|
|
195
|
+
cat > "$primary/.red/config.yaml" <<'EOF'
|
|
196
|
+
plugins:
|
|
197
|
+
dev:
|
|
198
|
+
enabled: true
|
|
199
|
+
lock:
|
|
200
|
+
primary-branch: false
|
|
201
|
+
EOF
|
|
202
|
+
result="$(run_hook "$primary" "$(payload "$primary" "git switch feature")")"
|
|
203
|
+
rc="$(sed -n '1p' <<<"$result")"
|
|
204
|
+
expect_eq "primary guard: legacy toggle off still blocks switch" "0" "$rc"
|
|
205
|
+
|
|
206
|
+
worktree="$primary/.red/tmp/work-wAAAA-i1/worktree"
|
|
207
|
+
mkdir -p "$worktree" "$primary/.red/tmp"
|
|
208
|
+
printf 'main\n' > "$primary/.red/tmp/branch-lock.yaml"
|
|
209
|
+
result="$(run_hook "$worktree" "$(payload "$worktree" "git switch feature")")"
|
|
210
|
+
rc="$(sed -n '1p' <<<"$result")"
|
|
211
|
+
expect_eq "worktree: branch lock is exempt" "0" "$rc"
|
|
212
|
+
|
|
213
|
+
mkdir -p "$worktree/.red"
|
|
214
|
+
cat > "$worktree/.red/config.yaml" <<'EOF'
|
|
215
|
+
dev:
|
|
216
|
+
lock:
|
|
217
|
+
primary-branch: true
|
|
218
|
+
EOF
|
|
219
|
+
result="$(run_hook "$worktree" "$(payload "$worktree" "git switch feature")")"
|
|
220
|
+
rc="$(sed -n '1p' <<<"$result")"
|
|
221
|
+
expect_eq "worktree: primary guard is exempt" "0" "$rc"
|
|
222
|
+
|
|
223
|
+
result="$(run_hook "$primary" "$(jq -nc --arg cwd "$primary" '{hook_event_name:"PreToolUse", cwd:$cwd, tool_input:{file:"x"}}')")"
|
|
224
|
+
rc="$(sed -n '1p' <<<"$result")"
|
|
225
|
+
expect_eq "non-shell payload: no-op allowed" "0" "$rc"
|
|
226
|
+
|
|
227
|
+
echo
|
|
228
|
+
echo "summary: $pass passed, $fail failed"
|
|
229
|
+
[[ "$fail" -eq 0 ]]
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Unit test for lib/dev-config.sh — runtime reader for dev.lock.primary-branch.
|
|
3
|
+
|
|
4
|
+
set -uo pipefail
|
|
5
|
+
|
|
6
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
+
LIB="$(dirname "$HERE")/lib/dev-config.sh"
|
|
8
|
+
|
|
9
|
+
# shellcheck source=../lib/dev-config.sh
|
|
10
|
+
source "$LIB"
|
|
11
|
+
|
|
12
|
+
pass=0
|
|
13
|
+
fail=0
|
|
14
|
+
|
|
15
|
+
expect_rc() {
|
|
16
|
+
local label="$1" expected="$2" file="$3"
|
|
17
|
+
dev_config_lock_primary_branch_enabled "$file"
|
|
18
|
+
local actual=$?
|
|
19
|
+
if [[ "$actual" == "$expected" ]]; then
|
|
20
|
+
echo "PASS $label"; pass=$((pass + 1))
|
|
21
|
+
else
|
|
22
|
+
printf 'FAIL %s\n expected rc: %q\n actual rc: %q\n' "$label" "$expected" "$actual"
|
|
23
|
+
fail=$((fail + 1))
|
|
24
|
+
fi
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
tmp="$(mktemp -d)"
|
|
28
|
+
trap 'rm -rf "$tmp"' EXIT
|
|
29
|
+
|
|
30
|
+
missing="$tmp/missing.yaml"
|
|
31
|
+
expect_rc "missing file: disabled" 1 "$missing"
|
|
32
|
+
|
|
33
|
+
cfg="$tmp/config.yaml"
|
|
34
|
+
|
|
35
|
+
cat > "$cfg" <<'EOF'
|
|
36
|
+
dev:
|
|
37
|
+
lock:
|
|
38
|
+
primary-branch: true
|
|
39
|
+
EOF
|
|
40
|
+
expect_rc "nested dev flag true: enabled" 0 "$cfg"
|
|
41
|
+
|
|
42
|
+
cat > "$cfg" <<'EOF'
|
|
43
|
+
dev:
|
|
44
|
+
lock:
|
|
45
|
+
primary-branch: false
|
|
46
|
+
EOF
|
|
47
|
+
expect_rc "nested dev flag false: disabled" 1 "$cfg"
|
|
48
|
+
|
|
49
|
+
cat > "$cfg" <<'EOF'
|
|
50
|
+
dev:
|
|
51
|
+
lock:
|
|
52
|
+
primary-branch: "true"
|
|
53
|
+
EOF
|
|
54
|
+
expect_rc "quoted true: enabled" 0 "$cfg"
|
|
55
|
+
|
|
56
|
+
cat > "$cfg" <<'EOF'
|
|
57
|
+
afk:
|
|
58
|
+
default_runner: codex
|
|
59
|
+
EOF
|
|
60
|
+
expect_rc "absent key: disabled" 1 "$cfg"
|
|
61
|
+
|
|
62
|
+
cat > "$cfg" <<'EOF'
|
|
63
|
+
dev:
|
|
64
|
+
lock:
|
|
65
|
+
primary-branch: true
|
|
66
|
+
EOF
|
|
67
|
+
expect_rc "malformed indentation: disabled" 1 "$cfg"
|
|
68
|
+
|
|
69
|
+
cat > "$cfg" <<'EOF'
|
|
70
|
+
dev:
|
|
71
|
+
lock:
|
|
72
|
+
primary-branch: "true" # branch protection on
|
|
73
|
+
EOF
|
|
74
|
+
expect_rc "inline comment after quoted true: enabled" 0 "$cfg"
|
|
75
|
+
|
|
76
|
+
cat > "$cfg" <<'EOF'
|
|
77
|
+
dev:
|
|
78
|
+
lock:
|
|
79
|
+
primary-branch: true
|
|
80
|
+
afk:
|
|
81
|
+
backpressure:
|
|
82
|
+
- npm run test
|
|
83
|
+
- npm run lint
|
|
84
|
+
EOF
|
|
85
|
+
expect_rc "block-sequence sibling does not disable guard: enabled" 0 "$cfg"
|
|
86
|
+
|
|
87
|
+
cat > "$cfg" <<'EOF'
|
|
88
|
+
afk:
|
|
89
|
+
backpressure:
|
|
90
|
+
- npm run test
|
|
91
|
+
dev:
|
|
92
|
+
lock:
|
|
93
|
+
primary-branch: true
|
|
94
|
+
EOF
|
|
95
|
+
expect_rc "block-sequence before dev key: enabled" 0 "$cfg"
|
|
96
|
+
|
|
97
|
+
echo
|
|
98
|
+
echo "summary: $pass passed, $fail failed"
|
|
99
|
+
[[ "$fail" -eq 0 ]]
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Unit test for lib/git-command-classifier.sh (issue #60).
|
|
3
|
+
#
|
|
4
|
+
# Locked to "main", verifies the minimal classifier:
|
|
5
|
+
# block — checkout/switch to a branch other than the lock target
|
|
6
|
+
# allow — switching back to the lock target, file-level `checkout -- <path>`,
|
|
7
|
+
# bare checkout, `git worktree add`, and non-git/non-checkout commands
|
|
8
|
+
|
|
9
|
+
set -uo pipefail
|
|
10
|
+
|
|
11
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
12
|
+
LIB="$(dirname "$HERE")/lib/git-command-classifier.sh"
|
|
13
|
+
|
|
14
|
+
# shellcheck source=../lib/git-command-classifier.sh
|
|
15
|
+
source "$LIB"
|
|
16
|
+
|
|
17
|
+
pass=0
|
|
18
|
+
fail=0
|
|
19
|
+
|
|
20
|
+
expect_verdict() {
|
|
21
|
+
local label="$1" lock="$2" cmd="$3" expected="$4"
|
|
22
|
+
local actual; actual="$(classify_git_command "$lock" "$cmd")"
|
|
23
|
+
if [[ "$actual" == "$expected" ]]; then
|
|
24
|
+
echo "PASS $label"; pass=$((pass + 1))
|
|
25
|
+
else
|
|
26
|
+
printf 'FAIL %s\n cmd: %q\n expected: %q\n actual: %q\n' \
|
|
27
|
+
"$label" "$cmd" "$expected" "$actual"
|
|
28
|
+
fail=$((fail + 1))
|
|
29
|
+
fi
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
LOCK="main"
|
|
33
|
+
|
|
34
|
+
# ===========================================================================
|
|
35
|
+
# BLOCK — leaving the locked branch
|
|
36
|
+
# ===========================================================================
|
|
37
|
+
expect_verdict "block/checkout other" "$LOCK" "git checkout feature" "block"
|
|
38
|
+
expect_verdict "block/switch other" "$LOCK" "git switch feature" "block"
|
|
39
|
+
expect_verdict "block/checkout slash branch" "$LOCK" "git checkout feature/x" "block"
|
|
40
|
+
expect_verdict "block/checkout -b new" "$LOCK" "git checkout -b shiny" "block"
|
|
41
|
+
expect_verdict "block/switch -c new" "$LOCK" "git switch -c shiny" "block"
|
|
42
|
+
expect_verdict "block/switch dash previous" "$LOCK" "git switch -" "block"
|
|
43
|
+
expect_verdict "block/compound command" "$LOCK" "cd repo && git checkout other" "block"
|
|
44
|
+
expect_verdict "block/checkout --track" "$LOCK" "git checkout --track origin/x" "block"
|
|
45
|
+
|
|
46
|
+
# global options before the subcommand must not bypass the lock
|
|
47
|
+
expect_verdict "block/-C path checkout" "$LOCK" "git -C /repo checkout other" "block"
|
|
48
|
+
expect_verdict "block/-C path switch" "$LOCK" "git -C /repo switch other" "block"
|
|
49
|
+
expect_verdict "block/-C path checkout -b" "$LOCK" "git -C /repo checkout -b shiny" "block"
|
|
50
|
+
expect_verdict "block/-c config checkout" "$LOCK" "git -c core.x=1 checkout other" "block"
|
|
51
|
+
expect_verdict "block/--git-dir eq switch" "$LOCK" "git --git-dir=/r/.git switch o" "block"
|
|
52
|
+
expect_verdict "block/--git-dir arg switch" "$LOCK" "git --git-dir /r/.git switch o" "block"
|
|
53
|
+
expect_verdict "block/--no-pager checkout" "$LOCK" "git --no-pager checkout other" "block"
|
|
54
|
+
expect_verdict "block/-C compound" "$LOCK" "cd x && git -C /r checkout other" "block"
|
|
55
|
+
expect_verdict "block/-C reset --hard" "$LOCK" "git -C /repo reset --hard" "block"
|
|
56
|
+
expect_verdict "block/-C stash" "$LOCK" "git -C /repo stash" "block"
|
|
57
|
+
|
|
58
|
+
# ===========================================================================
|
|
59
|
+
# ALLOW — same-branch / legitimate operations
|
|
60
|
+
# ===========================================================================
|
|
61
|
+
expect_verdict "allow/checkout lock target" "$LOCK" "git checkout main" "allow"
|
|
62
|
+
expect_verdict "allow/switch lock target" "$LOCK" "git switch main" "allow"
|
|
63
|
+
expect_verdict "allow/file restore --" "$LOCK" "git checkout -- src/app.ts" "allow"
|
|
64
|
+
expect_verdict "allow/file restore -- multi" "$LOCK" "git checkout -- a.txt b.txt" "allow"
|
|
65
|
+
expect_verdict "allow/bare checkout" "$LOCK" "git checkout" "allow"
|
|
66
|
+
expect_verdict "allow/worktree add" "$LOCK" "git worktree add ../wt branch" "allow"
|
|
67
|
+
expect_verdict "allow/non-checkout git" "$LOCK" "git status" "allow"
|
|
68
|
+
expect_verdict "allow/non-git command" "$LOCK" "ls -la" "allow"
|
|
69
|
+
expect_verdict "allow/commit" "$LOCK" "git commit -m wip" "allow"
|
|
70
|
+
|
|
71
|
+
# global options before the subcommand: legitimate operations still allowed
|
|
72
|
+
expect_verdict "allow/-C checkout lock" "$LOCK" "git -C /repo checkout main" "allow"
|
|
73
|
+
expect_verdict "allow/-C status" "$LOCK" "git -C /repo status" "allow"
|
|
74
|
+
expect_verdict "allow/-C file restore" "$LOCK" "git -C /repo checkout -- a.ts" "allow"
|
|
75
|
+
expect_verdict "allow/-C worktree add" "$LOCK" "git -C /repo worktree add ../w" "allow"
|
|
76
|
+
|
|
77
|
+
# slash lock target: switching back to it is allowed, elsewhere blocked
|
|
78
|
+
expect_verdict "allow/slash lock back" "feature/x" "git switch feature/x" "allow"
|
|
79
|
+
expect_verdict "block/slash other" "feature/x" "git switch feature/y" "block"
|
|
80
|
+
|
|
81
|
+
# ===========================================================================
|
|
82
|
+
# BLOCK — the work-loss family (issue #61)
|
|
83
|
+
# ===========================================================================
|
|
84
|
+
expect_verdict "block/stash bare" "$LOCK" "git stash" "block"
|
|
85
|
+
expect_verdict "block/stash push" "$LOCK" "git stash push" "block"
|
|
86
|
+
expect_verdict "block/stash push paths" "$LOCK" "git stash push -m x src/" "block"
|
|
87
|
+
expect_verdict "block/stash save" "$LOCK" "git stash save wip" "block"
|
|
88
|
+
expect_verdict "block/clean -f" "$LOCK" "git clean -f" "block"
|
|
89
|
+
expect_verdict "block/clean -fd" "$LOCK" "git clean -fd" "block"
|
|
90
|
+
expect_verdict "block/clean -xfd" "$LOCK" "git clean -xfd" "block"
|
|
91
|
+
expect_verdict "block/clean --force" "$LOCK" "git clean --force" "block"
|
|
92
|
+
expect_verdict "block/reset --hard" "$LOCK" "git reset --hard" "block"
|
|
93
|
+
expect_verdict "block/reset --hard HEAD~1" "$LOCK" "git reset --hard HEAD~1" "block"
|
|
94
|
+
expect_verdict "block/checkout dot" "$LOCK" "git checkout ." "block"
|
|
95
|
+
expect_verdict "block/restore dot" "$LOCK" "git restore ." "block"
|
|
96
|
+
expect_verdict "block/checkout dashdash dot" "$LOCK" "git checkout -- ." "block"
|
|
97
|
+
expect_verdict "block/family compound" "$LOCK" "cd repo && git reset --hard" "block"
|
|
98
|
+
|
|
99
|
+
# ===========================================================================
|
|
100
|
+
# ALLOW — non-destructive members of the same command families
|
|
101
|
+
# ===========================================================================
|
|
102
|
+
expect_verdict "allow/stash list" "$LOCK" "git stash list" "allow"
|
|
103
|
+
expect_verdict "allow/stash show" "$LOCK" "git stash show" "allow"
|
|
104
|
+
expect_verdict "allow/clean dry-run" "$LOCK" "git clean -n" "allow"
|
|
105
|
+
expect_verdict "allow/clean --dry-run" "$LOCK" "git clean --dry-run" "allow"
|
|
106
|
+
expect_verdict "allow/reset soft" "$LOCK" "git reset --soft HEAD~1" "allow"
|
|
107
|
+
expect_verdict "allow/reset mixed default" "$LOCK" "git reset HEAD file.txt" "allow"
|
|
108
|
+
expect_verdict "allow/restore staged" "$LOCK" "git restore --staged f.txt" "allow"
|
|
109
|
+
expect_verdict "allow/restore single file" "$LOCK" "git restore src/app.ts" "allow"
|
|
110
|
+
|
|
111
|
+
# ===========================================================================
|
|
112
|
+
# PRIMARY BRANCH GUARD — dev.lock.primary-branch (issue #396 / ADR 0043)
|
|
113
|
+
# Table: command, cwd-is-primary, flag-state => allow/block.
|
|
114
|
+
# ===========================================================================
|
|
115
|
+
expect_primary_guard() {
|
|
116
|
+
local label="$1" cmd="$2" primary="$3" flag="$4" expected="$5"
|
|
117
|
+
local actual="allow"
|
|
118
|
+
if [[ "$primary" == "primary" && "$flag" == "on" ]]; then
|
|
119
|
+
actual="$(classify_primary_branch_switch_guard "$cmd")"
|
|
120
|
+
fi
|
|
121
|
+
if [[ "$actual" == "$expected" ]]; then
|
|
122
|
+
echo "PASS $label"; pass=$((pass + 1))
|
|
123
|
+
else
|
|
124
|
+
printf 'FAIL %s\n cmd: %q\n primary: %q\n flag: %q\n expected: %q\n actual: %q\n' \
|
|
125
|
+
"$label" "$cmd" "$primary" "$flag" "$expected" "$actual"
|
|
126
|
+
fail=$((fail + 1))
|
|
127
|
+
fi
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
expect_primary_guard "primary/on/switch other blocked" "git switch other" primary on block
|
|
131
|
+
expect_primary_guard "primary/on/checkout other blocked" "git checkout other" primary on block
|
|
132
|
+
expect_primary_guard "primary/on/switch -b new blocked" "git switch -b new" primary on block
|
|
133
|
+
expect_primary_guard "primary/on/checkout -b new blocked" "git checkout -b new" primary on block
|
|
134
|
+
expect_primary_guard "primary/on/compound switch blocked" "cd repo && git switch other" primary on block
|
|
135
|
+
expect_primary_guard "primary/on/-C switch blocked" "git -C /repo switch other" primary on block
|
|
136
|
+
expect_primary_guard "primary/on/-C checkout -b blocked" "git -C /repo checkout -b new" primary on block
|
|
137
|
+
expect_primary_guard "primary/on/switch dash blocked" "git switch -" primary on block
|
|
138
|
+
expect_primary_guard "primary/on/commit allowed" "git commit -m wip" primary on allow
|
|
139
|
+
expect_primary_guard "primary/on/worktree add allowed" "git worktree add ../wt other" primary on allow
|
|
140
|
+
expect_primary_guard "primary/on/status allowed" "git status" primary on allow
|
|
141
|
+
expect_primary_guard "primary/on/checkout path allowed" "git checkout -- src/app.ts" primary on allow
|
|
142
|
+
expect_primary_guard "primary/on/checkout dot allowed" "git checkout ." primary on allow
|
|
143
|
+
expect_primary_guard "primary/off/switch allowed" "git switch other" primary off allow
|
|
144
|
+
expect_primary_guard "worktree/on/switch allowed" "git switch other" worktree on allow
|
|
145
|
+
expect_primary_guard "worktree/off/switch allowed" "git switch other" worktree off allow
|
|
146
|
+
|
|
147
|
+
# ---------------------------------------------------------------------------
|
|
148
|
+
# issue #1024: block git reset (any form) and git stash (all subcommands),
|
|
149
|
+
# plus git rebase --autostash, in the primary checkout. Parallel human WIP
|
|
150
|
+
# lives there; these commands have destroyed in-progress work before.
|
|
151
|
+
# ---------------------------------------------------------------------------
|
|
152
|
+
expect_primary_guard "primary/on/reset bare blocked" "git reset" primary on block
|
|
153
|
+
expect_primary_guard "primary/on/reset --hard blocked" "git reset --hard" primary on block
|
|
154
|
+
expect_primary_guard "primary/on/reset --hard HEAD~1 blocked" "git reset --hard HEAD~1" primary on block
|
|
155
|
+
expect_primary_guard "primary/on/reset --soft blocked" "git reset --soft HEAD~1" primary on block
|
|
156
|
+
expect_primary_guard "primary/on/reset --mixed blocked" "git reset --mixed" primary on block
|
|
157
|
+
expect_primary_guard "primary/on/reset pathspec blocked" "git reset HEAD file.txt" primary on block
|
|
158
|
+
expect_primary_guard "primary/on/-C reset blocked" "git -C /repo reset --hard" primary on block
|
|
159
|
+
expect_primary_guard "primary/on/compound reset blocked" "cd repo && git reset --hard" primary on block
|
|
160
|
+
|
|
161
|
+
expect_primary_guard "primary/on/stash bare blocked" "git stash" primary on block
|
|
162
|
+
expect_primary_guard "primary/on/stash push blocked" "git stash push" primary on block
|
|
163
|
+
expect_primary_guard "primary/on/stash push paths blocked" "git stash push -m x src/" primary on block
|
|
164
|
+
expect_primary_guard "primary/on/stash save blocked" "git stash save wip" primary on block
|
|
165
|
+
expect_primary_guard "primary/on/stash pop blocked" "git stash pop" primary on block
|
|
166
|
+
expect_primary_guard "primary/on/stash apply blocked" "git stash apply" primary on block
|
|
167
|
+
expect_primary_guard "primary/on/stash list blocked" "git stash list" primary on block
|
|
168
|
+
expect_primary_guard "primary/on/stash show blocked" "git stash show" primary on block
|
|
169
|
+
expect_primary_guard "primary/on/-C stash blocked" "git -C /repo stash" primary on block
|
|
170
|
+
|
|
171
|
+
expect_primary_guard "primary/on/rebase autostash blocked" "git rebase --autostash" primary on block
|
|
172
|
+
expect_primary_guard "primary/on/rebase autostash arg blocked" "git rebase --autostash origin/main" primary on block
|
|
173
|
+
expect_primary_guard "primary/on/rebase main autostash blocked" "git rebase main --autostash" primary on block
|
|
174
|
+
|
|
175
|
+
# rebase without --autostash is not the work-loss vector this rule targets
|
|
176
|
+
expect_primary_guard "primary/on/rebase plain allowed" "git rebase main" primary on allow
|
|
177
|
+
expect_primary_guard "primary/on/rebase --no-autostash allowed" "git rebase --no-autostash main" primary on allow
|
|
178
|
+
|
|
179
|
+
# worktrees stay exempt (scope), flag-off stays inert — for reset/stash too
|
|
180
|
+
expect_primary_guard "worktree/on/reset allowed" "git reset --hard" worktree on allow
|
|
181
|
+
expect_primary_guard "worktree/on/stash allowed" "git stash" worktree on allow
|
|
182
|
+
expect_primary_guard "primary/off/reset allowed" "git reset --hard" primary off allow
|
|
183
|
+
expect_primary_guard "primary/off/stash allowed" "git stash" primary off allow
|
|
184
|
+
|
|
185
|
+
echo
|
|
186
|
+
echo "summary: $pass passed, $fail failed"
|
|
187
|
+
[ "$fail" -eq 0 ]
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Unit test for lib/lock-store.sh (issue #60).
|
|
3
|
+
#
|
|
4
|
+
# Sources the module directly (no globals) and exercises all four entry points
|
|
5
|
+
# against temp lock files:
|
|
6
|
+
# - write/read round-trip, including a branch name with a slash
|
|
7
|
+
# - absent file => unlocked (read rc 1, no output; is_locked rc 1)
|
|
8
|
+
# - clear removes the file and is idempotent
|
|
9
|
+
# - write is atomic and overwrites (relock to a new target)
|
|
10
|
+
|
|
11
|
+
set -uo pipefail
|
|
12
|
+
|
|
13
|
+
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
14
|
+
LIB="$(dirname "$HERE")/lib/lock-store.sh"
|
|
15
|
+
|
|
16
|
+
# shellcheck source=../lib/lock-store.sh
|
|
17
|
+
source "$LIB"
|
|
18
|
+
|
|
19
|
+
pass=0
|
|
20
|
+
fail=0
|
|
21
|
+
|
|
22
|
+
expect_eq() {
|
|
23
|
+
local label="$1" expected="$2" actual="$3"
|
|
24
|
+
if [[ "$expected" == "$actual" ]]; then
|
|
25
|
+
echo "PASS $label"; pass=$((pass + 1))
|
|
26
|
+
else
|
|
27
|
+
printf 'FAIL %s\n expected: %q\n actual: %q\n' "$label" "$expected" "$actual"
|
|
28
|
+
fail=$((fail + 1))
|
|
29
|
+
fi
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# ===========================================================================
|
|
33
|
+
# absent file => unlocked
|
|
34
|
+
# ===========================================================================
|
|
35
|
+
tmpdir="$(mktemp -d)"
|
|
36
|
+
lock="$tmpdir/.red/tmp/branch-lock.yaml"
|
|
37
|
+
|
|
38
|
+
out="$(lock_store_read "$lock")"; rc=$?
|
|
39
|
+
expect_eq "absent/read rc 1" "1" "$rc"
|
|
40
|
+
expect_eq "absent/read silent" "" "$out"
|
|
41
|
+
lock_store_is_locked "$lock"; rc=$?
|
|
42
|
+
expect_eq "absent/is_locked rc 1" "1" "$rc"
|
|
43
|
+
|
|
44
|
+
# ===========================================================================
|
|
45
|
+
# write / read round-trip (parent dir created, slash branch preserved)
|
|
46
|
+
# ===========================================================================
|
|
47
|
+
lock_store_write "$lock" "feature/branch-lock"; rc=$?
|
|
48
|
+
expect_eq "write/rc 0" "0" "$rc"
|
|
49
|
+
expect_eq "write/file exists" "1" "$([[ -f "$lock" ]] && echo 1 || echo 0)"
|
|
50
|
+
expect_eq "write/read back" "feature/branch-lock" "$(lock_store_read "$lock")"
|
|
51
|
+
lock_store_is_locked "$lock"; rc=$?
|
|
52
|
+
expect_eq "write/is_locked rc 0" "0" "$rc"
|
|
53
|
+
# stored content is a single line (no trailing whitespace in the value)
|
|
54
|
+
expect_eq "write/single line" "1" "$(wc -l < "$lock" | tr -d ' ')"
|
|
55
|
+
|
|
56
|
+
# ===========================================================================
|
|
57
|
+
# write overwrites (relock to a new target)
|
|
58
|
+
# ===========================================================================
|
|
59
|
+
lock_store_write "$lock" "main"
|
|
60
|
+
expect_eq "relock/read new target" "main" "$(lock_store_read "$lock")"
|
|
61
|
+
|
|
62
|
+
# missing arg => rc 2, file untouched
|
|
63
|
+
lock_store_write "$lock" "" 2>/dev/null; rc=$?
|
|
64
|
+
expect_eq "write/missing branch rc 2" "2" "$rc"
|
|
65
|
+
expect_eq "write/untouched after bad" "main" "$(lock_store_read "$lock")"
|
|
66
|
+
|
|
67
|
+
# ===========================================================================
|
|
68
|
+
# clear removes the file and is idempotent
|
|
69
|
+
# ===========================================================================
|
|
70
|
+
lock_store_clear "$lock"; rc=$?
|
|
71
|
+
expect_eq "clear/rc 0" "0" "$rc"
|
|
72
|
+
expect_eq "clear/file gone" "0" "$([[ -f "$lock" ]] && echo 1 || echo 0)"
|
|
73
|
+
lock_store_clear "$lock"; rc=$?
|
|
74
|
+
expect_eq "clear/idempotent rc 0" "0" "$rc"
|
|
75
|
+
lock_store_is_locked "$lock"; rc=$?
|
|
76
|
+
expect_eq "clear/is_locked rc 1" "1" "$rc"
|
|
77
|
+
|
|
78
|
+
# ===========================================================================
|
|
79
|
+
# an empty lock file reads as unlocked (defensive: half-written / truncated)
|
|
80
|
+
# ===========================================================================
|
|
81
|
+
mkdir -p "$(dirname "$lock")"; : > "$lock"
|
|
82
|
+
out="$(lock_store_read "$lock")"; rc=$?
|
|
83
|
+
expect_eq "empty/read rc 1" "1" "$rc"
|
|
84
|
+
expect_eq "empty/read silent" "" "$out"
|
|
85
|
+
|
|
86
|
+
rm -rf "$tmpdir"
|
|
87
|
+
|
|
88
|
+
echo
|
|
89
|
+
echo "summary: $pass passed, $fail failed"
|
|
90
|
+
[ "$fail" -eq 0 ]
|