fix: move helper functions before their first call site
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful

The verification helpers (_is_parent_issue, _are_all_sub_issues_closed,
_get_sub_issue_list) and label/comment helpers (_label_id, _add_label,
_remove_label, _post_comment) were defined after the code that calls
them. Under set -euo pipefail, this causes a runtime crash.

Move all helper function definitions to right after the Claude session
completes, before the triage post-processing and verification blocks
that use them.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude 2026-04-08 09:17:05 +00:00
parent 7a1ea91530
commit 0697f7537b

View file

@ -492,6 +492,133 @@ if [ $CLAUDE_EXIT -eq 124 ]; then
log "WARNING: Claude session timed out after ${FORMULA_TIMEOUT_MINUTES}m"
fi
# ---------------------------------------------------------------------------
# Verification helpers — bug-report lifecycle
# ---------------------------------------------------------------------------
# Check if an issue is a parent with sub-issues (identified by sub-issues
# whose body contains "Decomposed from #N" where N is the parent's number).
# Returns: 0 if parent with sub-issues found, 1 otherwise
# Sets: _PARENT_SUB_ISSUES (space-separated list of sub-issue numbers)
_is_parent_issue() {
local parent_num="$1"
_PARENT_SUB_ISSUES=""
# Fetch all issues (open and closed) to find sub-issues
local all_issues_json
all_issues_json=$(curl -sf \
-H "Authorization: token ${FORGE_TOKEN}" \
"${FORGE_API}/issues?type=issues&state=all&limit=50" 2>/dev/null) || return 1
# Find issues whose body contains "Decomposed from #<parent_num>"
local sub_issues
sub_issues=$(python3 -c '
import sys, json
parent_num = sys.argv[1]
data = json.load(open("/dev/stdin"))
sub_issues = []
for issue in data:
body = issue.get("body") or ""
if f"Decomposed from #{parent_num}" in body:
sub_issues.append(str(issue["number"]))
print(" ".join(sub_issues))
' "$parent_num" < <(echo "$all_issues_json")) || return 1
if [ -n "$sub_issues" ]; then
_PARENT_SUB_ISSUES="$sub_issues"
return 0
fi
return 1
}
# Check if all sub-issues of a parent are closed.
# Requires: _PARENT_SUB_ISSUES to be set
# Returns: 0 if all closed, 1 if any still open
_are_all_sub_issues_closed() {
if [ -z "${_PARENT_SUB_ISSUES:-}" ]; then
return 1
fi
for sub_num in $_PARENT_SUB_ISSUES; do
local sub_state
sub_state=$(curl -sf \
-H "Authorization: token ${FORGE_TOKEN}" \
"${FORGE_API}/issues/${sub_num}" 2>/dev/null | jq -r '.state // "unknown"') || {
log "WARNING: could not fetch state of sub-issue #${sub_num}"
return 1
}
if [ "$sub_state" != "closed" ]; then
log "Sub-issue #${sub_num} is not closed (state: ${sub_state})"
return 1
fi
done
return 0
}
# Get sub-issue details for comment
# Returns: formatted list of sub-issues
_get_sub_issue_list() {
local result=""
for sub_num in $_PARENT_SUB_ISSUES; do
local sub_title
sub_title=$(curl -sf \
-H "Authorization: token ${FORGE_TOKEN}" \
"${FORGE_API}/issues/${sub_num}" 2>/dev/null | jq -r '.title // "unknown"') || {
sub_title="unknown"
}
result="${result}- #${sub_num} ${sub_title}"$'\n'
done
printf '%s' "$result"
}
# ---------------------------------------------------------------------------
# Label helpers
# ---------------------------------------------------------------------------
_label_id() {
local name="$1" color="$2"
local id
id=$(curl -sf \
-H "Authorization: token ${FORGE_TOKEN}" \
"${FORGE_API}/labels" 2>/dev/null \
| jq -r --arg n "$name" '.[] | select(.name == $n) | .id' 2>/dev/null || echo "")
if [ -z "$id" ]; then
id=$(curl -sf -X POST \
-H "Authorization: token ${FORGE_TOKEN}" \
-H "Content-Type: application/json" \
"${FORGE_API}/labels" \
-d "{\"name\":\"${name}\",\"color\":\"${color}\"}" 2>/dev/null \
| jq -r '.id // empty' 2>/dev/null || echo "")
fi
echo "$id"
}
_add_label() {
local issue="$1" label_id="$2"
[ -z "$label_id" ] && return 0
curl -sf -X POST \
-H "Authorization: token ${FORGE_TOKEN}" \
-H "Content-Type: application/json" \
"${FORGE_API}/issues/${issue}/labels" \
-d "{\"labels\":[${label_id}]}" >/dev/null 2>&1 || true
}
_remove_label() {
local issue="$1" label_id="$2"
[ -z "$label_id" ] && return 0
curl -sf -X DELETE \
-H "Authorization: token ${FORGE_TOKEN}" \
"${FORGE_API}/issues/${issue}/labels/${label_id}" >/dev/null 2>&1 || true
}
_post_comment() {
local issue="$1" body="$2"
curl -sf -X POST \
-H "Authorization: token ${FORGE_TOKEN}" \
-H "Content-Type: application/json" \
"${FORGE_API}/issues/${issue}/comments" \
-d "$(jq -nc --arg b "$body" '{body:$b}')" >/dev/null 2>&1 || true
}
# ---------------------------------------------------------------------------
# Triage post-processing: enforce backlog label on created issues
# ---------------------------------------------------------------------------
@ -793,133 +920,6 @@ if find "$(dirname "${SCREENSHOT_PREFIX}")" -name "$(basename "${SCREENSHOT_PREF
done
fi
# ---------------------------------------------------------------------------
# Verification helpers — bug-report lifecycle
# ---------------------------------------------------------------------------
# Check if an issue is a parent with sub-issues (identified by sub-issues
# whose body contains "Decomposed from #N" where N is the parent's number).
# Returns: 0 if parent with sub-issues found, 1 otherwise
# Sets: _PARENT_SUB_ISSUES (space-separated list of sub-issue numbers)
_is_parent_issue() {
local parent_num="$1"
_PARENT_SUB_ISSUES=""
# Fetch all issues (open and closed) to find sub-issues
local all_issues_json
all_issues_json=$(curl -sf \
-H "Authorization: token ${FORGE_TOKEN}" \
"${FORGE_API}/issues?type=issues&state=all&limit=50" 2>/dev/null) || return 1
# Find issues whose body contains "Decomposed from #<parent_num>"
local sub_issues
sub_issues=$(python3 -c '
import sys, json
parent_num = sys.argv[1]
data = json.load(open("/dev/stdin"))
sub_issues = []
for issue in data:
body = issue.get("body") or ""
if f"Decomposed from #{parent_num}" in body:
sub_issues.append(str(issue["number"]))
print(" ".join(sub_issues))
' "$parent_num" < <(echo "$all_issues_json")) || return 1
if [ -n "$sub_issues" ]; then
_PARENT_SUB_ISSUES="$sub_issues"
return 0
fi
return 1
}
# Check if all sub-issues of a parent are closed.
# Requires: _PARENT_SUB_ISSUES to be set
# Returns: 0 if all closed, 1 if any still open
_are_all_sub_issues_closed() {
if [ -z "${_PARENT_SUB_ISSUES:-}" ]; then
return 1
fi
for sub_num in $_PARENT_SUB_ISSUES; do
local sub_state
sub_state=$(curl -sf \
-H "Authorization: token ${FORGE_TOKEN}" \
"${FORGE_API}/issues/${sub_num}" 2>/dev/null | jq -r '.state // "unknown"') || {
log "WARNING: could not fetch state of sub-issue #${sub_num}"
return 1
}
if [ "$sub_state" != "closed" ]; then
log "Sub-issue #${sub_num} is not closed (state: ${sub_state})"
return 1
fi
done
return 0
}
# Get sub-issue details for comment
# Returns: formatted list of sub-issues
_get_sub_issue_list() {
local result=""
for sub_num in $_PARENT_SUB_ISSUES; do
local sub_title
sub_title=$(curl -sf \
-H "Authorization: token ${FORGE_TOKEN}" \
"${FORGE_API}/issues/${sub_num}" 2>/dev/null | jq -r '.title // "unknown"') || {
sub_title="unknown"
}
result="${result}- #${sub_num} ${sub_title}"$'\n'
done
printf '%s' "$result"
}
# ---------------------------------------------------------------------------
# Label helpers
# ---------------------------------------------------------------------------
_label_id() {
local name="$1" color="$2"
local id
id=$(curl -sf \
-H "Authorization: token ${FORGE_TOKEN}" \
"${FORGE_API}/labels" 2>/dev/null \
| jq -r --arg n "$name" '.[] | select(.name == $n) | .id' 2>/dev/null || echo "")
if [ -z "$id" ]; then
id=$(curl -sf -X POST \
-H "Authorization: token ${FORGE_TOKEN}" \
-H "Content-Type: application/json" \
"${FORGE_API}/labels" \
-d "{\"name\":\"${name}\",\"color\":\"${color}\"}" 2>/dev/null \
| jq -r '.id // empty' 2>/dev/null || echo "")
fi
echo "$id"
}
_add_label() {
local issue="$1" label_id="$2"
[ -z "$label_id" ] && return 0
curl -sf -X POST \
-H "Authorization: token ${FORGE_TOKEN}" \
-H "Content-Type: application/json" \
"${FORGE_API}/issues/${issue}/labels" \
-d "{\"labels\":[${label_id}]}" >/dev/null 2>&1 || true
}
_remove_label() {
local issue="$1" label_id="$2"
[ -z "$label_id" ] && return 0
curl -sf -X DELETE \
-H "Authorization: token ${FORGE_TOKEN}" \
"${FORGE_API}/issues/${issue}/labels/${label_id}" >/dev/null 2>&1 || true
}
_post_comment() {
local issue="$1" body="$2"
curl -sf -X POST \
-H "Authorization: token ${FORGE_TOKEN}" \
-H "Content-Type: application/json" \
"${FORGE_API}/issues/${issue}/comments" \
-d "$(jq -nc --arg b "$body" '{body:$b}')" >/dev/null 2>&1 || true
}
# ---------------------------------------------------------------------------
# Apply labels and post findings
# ---------------------------------------------------------------------------