From 0697f7537bae2bd672d638d47d194db8ab99a5b9 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 8 Apr 2026 09:17:05 +0000 Subject: [PATCH] fix: move helper functions before their first call site 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) --- docker/reproduce/entrypoint-reproduce.sh | 254 +++++++++++------------ 1 file changed, 127 insertions(+), 127 deletions(-) diff --git a/docker/reproduce/entrypoint-reproduce.sh b/docker/reproduce/entrypoint-reproduce.sh index 553dc10..6b4e469 100644 --- a/docker/reproduce/entrypoint-reproduce.sh +++ b/docker/reproduce/entrypoint-reproduce.sh @@ -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 #" + 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 #" - 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 # ---------------------------------------------------------------------------