forked from heavy-duty/ceremony
fix: stand down with exit 0 on non-mint issue arrivals
reconcile_opened_issue's two early exits were bare returns, which carry
the failed guard's status into the executed script's set -e — every
triage-authored mint killed the labels run before one issue was
reconciled (#91, 4/4 observed). The stand-downs now say return 0; a
genuine failure on the arrival path still aborts loudly.
The suite sources the script and takes the set -u-only branch, so it
was blind to this by construction. The new arrival section executes the
script as a subprocess behind a fixture-serving gh stub (the house
pattern from test/release-chain.test.sh) and covers all three arrival
outcomes plus the preserved loud-failure path; it fails against
bb37c15 with the production signature — exit 1, empty output.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
bb37c1567c
commit
c39b78959d
3 changed files with 95 additions and 2 deletions
|
|
@ -6,6 +6,7 @@ so entries say what changed, cite the issue, and stop.
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
- `issueflow-reconcile` — a triage-authored issue arrival stands down with exit 0 instead of killing the run before the sweep (#91).
|
||||||
- FLEET.md — the assignee's `attention` wake: one role-independent trigger ahead of every per-role list, one acked session per demand; a spec on paper until `duty.sh` polls it (#86).
|
- FLEET.md — the assignee's `attention` wake: one role-independent trigger ahead of every per-role list, one acked session per demand; a spec on paper until `duty.sh` polls it (#86).
|
||||||
- `attention` doctrine — define its assignee-owned pickup, ack, queue and clock semantics across labels, triage, and builder roles (#85).
|
- `attention` doctrine — define its assignee-owned pickup, ack, queue and clock semantics across labels, triage, and builder roles (#85).
|
||||||
- `attention` — add the issue-only, hand-set assignee-demand flag to the core label taxonomy (#84).
|
- `attention` — add the issue-only, hand-set assignee-demand flag to the core label taxonomy (#84).
|
||||||
|
|
|
||||||
|
|
@ -366,11 +366,14 @@ reconcile_issue() {
|
||||||
reconcile_opened_issue() {
|
reconcile_opened_issue() {
|
||||||
local n="$1" author triage=false labels remove="" label
|
local n="$1" author triage=false labels remove="" label
|
||||||
ISSUE_JSON="$(gh api "repos/$REPO/issues/$n")"
|
ISSUE_JSON="$(gh api "repos/$REPO/issues/$n")"
|
||||||
jq -e 'has("pull_request") | not' <<<"$ISSUE_JSON" >/dev/null || return
|
# The stand-downs return 0 explicitly: a bare return carries the failed
|
||||||
|
# test's status, which under execution is live `set -e` — and it killed the
|
||||||
|
# run on every triage-authored mint, before one issue was reconciled (#91).
|
||||||
|
jq -e 'has("pull_request") | not' <<<"$ISSUE_JSON" >/dev/null || return 0
|
||||||
author="$(jq -r '.user.login' <<<"$ISSUE_JSON")"
|
author="$(jq -r '.user.login' <<<"$ISSUE_JSON")"
|
||||||
is_triage_actor "$author" && triage=true
|
is_triage_actor "$author" && triage=true
|
||||||
labels="$(jq -r '.labels[].name' <<<"$ISSUE_JSON")"
|
labels="$(jq -r '.labels[].name' <<<"$ISSUE_JSON")"
|
||||||
[ "$(author_decision "$triage" <<<"$labels")" = ADD_NEEDS_TRIAGE ] || return
|
[ "$(author_decision "$triage" <<<"$labels")" = ADD_NEEDS_TRIAGE ] || return 0
|
||||||
for label in epic "${QUEUE_LABELS[@]}"; do
|
for label in epic "${QUEUE_LABELS[@]}"; do
|
||||||
grep -qxF "$label" <<<"$labels" && remove="$remove,$label"
|
grep -qxF "$label" <<<"$labels" && remove="$remove,$label"
|
||||||
done
|
done
|
||||||
|
|
|
||||||
|
|
@ -396,4 +396,93 @@ churned="$(issue_probe 24 $'claimed\nneeds-ruling')"
|
||||||
check "8 real-quiet days nudge through a 2-day-old label churn" 0 "" \
|
check "8 real-quiet days nudge through a 2-day-old label churn" 0 "" \
|
||||||
grep -q 'ruling nudge' <<<"$churned"
|
grep -q 'ruling nudge' <<<"$churned"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# The arrival path, executed the way the action executes it (#91): four
|
||||||
|
# triage-authored mints died silently because the stand-down `return`s in
|
||||||
|
# reconcile_opened_issue carried the failed test's status into `set -e`. A
|
||||||
|
# sourced test takes the `set -u`-only branch and is blind to that class of
|
||||||
|
# bug by construction, so these run the script as a subprocess behind a
|
||||||
|
# PATH-stubbed gh — the house pattern from test/release-chain.test.sh.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
ARRIVAL="$TMP/arrival"
|
||||||
|
mkdir -p "$ARRIVAL/stub" "$ARRIVAL/fixtures"
|
||||||
|
printf 'triage-actors=triage-one triage-two\n' >"$ARRIVAL/labels.conf"
|
||||||
|
cat >"$ARRIVAL/stub/gh" <<'EOF'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Endpoints map to files under $GH_FIXTURES ('/?&=' -> '_'); an absent file
|
||||||
|
# answers an empty list, a .error sentinel fails the call like a dead API.
|
||||||
|
if [ "$1" = api ]; then
|
||||||
|
shift
|
||||||
|
endpoint="" jqexpr=""
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--jq) jqexpr="$2"; shift ;;
|
||||||
|
-f|-F) shift ;;
|
||||||
|
-*) ;;
|
||||||
|
*) [ -n "$endpoint" ] || endpoint="$1" ;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
file="$GH_FIXTURES/$(printf '%s' "$endpoint" | tr '/?&=' '____').json"
|
||||||
|
[ ! -f "$file.error" ] || exit 1
|
||||||
|
if [ -f "$file" ]; then payload="$(cat "$file")"; else payload='[]'; fi
|
||||||
|
if [ -n "$jqexpr" ]; then jq -r "$jqexpr" <<<"$payload"; else printf '%s\n' "$payload"; fi
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if [ "$1" = issue ]; then printf '%s\n' "$*" >>"$GH_FIXTURES/edits"; exit 0; fi
|
||||||
|
echo "gh stub: unexpected call: gh $*" >&2
|
||||||
|
exit 97
|
||||||
|
EOF
|
||||||
|
chmod +x "$ARRIVAL/stub/gh"
|
||||||
|
printf '%s\n' \
|
||||||
|
'{"data":{"repository":{"pullRequests":{"nodes":[],"pageInfo":{"hasNextPage":false,"endCursor":null}}}}}' \
|
||||||
|
>"$ARRIVAL/fixtures/graphql.json"
|
||||||
|
arrival_fixture() { printf '%s\n' "$1" >"$ARRIVAL/fixtures/repos_owner_repo_issues_91.json"; }
|
||||||
|
arrival_run() {
|
||||||
|
: >"$ARRIVAL/fixtures/edits"
|
||||||
|
env PATH="$ARRIVAL/stub:$PATH" GH_FIXTURES="$ARRIVAL/fixtures" \
|
||||||
|
REPO=owner/repo LABELS_CONF="$ARRIVAL/labels.conf" \
|
||||||
|
EVENT_NAME=issues EVENT_ACTION=opened EVENT_ISSUE=91 \
|
||||||
|
bash "$ROOT/actions/issueflow-reconcile/issueflow-reconcile.sh"
|
||||||
|
}
|
||||||
|
|
||||||
|
arrival_fixture '{"user":{"login":"triage-one"},"labels":[{"name":"ready"}]}'
|
||||||
|
triage_out="$(arrival_run 2>&1)"
|
||||||
|
triage_rc=$?
|
||||||
|
check "a triage-authored arrival exits 0 (#91's four dead mints)" 0 "" \
|
||||||
|
test "$triage_rc" -eq 0
|
||||||
|
check "...and its output reaches the sweep" 0 "" \
|
||||||
|
grep -qF 'issueflow: reconciled.' <<<"$triage_out"
|
||||||
|
check "...and mints nothing" 1 "" test -s "$ARRIVAL/fixtures/edits"
|
||||||
|
|
||||||
|
arrival_fixture '{"user":{"login":"outsider"},"labels":[{"name":"ready"}]}'
|
||||||
|
outside_out="$(arrival_run 2>&1)"
|
||||||
|
outside_rc=$?
|
||||||
|
check "an outside-authored arrival exits 0" 0 "" test "$outside_rc" -eq 0
|
||||||
|
check "...still mints needs-triage" 0 "" \
|
||||||
|
grep -qF 'needs-triage (opened by outsider)' <<<"$outside_out"
|
||||||
|
check "...still strips the smuggled queue label" 0 "" \
|
||||||
|
grep -qxF 'issue edit 91 -R owner/repo --add-label needs-triage --remove-label ready' \
|
||||||
|
"$ARRIVAL/fixtures/edits"
|
||||||
|
check "...and the sweep still runs after the mint" 0 "" \
|
||||||
|
grep -qF 'issueflow: reconciled.' <<<"$outside_out"
|
||||||
|
|
||||||
|
arrival_fixture '{"user":{"login":"outsider"},"labels":[],"pull_request":{"url":"x"}}'
|
||||||
|
pr_out="$(arrival_run 2>&1)"
|
||||||
|
pr_rc=$?
|
||||||
|
check "a PR arrival exits 0" 0 "" test "$pr_rc" -eq 0
|
||||||
|
check "...stands down without minting" 1 "" test -s "$ARRIVAL/fixtures/edits"
|
||||||
|
check "...and the sweep still runs" 0 "" \
|
||||||
|
grep -qF 'issueflow: reconciled.' <<<"$pr_out"
|
||||||
|
|
||||||
|
# D2 preserved: only the deliberate stand-downs changed; a genuine failure on
|
||||||
|
# the arrival path still kills the run loudly.
|
||||||
|
: >"$ARRIVAL/fixtures/repos_owner_repo_issues_91.json.error"
|
||||||
|
err_out="$(arrival_run 2>&1)"
|
||||||
|
err_rc=$?
|
||||||
|
check "a dead API on the arrival path still fails the run (D2)" 0 "" \
|
||||||
|
test "$err_rc" -eq 1
|
||||||
|
check "...and the sweep does not run over a lying arrival" 1 "" \
|
||||||
|
grep -qF 'issueflow: reconciled.' <<<"$err_out"
|
||||||
|
|
||||||
summary
|
summary
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue