forgedock 1.49.0 → 1.51.0
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/bin/engine/outcome-codes.mjs +2 -0
- package/bin/engine-cli.mjs +123 -20
- package/bin/forgedock.mjs +31 -3
- package/bin/lean/batch-health.mjs +219 -0
- package/bin/lean/batch-health.test.mjs +156 -0
- package/bin/lean/batch.mjs +12 -3
- package/bin/lean/batch.test.mjs +304 -8
- package/bin/lean/conformance.mjs +79 -7
- package/bin/lean/conformance.test.mjs +63 -1
- package/bin/lean/context.mjs +8 -2
- package/bin/lean/context.test.mjs +11 -1
- package/bin/lean/credentials.mjs +18 -0
- package/bin/lean/execution.mjs +23 -9
- package/bin/lean/execution.test.mjs +93 -0
- package/bin/lean/fixclosereasons.mjs +52 -0
- package/bin/lean/fixclosereasons.test.mjs +64 -0
- package/bin/lean/heavy.mjs +14 -4
- package/bin/lean/heavy.test.mjs +55 -1
- package/bin/lean/lean.mjs +93 -36
- package/bin/lean/lean.test.mjs +143 -19
- package/bin/lean/live-execution.mjs +136 -0
- package/bin/lean/live-execution.test.mjs +83 -0
- package/bin/lean/orchestrate-blocked.test.mjs +47 -0
- package/bin/lean/orchestrate.mjs +135 -17
- package/bin/lean/prefilter.mjs +4 -3
- package/bin/lean/prefilter.test.mjs +5 -4
- package/bin/lean/prompts.mjs +4 -2
- package/bin/lean/recovery.test.mjs +5 -5
- package/bin/lean/sandbox-provider.contract.test.mjs +36 -1
- package/bin/lean/sandbox-remote.mjs +6 -2
- package/bin/lean/schedd.mjs +9 -0
- package/bin/pool/agent.mjs +3 -1
- package/bin/pool/router.mjs +72 -7
- package/bin/review-staging.mjs +2 -0
- package/bin/runner.mjs +24 -0
- package/commands/orchestrate.md +2 -1
- package/commands/review-pr-staging.md +2 -2
- package/docs/CONFIG.md +39 -1
- package/docs/OUTCOME-CODES.md +2 -1
- package/package.json +1 -1
- package/pool/README.md +2 -0
- package/pool/image/cs-cred +8 -1
- package/scripts/review-staging-post.sh +70 -25
- package/scripts/review-staging-post.test.sh +40 -8
|
@@ -10,12 +10,15 @@
|
|
|
10
10
|
# delivery counts trusted, current-head FORGE:REVIEW-AGENT comments (findings block required) for every domain listed in
|
|
11
11
|
# DIR/<N>_launched_review_agents. A shortfall labels the PR review-degraded + needs-human and posts
|
|
12
12
|
# FORGE:GATE_FAILURE:TYPE=review-panel-integrity; an empty/unreadable list or a missing domain is a delivery failure.
|
|
13
|
-
#
|
|
13
|
+
# The comments are listed once per attempt; a missing domain or an API error re-lists after each
|
|
14
|
+
# FORGE_REVIEW_DELIVERY_RETRY_DELAYS wait (seconds, default "5 15 45"). An API error on the last attempt is
|
|
15
|
+
# REVIEW DELIVERY UNVERIFIED (exit 2, nothing labelled or posted), never "missing" (forge#5166).
|
|
16
|
+
# Exit 0 = every launched reviewer delivered, 1 = not, 2 = could not be verified.
|
|
14
17
|
# test-gate reads the saved /test-gate output FILE (the last FORGE:TEST_GATE:RESULT= marker; an absent marker stays empty and
|
|
15
18
|
# fails closed as BLOCK, never SKIP), applies the trusted override, the posture and the FORGE:GATE_PASS /
|
|
16
19
|
# FORGE:GATE_FAILURE test-gate comments, and persists DIR/<N>_test_gate_verdict and _reason.
|
|
17
20
|
# Exit 0 = continue, 1 = RESULT: BLOCK DEPLOY.
|
|
18
|
-
# Exit: 2 on a usage error.
|
|
21
|
+
# Exit: 2 on a usage error (and delivery UNVERIFIED).
|
|
19
22
|
#
|
|
20
23
|
# The `# BEGIN <name>` ... `# END <name>` blocks below are the spec's former fences, copied without their forge_resolve locator
|
|
21
24
|
# stubs (this script resolves helpers once, below). Helpers (trusted-comments.sh) resolve through forge_resolve from trusted
|
|
@@ -59,24 +62,77 @@ type forge_resolve >/dev/null 2>&1 || { setopt nonomatch 2>/dev/null || true; _S
|
|
|
59
62
|
|
|
60
63
|
if [ "$CMD" = delivery ]; then
|
|
61
64
|
echo "=== Incomplete-panel guard and Phase 6.75: verify agent delivery ==="
|
|
65
|
+
# BEGIN delivery-fetch
|
|
66
|
+
# The PR's comments are listed ONCE per attempt into a cache file; the panel guard and the per-domain check both read it (forge#5166).
|
|
67
|
+
# An attempt is an API error unless gh exits 0 and every page is a JSON array: an error is never evidence that a comment is absent.
|
|
68
|
+
DELIVERY_CACHE="$_SD/${PR_NUMBER}_delivery_comments.json"
|
|
69
|
+
delivery_fetch() {
|
|
70
|
+
rm -f "$DELIVERY_CACHE" "$DELIVERY_CACHE.tmp"
|
|
71
|
+
gh api --paginate "repos/${GH_REPO}/issues/${PR_NUMBER}/comments" > "$DELIVERY_CACHE.tmp" 2>/dev/null || { rm -f "$DELIVERY_CACHE.tmp"; return 1; }
|
|
72
|
+
jq -se 'length > 0 and all(.[]; type == "array")' "$DELIVERY_CACHE.tmp" >/dev/null 2>&1 || { rm -f "$DELIVERY_CACHE.tmp"; return 1; }
|
|
73
|
+
mv -f "$DELIVERY_CACHE.tmp" "$DELIVERY_CACHE"
|
|
74
|
+
}
|
|
75
|
+
# Unique trusted current-head reviewer domains in the cached listing (findings block not required), one per line.
|
|
76
|
+
delivery_domains() {
|
|
77
|
+
bash "$TRUSTED_SCRIPT" bodies "^<!-- FORGE:REVIEW-AGENT:[a-z0-9-]+ -->[\\s\\S]*(^|\\n)Reviewed-SHA:[ ]${REVIEW_SHA_FULL}(\\r?\\n|$)" < "$DELIVERY_CACHE" 2>/dev/null \
|
|
78
|
+
| jq -r 'scan("^<!-- FORGE:REVIEW-AGENT:([a-z0-9-]+) -->") | .[0]' 2>/dev/null | sort -u
|
|
79
|
+
}
|
|
80
|
+
# Trusted current-head comments for one domain that carry a findings block, in the cached listing.
|
|
81
|
+
delivery_count() {
|
|
82
|
+
local AGENT_DOMAIN="$1" n
|
|
83
|
+
n=$(bash "$TRUSTED_SCRIPT" count "^(?=[\\s\\S]*<!-- REVIEW-FINDINGS-START -->[\\s\\S]*<!-- REVIEW-FINDINGS-END -->)<!-- FORGE:REVIEW-AGENT:${AGENT_DOMAIN} -->[\\s\\S]*(^|\\n)Reviewed-SHA:[ ]${REVIEW_SHA_FULL}(\\r?\\n|$)" < "$DELIVERY_CACHE" 2>/dev/null)
|
|
84
|
+
case "$n" in ''|*[!0-9]*) n=0 ;; esac # fail closed: counts as missing
|
|
85
|
+
printf '%s\n' "$n"
|
|
86
|
+
}
|
|
87
|
+
# END delivery-fetch
|
|
62
88
|
# BEGIN panel-guard
|
|
63
89
|
# Each Bash call is a fresh shell: re-resolve the full head SHA if it is not set, and refuse when it is not a commit id
|
|
64
90
|
# (an empty SHA would make the Reviewed-SHA match accept any head).
|
|
65
91
|
[ -n "${REVIEW_SHA_FULL:-}" ] || REVIEW_SHA_FULL=$(gh pr view "$PR_NUMBER" -R "$GH_REPO" --json headRefOid --jq '.headRefOid' 2>/dev/null || echo "")
|
|
66
92
|
case "$REVIEW_SHA_FULL" in *[!0-9a-f]*|"") REVIEW_SHA_FULL="" ;; *) [ "${#REVIEW_SHA_FULL}" -eq 40 ] || [ "${#REVIEW_SHA_FULL}" -eq 64 ] || REVIEW_SHA_FULL="" ;; esac
|
|
67
93
|
TRUSTED_SCRIPT=$(forge_resolve trusted-comments.sh)
|
|
68
|
-
ACTUAL_AGENT_COUNT=0
|
|
69
|
-
if [ -n "$REVIEW_SHA_FULL" ] && [ -n "$TRUSTED_SCRIPT" ]; then
|
|
70
|
-
# Only TRUSTED, current-head reviewer comments count, over every page of comments. Unique domains, so a duplicate does not inflate the count.
|
|
71
|
-
ACTUAL_AGENT_COUNT=$(gh api --paginate "repos/${GH_REPO}/issues/${PR_NUMBER}/comments" 2>/dev/null \
|
|
72
|
-
| bash "$TRUSTED_SCRIPT" bodies "^<!-- FORGE:REVIEW-AGENT:[a-z0-9-]+ -->[\\s\\S]*(^|\\n)Reviewed-SHA:[ ]${REVIEW_SHA_FULL}(\\r?\\n|$)" 2>/dev/null \
|
|
73
|
-
| jq -r 'scan("^<!-- FORGE:REVIEW-AGENT:([a-z0-9-]+) -->") | .[0]' 2>/dev/null | sort -u | grep -c . || echo 0)
|
|
74
|
-
fi
|
|
75
|
-
case "$ACTUAL_AGENT_COUNT" in ''|*[!0-9]*) ACTUAL_AGENT_COUNT=0 ;; esac # fail closed: unreadable or untrusted counts as not posted
|
|
76
94
|
# Fresh shell: SELECTED_AGENT_COUNT is not carried over. Reload it from the PR-keyed launched-agent file, then fail closed when it is empty, non-integer or zero (a staging review always selects at least one reviewer).
|
|
77
95
|
[ -n "${SELECTED_AGENT_COUNT:-}" ] || { [ -n "${PR_NUMBER:-}" ] && [ -r "$_SD/${PR_NUMBER}_launched_review_agents" ] && SELECTED_AGENT_COUNT=$(grep -E '^[a-z0-9-]+$' "$_SD/${PR_NUMBER}_launched_review_agents" | sort -u | grep -c .); }
|
|
78
96
|
SELECTED_COUNT_INVALID=false
|
|
79
97
|
case "${SELECTED_AGENT_COUNT:-}" in ''|*[!0-9]*|0) SELECTED_COUNT_INVALID=true; SELECTED_AGENT_COUNT=0 ;; esac
|
|
98
|
+
# Reload the launched-agent list persisted at dispatch (validated: lowercase marker domains only).
|
|
99
|
+
[ -n "${LAUNCHED_REVIEW_AGENTS:-}" ] || { [ -r "$_SD/${PR_NUMBER}_launched_review_agents" ] && LAUNCHED_REVIEW_AGENTS=$(grep -E '^[a-z0-9-]+$' "$_SD/${PR_NUMBER}_launched_review_agents" | sort -u | tr '\n' ' '); }
|
|
100
|
+
ACTUAL_AGENT_COUNT=0
|
|
101
|
+
DELIVERY_LAST=skipped # ok = the last attempt was a successful listing, error = it was an API error
|
|
102
|
+
DELIVERY_FOUND=" " # launched domains with a findings block, found on any attempt (found stays found)
|
|
103
|
+
DELIVERY_SEEN="" # union of trusted current-head reviewer domains over the successful listings
|
|
104
|
+
if [ -n "$REVIEW_SHA_FULL" ] && [ -n "$TRUSTED_SCRIPT" ] && [ "${DISPATCH_FAILED:-}" != "true" ]; then
|
|
105
|
+
if [ "$SELECTED_COUNT_INVALID" = "true" ] || [ -z "${LAUNCHED_REVIEW_AGENTS:-}" ]; then
|
|
106
|
+
# Nothing to wait for: one listing for the report only, no retry.
|
|
107
|
+
if delivery_fetch; then DELIVERY_LAST=ok; DELIVERY_SEEN=$(delivery_domains); else DELIVERY_LAST=error; fi
|
|
108
|
+
else
|
|
109
|
+
# Retry on listing lag or an API error, after each FORGE_REVIEW_DELIVERY_RETRY_DELAYS wait (seconds; non-integers ignored).
|
|
110
|
+
_ATTEMPT=0
|
|
111
|
+
for _DELAY in 0 ${FORGE_REVIEW_DELIVERY_RETRY_DELAYS-5 15 45}; do
|
|
112
|
+
case "$_DELAY" in ''|*[!0-9]*) continue ;; esac
|
|
113
|
+
_ATTEMPT=$((_ATTEMPT + 1))
|
|
114
|
+
[ "$_ATTEMPT" -gt 1 ] && [ "$_DELAY" -gt 0 ] && sleep "$_DELAY"
|
|
115
|
+
if ! delivery_fetch; then DELIVERY_LAST=error; echo "DELIVERY: attempt ${_ATTEMPT}: API error listing PR comments"; continue; fi
|
|
116
|
+
DELIVERY_LAST=ok
|
|
117
|
+
DELIVERY_SEEN=$(printf '%s\n%s\n' "$DELIVERY_SEEN" "$(delivery_domains)" | grep -E '^[a-z0-9-]+$' | sort -u)
|
|
118
|
+
_PENDING=""
|
|
119
|
+
for _D in $LAUNCHED_REVIEW_AGENTS; do
|
|
120
|
+
case "$DELIVERY_FOUND" in *" $_D "*) continue ;; esac
|
|
121
|
+
if [ "$(delivery_count "$_D")" -ge 1 ]; then DELIVERY_FOUND="${DELIVERY_FOUND}${_D} "; echo "DELIVERY: ${_D} found on attempt ${_ATTEMPT}"; else _PENDING="${_PENDING} ${_D}"; fi
|
|
122
|
+
done
|
|
123
|
+
[ -z "$_PENDING" ] && break
|
|
124
|
+
echo "DELIVERY: attempt ${_ATTEMPT}: missing:${_PENDING}"
|
|
125
|
+
done
|
|
126
|
+
fi
|
|
127
|
+
ACTUAL_AGENT_COUNT=$(printf '%s\n' "$DELIVERY_SEEN" | grep -c . || true)
|
|
128
|
+
fi
|
|
129
|
+
case "$ACTUAL_AGENT_COUNT" in ''|*[!0-9]*) ACTUAL_AGENT_COUNT=0 ;; esac # fail closed: unreadable or untrusted counts as not posted
|
|
130
|
+
if [ "${DISPATCH_FAILED:-}" != "true" ] && [ "$SELECTED_COUNT_INVALID" != "true" ] && [ -n "${LAUNCHED_REVIEW_AGENTS:-}" ] && [ "$DELIVERY_LAST" = error ]; then
|
|
131
|
+
# Never "missing": the comments could not be listed, so nothing is labelled or posted (forge#5166).
|
|
132
|
+
echo "REVIEW DELIVERY UNVERIFIED: could not list PR comments after ${_ATTEMPT:-1} attempt(s) (GitHub API error); found so far:${DELIVERY_FOUND% }"
|
|
133
|
+
echo "Do not re-dispatch reviewers: rerun the finalize step once GitHub answers."
|
|
134
|
+
exit 2
|
|
135
|
+
fi
|
|
80
136
|
if [ "${DISPATCH_FAILED:-}" = "true" ] || [ "$SELECTED_COUNT_INVALID" = "true" ] || [ "$ACTUAL_AGENT_COUNT" -lt "$SELECTED_AGENT_COUNT" ]; then
|
|
81
137
|
gh label create "review-degraded" --color "E4E669" --description "PR review panel was incomplete; re-review required before deployment. Managed by ForgeDock." --force -R "$GH_REPO" 2>/dev/null || true
|
|
82
138
|
gh pr edit "$PR_NUMBER" -R "$GH_REPO" --add-label "review-degraded" --add-label "needs-human" 2>/dev/null || true # allowlist:check-command-side-effects
|
|
@@ -91,7 +147,7 @@ Re-run the full staging review in a fresh session before deployment."
|
|
|
91
147
|
fi
|
|
92
148
|
# END panel-guard
|
|
93
149
|
# BEGIN delivery-check
|
|
94
|
-
# Reload the launched-agent list persisted at dispatch (validated: lowercase marker domains only).
|
|
150
|
+
# Reload the launched-agent list persisted at dispatch (validated: lowercase marker domains only); a no-op when the panel guard loaded it.
|
|
95
151
|
[ -n "${LAUNCHED_REVIEW_AGENTS:-}" ] || { [ -r "$_SD/${PR_NUMBER}_launched_review_agents" ] && LAUNCHED_REVIEW_AGENTS=$(grep -E '^[a-z0-9-]+$' "$_SD/${PR_NUMBER}_launched_review_agents" | sort -u | tr '\n' ' '); }
|
|
96
152
|
# Fail closed: a staging review always launches at least the Phase 2 material-change agent, so an empty list means lost state or skipped bookkeeping, never "nothing to verify".
|
|
97
153
|
if [ -z "${PR_NUMBER:-}" ] || [ -z "${LAUNCHED_REVIEW_AGENTS:-}" ]; then
|
|
@@ -100,21 +156,9 @@ if [ -z "${PR_NUMBER:-}" ] || [ -z "${LAUNCHED_REVIEW_AGENTS:-}" ]; then
|
|
|
100
156
|
exit 1
|
|
101
157
|
fi
|
|
102
158
|
MISSING_AGENT_COMMENTS=""
|
|
103
|
-
#
|
|
104
|
-
# (an empty SHA would make the Reviewed-SHA match accept any head).
|
|
105
|
-
[ -n "${REVIEW_SHA_FULL:-}" ] || REVIEW_SHA_FULL=$(gh pr view "$PR_NUMBER" -R "$GH_REPO" --json headRefOid --jq '.headRefOid' 2>/dev/null || echo "")
|
|
106
|
-
case "$REVIEW_SHA_FULL" in *[!0-9a-f]*|"") REVIEW_SHA_FULL="" ;; *) [ "${#REVIEW_SHA_FULL}" -eq 40 ] || [ "${#REVIEW_SHA_FULL}" -eq 64 ] || REVIEW_SHA_FULL="" ;; esac
|
|
107
|
-
TRUSTED_SCRIPT=$(forge_resolve trusted-comments.sh)
|
|
159
|
+
# Counted once per attempt from the cached listing above (delivery_count); a domain not found on any attempt is missing.
|
|
108
160
|
for AGENT_DOMAIN in $LAUNCHED_REVIEW_AGENTS; do
|
|
109
|
-
|
|
110
|
-
if [ -n "$REVIEW_SHA_FULL" ] && [ -n "$TRUSTED_SCRIPT" ]; then
|
|
111
|
-
AGENT_COMMENT_COUNT=$(gh api --paginate "repos/${GH_REPO}/issues/${PR_NUMBER}/comments" 2>/dev/null \
|
|
112
|
-
| bash "$TRUSTED_SCRIPT" count "^(?=[\\s\\S]*<!-- REVIEW-FINDINGS-START -->[\\s\\S]*<!-- REVIEW-FINDINGS-END -->)<!-- FORGE:REVIEW-AGENT:${AGENT_DOMAIN} -->[\\s\\S]*(^|\\n)Reviewed-SHA:[ ]${REVIEW_SHA_FULL}(\\r?\\n|$)" 2>/dev/null || echo 0)
|
|
113
|
-
fi
|
|
114
|
-
case "$AGENT_COMMENT_COUNT" in ''|*[!0-9]*) AGENT_COMMENT_COUNT=0 ;; esac # fail closed: counts as missing
|
|
115
|
-
if [ "${AGENT_COMMENT_COUNT:-0}" -lt 1 ]; then
|
|
116
|
-
MISSING_AGENT_COMMENTS="${MISSING_AGENT_COMMENTS} ${AGENT_DOMAIN}"
|
|
117
|
-
fi
|
|
161
|
+
case "$DELIVERY_FOUND" in *" ${AGENT_DOMAIN} "*) ;; *) MISSING_AGENT_COMMENTS="${MISSING_AGENT_COMMENTS} ${AGENT_DOMAIN}" ;; esac
|
|
118
162
|
done
|
|
119
163
|
|
|
120
164
|
if [ -n "$MISSING_AGENT_COMMENTS" ]; then
|
|
@@ -123,6 +167,7 @@ if [ -n "$MISSING_AGENT_COMMENTS" ]; then
|
|
|
123
167
|
exit 1
|
|
124
168
|
fi
|
|
125
169
|
# END delivery-check
|
|
170
|
+
rm -f "$DELIVERY_CACHE"
|
|
126
171
|
echo "DELIVERY: OK (${ACTUAL_AGENT_COUNT}/${SELECTED_AGENT_COUNT} launched reviewers posted a findings block for head ${REVIEW_SHA_FULL})"
|
|
127
172
|
exit 0
|
|
128
173
|
fi
|
|
@@ -19,12 +19,12 @@ OTHER=fedcba9876543210fedcba9876543210fedcba98
|
|
|
19
19
|
blk() { awk -v n="$1" '$0 == "# BEGIN " n {f=1; next} $0 == "# END " n {f=0} f' "$POST"; }
|
|
20
20
|
|
|
21
21
|
# (1) the fences now live only in the script: the blocks exist, the spec no longer carries them
|
|
22
|
-
for b in panel-guard delivery-check test-gate-extract test-gate-verdict; do
|
|
22
|
+
for b in delivery-fetch panel-guard delivery-check test-gate-extract test-gate-verdict; do
|
|
23
23
|
[ -n "$(blk "$b")" ] && ok || bad "block $b missing from review-staging-post.sh"
|
|
24
24
|
done
|
|
25
25
|
if grep -qE 'ACTUAL_AGENT_COUNT=\$\(gh api|MISSING_AGENT_COMMENTS=""|GATE_VERDICT_INVALID=0' "$SPEC"; then bad "the spec must not carry a copy of the panel-guard, delivery or verdict-handling fence"; else ok; fi
|
|
26
|
-
# 6.75 carries the findings-block lookahead; the 6.75 empty-list guard precedes the per-domain loop
|
|
27
|
-
blk delivery-
|
|
26
|
+
# 6.75 carries the findings-block lookahead (in the shared cached-listing counter); the 6.75 empty-list guard precedes the per-domain loop
|
|
27
|
+
blk delivery-fetch | grep -qF 'count "^(?=[\\s\\S]*<!-- REVIEW-FINDINGS-START -->[\\s\\S]*<!-- REVIEW-FINDINGS-END -->)<!-- FORGE:REVIEW-AGENT:${AGENT_DOMAIN} -->' && ok || bad "6.75 count must require the findings block"
|
|
28
28
|
G=$(blk delivery-check | grep -n 'launched-agent list is empty or unreadable' | head -1 | cut -d: -f1); L=$(blk delivery-check | grep -n '^for AGENT_DOMAIN in' | head -1 | cut -d: -f1)
|
|
29
29
|
[ -n "$G" ] && [ -n "$L" ] && [ "$G" -lt "$L" ] && ok || bad "6.75 empty-list guard must precede the for AGENT_DOMAIN loop"
|
|
30
30
|
# the producer never coerces an absent marker to SKIP; the catch-all arm never sets SKIP
|
|
@@ -38,7 +38,8 @@ bash "$POST" delivery --repo o/r --pr 1 >/dev/null 2>&1; [ $? -eq 2 ] && ok || b
|
|
|
38
38
|
bash "$POST" test-gate --repo o/r --pr 1 --sd "$TMP/u" --output x --prs-file "$TMP/none" >/dev/null 2>&1; [ $? -eq 2 ] && ok || bad "unreadable --prs-file must exit 2"
|
|
39
39
|
bash "$POST" test-gate --repo o/r --pr 1 --sd "$TMP/u" --output x --prs-file /dev/null --posture lax >/dev/null 2>&1; [ $? -eq 2 ] && ok || bad "unknown --posture must exit 2"
|
|
40
40
|
|
|
41
|
-
# gh stub: $FIX/comments.json is the PR's comment list
|
|
41
|
+
# gh stub: $FIX/comments.json is the PR's comment list ($FIX/comments.<k>.json, when present, answers the k-th listing; no file = API error);
|
|
42
|
+
# each listing is logged to $FIX/api_calls; posted bodies and edits go to $FIX/posted / $FIX/edits
|
|
42
43
|
mkdir -p "$TMP/bin"
|
|
43
44
|
cat > "$TMP/bin/gh" <<'STUB'
|
|
44
45
|
#!/usr/bin/env bash
|
|
@@ -49,17 +50,21 @@ case "$1 $2" in
|
|
|
49
50
|
"label create") exit 0 ;;
|
|
50
51
|
"pr view") [ -f "$F/head" ] && { cat "$F/head"; exit 0; }; exit 1 ;;
|
|
51
52
|
esac
|
|
52
|
-
if [ "$1" = api ]; then case "$a" in *"/comments"*)
|
|
53
|
+
if [ "$1" = api ]; then case "$a" in *"/comments"*)
|
|
54
|
+
echo x >> "$F/api_calls"; k=$(wc -l < "$F/api_calls" | tr -d ' ')
|
|
55
|
+
if [ -f "$F/comments.$k.json" ]; then cat "$F/comments.$k.json"; else cat "$F/comments.json" 2>/dev/null || exit 1; fi; exit 0 ;; esac; fi
|
|
53
56
|
echo "gh stub: unhandled: $a" >&2; exit 1
|
|
54
57
|
STUB
|
|
55
58
|
printf '#!/bin/sh\nexit 1\n' > "$TMP/bin/yq" # no yq: defaults apply
|
|
56
59
|
chmod +x "$TMP/bin/gh" "$TMP/bin/yq"
|
|
57
60
|
export PATH="$TMP/bin:$PATH"
|
|
61
|
+
export FORGE_REVIEW_DELIVERY_RETRY_DELAYS="0 0 0" # retries without waiting (4 attempts)
|
|
58
62
|
newfix() { FIX="$TMP/fix.$1"; rm -rf "$FIX"; mkdir -p "$FIX/sd"; export FIX; }
|
|
59
63
|
FB=$'<!-- REVIEW-FINDINGS-START -->\n<!-- REVIEW-FINDINGS-END -->'
|
|
60
64
|
cm() { jq -n --arg b "$1" --arg t "${2:-Bot}" '{body:$b, author_association:"NONE", user:{type:$t, login:"x"}}'; }
|
|
61
65
|
agent() { printf '<!-- FORGE:REVIEW-AGENT:%s -->\nReviewed-SHA: %s\n\n%s' "$1" "${2:-$HEAD}" "${3-$FB}"; }
|
|
62
|
-
comments() { jq -s '.' > "$FIX/comments.json"; }
|
|
66
|
+
comments() { jq -s '.' > "$FIX/comments${1:+.$1}.json"; }
|
|
67
|
+
calls() { [ -f "$FIX/api_calls" ] && wc -l < "$FIX/api_calls" | tr -d ' ' || echo 0; }
|
|
63
68
|
deliver() { bash "$POST" delivery --repo o/r --pr 42 --sd "$FIX/sd" --head "${1:-$HEAD}" 2>&1; echo "rc=$?"; }
|
|
64
69
|
|
|
65
70
|
# (3) delivery: a complete panel passes and posts nothing
|
|
@@ -67,6 +72,7 @@ newfix full; printf 'security\nmaterial-change\n' > "$FIX/sd/42_launched_review_
|
|
|
67
72
|
{ cm "$(agent security)"; cm "$(agent material-change)"; } | comments
|
|
68
73
|
OUT=$(deliver); case "$OUT" in *"DELIVERY: OK (2/2"*"rc=0") ok ;; *) bad "complete panel must pass (got: $OUT)" ;; esac
|
|
69
74
|
[ ! -s "$FIX/posted" ] && ok || bad "a complete panel must post nothing"
|
|
75
|
+
[ "$(calls)" = 1 ] && ok || bad "delivery: complete panel lists comments once (got $(calls) listings)"
|
|
70
76
|
# a duplicate domain line does not inflate the selected count
|
|
71
77
|
printf 'security\nsecurity\nmaterial-change\n' > "$FIX/sd/42_launched_review_agents"
|
|
72
78
|
OUT=$(deliver); case "$OUT" in *"rc=0") ok ;; *) bad "duplicate launched lines must count once (got: $OUT)" ;; esac
|
|
@@ -94,9 +100,35 @@ newfix empty; : > "$FIX/sd/42_launched_review_agents"; cm "$(agent security)" |
|
|
|
94
100
|
OUT=$(deliver); case "$OUT" in *"rc=1") ok ;; *) bad "empty launched list must fail (got: $OUT)" ;; esac
|
|
95
101
|
newfix none; cm "$(agent security)" | comments
|
|
96
102
|
OUT=$(deliver); case "$OUT" in *"rc=1") ok ;; *) bad "absent launched list must fail (got: $OUT)" ;; esac
|
|
97
|
-
# unreadable comments
|
|
103
|
+
# unreadable comments (an API error on every attempt) fail closed as UNVERIFIED, never as missing (forge#5166)
|
|
98
104
|
newfix unreadable; printf 'security\n' > "$FIX/sd/42_launched_review_agents"
|
|
99
|
-
OUT=$(deliver); case "$OUT" in *"rc=
|
|
105
|
+
OUT=$(deliver); case "$OUT" in *"REVIEW DELIVERY UNVERIFIED"*"rc=2") ok ;; *) bad "delivery: API error on every attempt is UNVERIFIED (got: $OUT)" ;; esac
|
|
106
|
+
case "$OUT" in *"REVIEW DELIVERY FAILURE"*) bad "an API error must not be reported as missing" ;; *) ok ;; esac
|
|
107
|
+
[ ! -s "$FIX/posted" ] && [ ! -s "$FIX/edits" ] && ok || bad "UNVERIFIED must post no GATE_FAILURE and add no label"
|
|
108
|
+
[ "$(calls)" = 4 ] && ok || bad "UNVERIFIED must come after every retry (got $(calls) listings)"
|
|
109
|
+
# delivery: lagging listing passes on retry -- the first listing lacks a comment, the second shows it
|
|
110
|
+
newfix lag; printf 'security\nmaterial-change\n' > "$FIX/sd/42_launched_review_agents"
|
|
111
|
+
cm "$(agent security)" | comments 1
|
|
112
|
+
{ cm "$(agent security)"; cm "$(agent material-change)"; } | comments
|
|
113
|
+
OUT=$(deliver); case "$OUT" in *"DELIVERY: OK (2/2"*"rc=0") ok ;; *) bad "delivery: lagging listing passes on retry (got: $OUT)" ;; esac
|
|
114
|
+
case "$OUT" in *"material-change found on attempt 2"*) ok ;; *) bad "lag: must print the attempt a domain was found on (got: $OUT)" ;; esac
|
|
115
|
+
case "$OUT" in *"security found on attempt 1"*) ok ;; *) bad "lag: security must be found on attempt 1 (got: $OUT)" ;; esac
|
|
116
|
+
[ ! -s "$FIX/posted" ] && [ ! -s "$FIX/edits" ] && ok || bad "lag: a panel delivered on retry must post and label nothing"
|
|
117
|
+
[ "$(calls)" = 2 ] && ok || bad "lag: retries stop once every domain is found (got $(calls) listings)"
|
|
118
|
+
# a transient API error, then a full listing, passes
|
|
119
|
+
newfix transient; printf 'security\n' > "$FIX/sd/42_launched_review_agents"
|
|
120
|
+
echo 'not json' > "$FIX/comments.1.json"; cm "$(agent security)" | comments
|
|
121
|
+
OUT=$(deliver); case "$OUT" in *"attempt 1: API error"*"security found on attempt 2"*"rc=0") ok ;; *) bad "a transient API error must be retried (got: $OUT)" ;; esac
|
|
122
|
+
# a real absence after every retry is still missing (listed and absent), with the panel-integrity failure
|
|
123
|
+
newfix absent; printf 'security\nmaterial-change\n' > "$FIX/sd/42_launched_review_agents"; cm "$(agent security)" | comments
|
|
124
|
+
OUT=$(deliver); case "$OUT" in *"rc=1") ok ;; *) bad "absent after retries must fail as missing (got: $OUT)" ;; esac
|
|
125
|
+
case "$OUT" in *UNVERIFIED*) bad "a listed absence is not UNVERIFIED" ;; *) ok ;; esac
|
|
126
|
+
grep -qF 'review-panel-integrity' "$FIX/posted" 2>/dev/null && ok || bad "absent: must post GATE_FAILURE:TYPE=review-panel-integrity"
|
|
127
|
+
[ "$(calls)" = 4 ] && ok || bad "absent: must retry before declaring missing (got $(calls) listings)"
|
|
128
|
+
# an API error on the LAST attempt after a lacking listing is UNVERIFIED, not missing
|
|
129
|
+
newfix lasterr; printf 'security\nmaterial-change\n' > "$FIX/sd/42_launched_review_agents"
|
|
130
|
+
cm "$(agent security)" | comments 1; cm "$(agent security)" | comments 2; cm "$(agent security)" | comments 3; echo '{}' > "$FIX/comments.4.json"
|
|
131
|
+
OUT=$(deliver); case "$OUT" in *"REVIEW DELIVERY UNVERIFIED"*"rc=2") ok ;; *) bad "an error on the last attempt must be UNVERIFIED (got: $OUT)" ;; esac
|
|
100
132
|
# a malformed --head with no resolvable head refuses
|
|
101
133
|
newfix badhead; printf 'security\n' > "$FIX/sd/42_launched_review_agents"; cm "$(agent security)" | comments
|
|
102
134
|
OUT=$(deliver "n/a"); case "$OUT" in *"rc=1") ok ;; *) bad "an unresolvable head must fail closed (got: $OUT)" ;; esac
|