forge-workflow 0.0.9 → 0.0.10
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/.claude/commands/dev.md +2 -2
- package/.claude/commands/plan.md +2 -2
- package/.claude/commands/ship.md +2 -2
- package/.claude/commands/status.md +4 -4
- package/.cline/workflows/dev.md +2 -2
- package/.cline/workflows/plan.md +2 -2
- package/.cline/workflows/ship.md +2 -2
- package/.cline/workflows/status.md +4 -4
- package/.codex/skills/dev/SKILL.md +2 -2
- package/.codex/skills/plan/SKILL.md +2 -2
- package/.codex/skills/ship/SKILL.md +2 -2
- package/.codex/skills/status/SKILL.md +4 -4
- package/.cursor/commands/dev.md +2 -2
- package/.cursor/commands/plan.md +2 -2
- package/.cursor/commands/ship.md +2 -2
- package/.cursor/commands/status.md +4 -4
- package/.github/prompts/dev.prompt.md +2 -2
- package/.github/prompts/plan.prompt.md +2 -2
- package/.github/prompts/ship.prompt.md +2 -2
- package/.github/prompts/status.prompt.md +4 -4
- package/.github/workflows/beads-to-github.yml +43 -10
- package/.github/workflows/github-to-beads.yml +10 -7
- package/.kilocode/workflows/dev.md +2 -2
- package/.kilocode/workflows/plan.md +2 -2
- package/.kilocode/workflows/ship.md +2 -2
- package/.kilocode/workflows/status.md +4 -4
- package/.opencode/commands/dev.md +2 -2
- package/.opencode/commands/plan.md +2 -2
- package/.opencode/commands/ship.md +2 -2
- package/.opencode/commands/status.md +4 -4
- package/.roo/commands/dev.md +2 -2
- package/.roo/commands/plan.md +2 -2
- package/.roo/commands/ship.md +2 -2
- package/.roo/commands/status.md +4 -4
- package/AGENTS.md +1 -0
- package/CLAUDE.md +12 -0
- package/bin/forge.js +10 -5
- package/docs/BEADS_GITHUB_SYNC.md +26 -0
- package/docs/TOOLCHAIN.md +130 -148
- package/lib/beads-bootstrap.js +225 -0
- package/lib/beads-health-check.js +55 -10
- package/lib/beads-setup.js +104 -28
- package/lib/beads-sync-scaffold.js +11 -6
- package/lib/commands/_issue.js +11 -1
- package/lib/commands/issues.js +49 -0
- package/lib/commands/recommend.js +22 -1
- package/lib/commands/setup.js +16 -10
- package/lib/commands/status.js +181 -0
- package/lib/commands/team.js +11 -1
- package/lib/commands/test.js +37 -2
- package/lib/commands/validate.js +14 -8
- package/lib/commands/worktree.js +27 -54
- package/lib/dep-guard/keyword-ripple.js +184 -0
- package/lib/detect-worktree.js +9 -10
- package/lib/forge-issues.js +326 -0
- package/lib/issue-sync/authority.js +100 -0
- package/lib/issue-sync/github-pull.js +184 -0
- package/lib/issue-sync/import-primitives.js +98 -0
- package/lib/issue-sync/legacy-link-bridge.js +436 -0
- package/lib/issue-sync/link-store.js +292 -0
- package/lib/issue-sync/project-github.js +123 -0
- package/lib/issue-sync/reconcile.js +195 -0
- package/lib/issue-sync/schema.js +126 -0
- package/lib/lefthook-check.js +5 -2
- package/lib/project-memory.js +564 -0
- package/lib/runtime-health.js +100 -12
- package/lib/smart-status/conflicts.js +205 -0
- package/lib/smart-status/scoring.js +177 -0
- package/lib/status/beads-snapshot.js +102 -0
- package/lib/status/presenter.js +65 -0
- package/lib/workflow/enforce-stage.js +3 -1
- package/lib/workflow/state-manager.js +164 -8
- package/package.json +12 -4
- package/scripts/beads-context.sh +124 -5
- package/scripts/beads-context.test.js +21 -4
- package/scripts/beads-migrate-to-dolt.sh +7 -0
- package/scripts/beads-upgrade-smoke.sh +263 -0
- package/scripts/behavioral-judge.sh +115 -11
- package/scripts/benchmark.js +349 -63
- package/scripts/dep-guard-analyze.js +52 -17
- package/scripts/dep-guard-keyword-ripple.js +29 -0
- package/scripts/dep-guard-render-review.js +86 -0
- package/scripts/dep-guard.sh +45 -232
- package/scripts/forge-team/lib/sync-github.sh +160 -28
- package/scripts/forge-team/tests/sync-github.test.sh +195 -58
- package/scripts/github-beads-sync/index.mjs +122 -98
- package/scripts/github-beads-sync/mapping.mjs +54 -0
- package/scripts/github-beads-sync/reverse-sync-cli.mjs +2 -2
- package/scripts/github-beads-sync/reverse-sync.mjs +31 -7
- package/scripts/lib/beads-migrate-to-dolt.mjs +503 -0
- package/scripts/preflight.sh +181 -0
- package/scripts/smart-status-score.js +31 -0
- package/scripts/smart-status-sessions.js +51 -0
- package/scripts/smart-status.sh +74 -329
- package/scripts/sync-agentic-workflow.js +48 -0
- package/scripts/test-ci-shard.js +244 -0
- package/scripts/test-dashboard.js +188 -52
- package/scripts/test-full-suite.js +186 -0
- package/scripts/test-profile.js +278 -0
- package/scripts/test.js +219 -28
- package/scripts/validate.js +143 -0
- package/scripts/validate.sh +18 -1
|
@@ -3,16 +3,17 @@ set -euo pipefail
|
|
|
3
3
|
|
|
4
4
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
5
|
|
|
6
|
-
# ── Setup temp dir for test isolation ────────────────────────────────────
|
|
7
6
|
TEST_TMP="$(mktemp -d)"
|
|
8
7
|
trap 'rm -rf "$TEST_TMP" "$mock_dir"' EXIT
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
mock_dir="$(mktemp -d)"
|
|
10
|
+
portable_bin="$(mktemp -d)"
|
|
12
11
|
log_file="$mock_dir/calls.log"
|
|
13
12
|
touch "$log_file"
|
|
13
|
+
REAL_GREP="$(command -v grep)"
|
|
14
|
+
ORIGINAL_PATH="$PATH"
|
|
15
|
+
export REAL_GREP
|
|
14
16
|
|
|
15
|
-
# ── Mock gh ──────────────────────────────────────────────────────────────
|
|
16
17
|
cat > "$mock_dir/gh" << 'MOCK'
|
|
17
18
|
#!/usr/bin/env bash
|
|
18
19
|
echo "$*" >> "$LOG_FILE"
|
|
@@ -23,45 +24,69 @@ case "$1 $2" in
|
|
|
23
24
|
"issue comment") echo ""; exit 0 ;;
|
|
24
25
|
"api user") echo "testuser"; exit 0 ;;
|
|
25
26
|
esac
|
|
26
|
-
echo "unknown gh command: $*" >&2
|
|
27
|
+
echo "unknown gh command: $*" >&2
|
|
28
|
+
exit 1
|
|
27
29
|
MOCK
|
|
28
30
|
chmod +x "$mock_dir/gh"
|
|
29
31
|
|
|
30
|
-
# ── Mock bd ──────────────────────────────────────────────────────────────
|
|
31
32
|
cat > "$mock_dir/bd" << 'MOCK'
|
|
32
33
|
#!/usr/bin/env bash
|
|
33
34
|
echo "$*" >> "$LOG_FILE"
|
|
34
35
|
case "$1" in
|
|
35
36
|
show)
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
if [[ "${3:-}" == "--json" ]]; then
|
|
38
|
+
cat <<'JSON'
|
|
39
|
+
{"id":"beads-001","github":{"number":42,"nodeId":"I_kwDOForge42","url":"https://github.com/test/repo/issues/42"},"shared":{"title":"Test Issue"}}
|
|
40
|
+
JSON
|
|
41
|
+
else
|
|
42
|
+
echo "o $2 - Test Issue"
|
|
43
|
+
echo "Title: Test Issue"
|
|
44
|
+
fi
|
|
38
45
|
;;
|
|
39
46
|
set-state) echo "State set" ;;
|
|
40
47
|
esac
|
|
41
48
|
MOCK
|
|
42
49
|
chmod +x "$mock_dir/bd"
|
|
43
50
|
|
|
44
|
-
# ── Mock bd that returns NO github_issue ─────────────────────────────────
|
|
45
51
|
cat > "$mock_dir/bd-no-gh" << 'MOCK'
|
|
46
52
|
#!/usr/bin/env bash
|
|
47
53
|
echo "$*" >> "$LOG_FILE"
|
|
48
54
|
case "$1" in
|
|
49
55
|
show)
|
|
50
|
-
|
|
51
|
-
|
|
56
|
+
if [[ "${3:-}" == "--json" ]]; then
|
|
57
|
+
cat <<'JSON'
|
|
58
|
+
{"id":"beads-999","shared":{"title":"Test Issue"}}
|
|
59
|
+
JSON
|
|
60
|
+
else
|
|
61
|
+
echo "o $2 - Test Issue"
|
|
62
|
+
echo "Title: Test Issue"
|
|
63
|
+
fi
|
|
52
64
|
;;
|
|
53
65
|
set-state) echo "State set" ;;
|
|
54
66
|
esac
|
|
55
67
|
MOCK
|
|
56
68
|
chmod +x "$mock_dir/bd-no-gh"
|
|
57
69
|
|
|
58
|
-
|
|
70
|
+
cat > "$portable_bin/grep" << 'MOCK'
|
|
71
|
+
#!/usr/bin/env bash
|
|
72
|
+
for arg in "$@"; do
|
|
73
|
+
case "$arg" in
|
|
74
|
+
-P|--perl-regexp|-*P*)
|
|
75
|
+
echo "grep: PCRE support disabled for test" >&2
|
|
76
|
+
exit 2
|
|
77
|
+
;;
|
|
78
|
+
esac
|
|
79
|
+
done
|
|
80
|
+
exec "$REAL_GREP" "$@"
|
|
81
|
+
MOCK
|
|
82
|
+
chmod +x "$portable_bin/grep"
|
|
83
|
+
|
|
59
84
|
export LOG_FILE="$log_file"
|
|
60
85
|
export GH_CMD="$mock_dir/gh"
|
|
61
86
|
export BD_CMD="$mock_dir/bd"
|
|
62
87
|
export TEAM_MAP_ROOT="$TEST_TMP"
|
|
88
|
+
mapping_file="$TEAM_MAP_ROOT/.github/beads-mapping.json"
|
|
63
89
|
|
|
64
|
-
# Source the library under test
|
|
65
90
|
source "$SCRIPT_DIR/lib/sync-github.sh"
|
|
66
91
|
|
|
67
92
|
PASS=0
|
|
@@ -70,119 +95,193 @@ FAIL=0
|
|
|
70
95
|
assert_eq() {
|
|
71
96
|
local label="$1" expected="$2" actual="$3"
|
|
72
97
|
if [[ "$actual" == "$expected" ]]; then
|
|
73
|
-
PASS=$((PASS + 1))
|
|
98
|
+
PASS=$((PASS + 1))
|
|
99
|
+
echo " PASS: $label"
|
|
74
100
|
else
|
|
75
|
-
FAIL=$((FAIL + 1))
|
|
101
|
+
FAIL=$((FAIL + 1))
|
|
102
|
+
echo " FAIL: $label (expected '$expected', got '$actual')"
|
|
76
103
|
fi
|
|
77
104
|
}
|
|
78
105
|
|
|
79
106
|
assert_contains() {
|
|
80
107
|
local label="$1" expected="$2" actual="$3"
|
|
81
108
|
if [[ "$actual" == *"$expected"* ]]; then
|
|
82
|
-
PASS=$((PASS + 1))
|
|
109
|
+
PASS=$((PASS + 1))
|
|
110
|
+
echo " PASS: $label"
|
|
111
|
+
else
|
|
112
|
+
FAIL=$((FAIL + 1))
|
|
113
|
+
echo " FAIL: $label (expected to contain '$expected', got '$actual')"
|
|
114
|
+
fi
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
assert_not_contains() {
|
|
118
|
+
local label="$1" unexpected="$2" actual="$3"
|
|
119
|
+
if [[ "$actual" == *"$unexpected"* ]]; then
|
|
120
|
+
FAIL=$((FAIL + 1))
|
|
121
|
+
echo " FAIL: $label (did not expect '$unexpected', got '$actual')"
|
|
83
122
|
else
|
|
84
|
-
|
|
123
|
+
PASS=$((PASS + 1))
|
|
124
|
+
echo " PASS: $label"
|
|
85
125
|
fi
|
|
86
126
|
}
|
|
87
127
|
|
|
88
128
|
assert_exit() {
|
|
89
129
|
local label="$1" expected="$2" actual="$3"
|
|
90
130
|
if [[ "$actual" -eq "$expected" ]]; then
|
|
91
|
-
PASS=$((PASS + 1))
|
|
131
|
+
PASS=$((PASS + 1))
|
|
132
|
+
echo " PASS: $label"
|
|
92
133
|
else
|
|
93
|
-
FAIL=$((FAIL + 1))
|
|
134
|
+
FAIL=$((FAIL + 1))
|
|
135
|
+
echo " FAIL: $label (expected exit $expected, got $actual)"
|
|
94
136
|
fi
|
|
95
137
|
}
|
|
96
138
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
> "$log_file" # clear log
|
|
139
|
+
echo "== Test 1: sync_issue_create =="
|
|
140
|
+
> "$log_file"
|
|
100
141
|
rc=0
|
|
101
142
|
sync_issue_create "beads-001" || rc=$?
|
|
102
143
|
assert_exit "exit code 0" 0 "$rc"
|
|
103
|
-
# Check gh was called with issue create
|
|
104
144
|
log_contents="$(cat "$log_file")"
|
|
105
145
|
assert_contains "gh issue create called" "issue create" "$log_contents"
|
|
106
146
|
assert_contains "title includes Test Issue" "Test Issue" "$log_contents"
|
|
107
147
|
assert_contains "body includes beads-001" "beads-001" "$log_contents"
|
|
108
|
-
|
|
109
|
-
assert_contains "
|
|
110
|
-
assert_contains "
|
|
148
|
+
assert_contains "sync create writes legacy github_issue state" "github_issue=42" "$log_contents"
|
|
149
|
+
assert_contains "sync create calls set-state for hook compatibility" "set-state" "$log_contents"
|
|
150
|
+
assert_contains "sync create persists mapping entry" '"42": "beads-001"' "$(cat "$mapping_file")"
|
|
111
151
|
|
|
112
|
-
|
|
113
|
-
echo ""
|
|
114
|
-
echo "── Test 2: sync_issue_claim ──"
|
|
152
|
+
echo
|
|
153
|
+
echo "== Test 2: sync_issue_claim =="
|
|
115
154
|
> "$log_file"
|
|
116
155
|
unset _GITHUB_USER_CACHE
|
|
117
156
|
rc=0
|
|
118
157
|
sync_issue_claim "beads-001" || rc=$?
|
|
119
158
|
assert_exit "exit code 0" 0 "$rc"
|
|
120
159
|
log_contents="$(cat "$log_file")"
|
|
160
|
+
assert_contains "canonical lookup uses --json" "--json" "$log_contents"
|
|
121
161
|
assert_contains "gh issue edit called" "issue edit" "$log_contents"
|
|
122
162
|
assert_contains "add-assignee used" "--add-assignee" "$log_contents"
|
|
123
163
|
assert_contains "assignee is testuser" "testuser" "$log_contents"
|
|
124
164
|
|
|
125
|
-
|
|
126
|
-
echo ""
|
|
127
|
-
echo "── Test 3: sync_issue_status ──"
|
|
165
|
+
echo
|
|
166
|
+
echo "== Test 3: sync_issue_status =="
|
|
128
167
|
> "$log_file"
|
|
129
168
|
rc=0
|
|
130
169
|
sync_issue_status "beads-001" "in_progress" || rc=$?
|
|
131
170
|
assert_exit "exit code 0" 0 "$rc"
|
|
132
171
|
log_contents="$(cat "$log_file")"
|
|
172
|
+
assert_contains "canonical lookup uses --json" "--json" "$log_contents"
|
|
133
173
|
assert_contains "gh issue edit called" "issue edit" "$log_contents"
|
|
134
174
|
assert_contains "removes status/open" "--remove-label status/open" "$log_contents"
|
|
135
175
|
assert_contains "adds status/in-progress" "--add-label status/in-progress" "$log_contents"
|
|
136
176
|
|
|
137
|
-
|
|
138
|
-
echo ""
|
|
139
|
-
echo "── Test 4: sync_issue_close ──"
|
|
177
|
+
echo
|
|
178
|
+
echo "== Test 4: sync_issue_close =="
|
|
140
179
|
> "$log_file"
|
|
141
180
|
rc=0
|
|
142
181
|
sync_issue_close "beads-001" || rc=$?
|
|
143
182
|
assert_exit "exit code 0" 0 "$rc"
|
|
144
183
|
log_contents="$(cat "$log_file")"
|
|
184
|
+
assert_contains "canonical lookup uses --json" "--json" "$log_contents"
|
|
145
185
|
assert_contains "gh issue close called" "issue close" "$log_contents"
|
|
146
186
|
assert_contains "closes issue 42" " 42" "$log_contents"
|
|
147
187
|
|
|
148
|
-
|
|
149
|
-
echo ""
|
|
150
|
-
echo "── Test 5: sync_issue_deps ──"
|
|
188
|
+
echo
|
|
189
|
+
echo "== Test 5: sync_issue_deps =="
|
|
151
190
|
> "$log_file"
|
|
152
191
|
rc=0
|
|
153
192
|
sync_issue_deps "beads-001" "beads-002" || rc=$?
|
|
154
193
|
assert_exit "exit code 0" 0 "$rc"
|
|
155
194
|
log_contents="$(cat "$log_file")"
|
|
195
|
+
assert_contains "canonical lookup uses --json" "--json" "$log_contents"
|
|
156
196
|
assert_contains "gh issue comment called" "issue comment" "$log_contents"
|
|
157
197
|
assert_contains "Blocked by referenced" "Blocked by #42" "$log_contents"
|
|
158
198
|
|
|
159
|
-
|
|
160
|
-
echo ""
|
|
161
|
-
echo "── Test 6: _get_github_issue_number ──"
|
|
199
|
+
echo
|
|
200
|
+
echo "== Test 6: _get_github_issue_number =="
|
|
162
201
|
result="$(_get_github_issue_number "beads-001")"
|
|
163
202
|
assert_eq "extracts issue number 42" "42" "$result"
|
|
203
|
+
log_contents="$(cat "$log_file")"
|
|
204
|
+
assert_contains "_get_github_issue_number uses JSON" "--json" "$log_contents"
|
|
164
205
|
|
|
165
|
-
|
|
166
|
-
echo ""
|
|
167
|
-
|
|
206
|
+
echo
|
|
207
|
+
echo "== Test 7: Mapping fallback when canonical GitHub number missing =="
|
|
208
|
+
mkdir -p "$(dirname "$mapping_file")"
|
|
209
|
+
cat > "$mapping_file" << 'JSON'
|
|
210
|
+
{
|
|
211
|
+
"77": "beads-999"
|
|
212
|
+
}
|
|
213
|
+
JSON
|
|
214
|
+
export BD_CMD="$mock_dir/bd-no-gh"
|
|
215
|
+
result="$(_get_github_issue_number "beads-999")"
|
|
216
|
+
assert_eq "mapping fallback returns issue number 77" "77" "$result"
|
|
217
|
+
export BD_CMD="$mock_dir/bd"
|
|
218
|
+
|
|
219
|
+
echo
|
|
220
|
+
echo "== Test 8: Mapping fallback returns newest issue number =="
|
|
221
|
+
cat > "$mapping_file" << 'JSON'
|
|
222
|
+
{
|
|
223
|
+
"77": "beads-999",
|
|
224
|
+
"91": "beads-999",
|
|
225
|
+
"88": "beads-other"
|
|
226
|
+
}
|
|
227
|
+
JSON
|
|
228
|
+
export BD_CMD="$mock_dir/bd-no-gh"
|
|
229
|
+
result="$(_get_github_issue_number "beads-999")"
|
|
230
|
+
assert_eq "mapping fallback returns newest matching issue number" "91" "$result"
|
|
231
|
+
export BD_CMD="$mock_dir/bd"
|
|
232
|
+
|
|
233
|
+
echo
|
|
234
|
+
echo "== Test 9: Missing canonical GitHub number =="
|
|
235
|
+
rm -f "$mapping_file"
|
|
168
236
|
export BD_CMD="$mock_dir/bd-no-gh"
|
|
169
237
|
rc=0
|
|
170
238
|
result="$(_get_github_issue_number "beads-999" 2>/dev/null)" || rc=$?
|
|
171
|
-
assert_exit "returns error when no
|
|
172
|
-
|
|
239
|
+
assert_exit "returns error when no canonical github number" 1 "$rc"
|
|
240
|
+
export BD_CMD="$mock_dir/bd"
|
|
241
|
+
|
|
242
|
+
echo
|
|
243
|
+
echo "== Test 10: Legacy github_issue state fallback =="
|
|
244
|
+
cat > "$mock_dir/bd-legacy" << 'MOCK'
|
|
245
|
+
#!/usr/bin/env bash
|
|
246
|
+
echo "$*" >> "$LOG_FILE"
|
|
247
|
+
case "$1" in
|
|
248
|
+
show)
|
|
249
|
+
if [[ "${3:-}" == "--json" ]]; then
|
|
250
|
+
cat <<'JSON'
|
|
251
|
+
{"id":"beads-legacy","github_issue":43,"shared":{"title":"Legacy Issue"}}
|
|
252
|
+
JSON
|
|
253
|
+
else
|
|
254
|
+
echo "o $2 - Legacy Issue"
|
|
255
|
+
echo "github_issue:43"
|
|
256
|
+
echo "Title: Legacy Issue"
|
|
257
|
+
fi
|
|
258
|
+
;;
|
|
259
|
+
set-state) echo "State set" ;;
|
|
260
|
+
esac
|
|
261
|
+
MOCK
|
|
262
|
+
chmod +x "$mock_dir/bd-legacy"
|
|
263
|
+
export BD_CMD="$mock_dir/bd-legacy"
|
|
264
|
+
rc=0
|
|
265
|
+
result="$(_get_github_issue_number "beads-legacy" 2>/dev/null)" || rc=$?
|
|
266
|
+
assert_exit "legacy github_issue exits successfully" 0 "$rc"
|
|
267
|
+
assert_eq "legacy github_issue returns number" "43" "$result"
|
|
173
268
|
export BD_CMD="$mock_dir/bd"
|
|
174
269
|
|
|
175
|
-
|
|
176
|
-
echo ""
|
|
177
|
-
echo "── Test 8: Injection in title sanitized ──"
|
|
178
|
-
# Create a bd mock that returns injection-laden title
|
|
270
|
+
echo
|
|
271
|
+
echo "== Test 11: Injection in title sanitized =="
|
|
179
272
|
cat > "$mock_dir/bd-inject" << 'MOCK'
|
|
180
273
|
#!/usr/bin/env bash
|
|
181
274
|
echo "$*" >> "$LOG_FILE"
|
|
182
275
|
case "$1" in
|
|
183
276
|
show)
|
|
184
|
-
|
|
185
|
-
|
|
277
|
+
if [[ "${3:-}" == "--json" ]]; then
|
|
278
|
+
cat <<'JSON'
|
|
279
|
+
{"id":"beads-inject","github":{"number":99,"url":"https://github.com/test/repo/issues/99"},"shared":{"title":"Evil $(rm -rf /) Issue"}}
|
|
280
|
+
JSON
|
|
281
|
+
else
|
|
282
|
+
echo 'o beads-inject - Evil $(rm -rf /) Issue'
|
|
283
|
+
echo 'Title: Evil $(rm -rf /) Issue'
|
|
284
|
+
fi
|
|
186
285
|
;;
|
|
187
286
|
set-state) echo "State set" ;;
|
|
188
287
|
esac
|
|
@@ -194,16 +293,54 @@ rc=0
|
|
|
194
293
|
sync_issue_create "beads-inject" || rc=$?
|
|
195
294
|
assert_exit "exit code 0 with injection" 0 "$rc"
|
|
196
295
|
log_contents="$(cat "$log_file")"
|
|
197
|
-
# The $(...) pattern must NOT appear in the gh call
|
|
198
296
|
if [[ "$log_contents" == *'$(rm'* ]]; then
|
|
199
|
-
FAIL=$((FAIL + 1))
|
|
297
|
+
FAIL=$((FAIL + 1))
|
|
298
|
+
echo " FAIL: injection NOT sanitized - \$\(rm found in gh call"
|
|
200
299
|
else
|
|
201
|
-
PASS=$((PASS + 1))
|
|
300
|
+
PASS=$((PASS + 1))
|
|
301
|
+
echo " PASS: injection sanitized - no \$\(rm in gh call"
|
|
202
302
|
fi
|
|
203
|
-
# Restore normal bd mock
|
|
204
303
|
export BD_CMD="$mock_dir/bd"
|
|
205
304
|
|
|
206
|
-
|
|
207
|
-
echo ""
|
|
305
|
+
echo
|
|
306
|
+
echo "== Test 11: sync_issue_create without grep -P =="
|
|
307
|
+
export PATH="$portable_bin:$ORIGINAL_PATH"
|
|
308
|
+
> "$log_file"
|
|
309
|
+
rc=0
|
|
310
|
+
sync_issue_create "beads-001" || rc=$?
|
|
311
|
+
assert_exit "sync_issue_create works when grep -P is unavailable" 0 "$rc"
|
|
312
|
+
log_contents="$(cat "$log_file")"
|
|
313
|
+
assert_contains "gh issue create called without PCRE grep" "issue create" "$log_contents"
|
|
314
|
+
export PATH="$ORIGINAL_PATH"
|
|
315
|
+
|
|
316
|
+
echo
|
|
317
|
+
echo "== Test 12: sync_issue_create with summary-only bd show =="
|
|
318
|
+
cat > "$mock_dir/bd-summary-only" << 'MOCK'
|
|
319
|
+
#!/usr/bin/env bash
|
|
320
|
+
echo "$*" >> "$LOG_FILE"
|
|
321
|
+
case "$1" in
|
|
322
|
+
show)
|
|
323
|
+
if [[ "${3:-}" == "--json" ]]; then
|
|
324
|
+
cat <<'JSON'
|
|
325
|
+
{"id":"beads-summary","github":{"number":77,"url":"https://github.com/test/repo/issues/77"},"shared":{"title":"Summary Only Title"}}
|
|
326
|
+
JSON
|
|
327
|
+
else
|
|
328
|
+
echo "o $2 - Summary Only Title"
|
|
329
|
+
fi
|
|
330
|
+
;;
|
|
331
|
+
set-state) echo "State set" ;;
|
|
332
|
+
esac
|
|
333
|
+
MOCK
|
|
334
|
+
chmod +x "$mock_dir/bd-summary-only"
|
|
335
|
+
export BD_CMD="$mock_dir/bd-summary-only"
|
|
336
|
+
> "$log_file"
|
|
337
|
+
rc=0
|
|
338
|
+
sync_issue_create "beads-summary" || rc=$?
|
|
339
|
+
assert_exit "sync_issue_create works with summary-only bd show" 0 "$rc"
|
|
340
|
+
log_contents="$(cat "$log_file")"
|
|
341
|
+
assert_contains "summary-only title is passed to gh issue create" "Summary Only Title" "$log_contents"
|
|
342
|
+
export BD_CMD="$mock_dir/bd"
|
|
343
|
+
|
|
344
|
+
echo
|
|
208
345
|
echo "Results: $PASS passed, $FAIL failed"
|
|
209
346
|
[[ $FAIL -eq 0 ]] && exit 0 || exit 1
|