From 353fa54ae19ded0266f65b8501955d4ad1ea5447 Mon Sep 17 00:00:00 2001 From: Andriujose <43181885+andriujoseba@users.noreply.github.com> Date: Tue, 4 Aug 2026 15:03:25 +0000 Subject: [PATCH] fix: abort on unreadable issue board --- .../issueflow-reconcile.sh | 23 +++++++--- changelog.d/257.md | 3 ++ test/issueflow-reconcile.test.sh | 45 +++++++++++++++++++ 3 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 changelog.d/257.md diff --git a/actions/issueflow-reconcile/issueflow-reconcile.sh b/actions/issueflow-reconcile/issueflow-reconcile.sh index 06f4875..f6c6db1 100644 --- a/actions/issueflow-reconcile/issueflow-reconcile.sh +++ b/actions/issueflow-reconcile/issueflow-reconcile.sh @@ -918,13 +918,26 @@ main() { done < <(refs_references <<<"$body") done)" - local n tail_line + local n tail_line issue_numbers SKIPPED_COUNT=0 SKIPPED_ISSUES="" - for n in $(gh api --paginate "repos/$REPO/issues?state=open&per_page=100" \ - --jq '.[] | select(has("pull_request") | not) | .number'); do - reconcile_issue_pass "$n" - done + # A command substitution in a for list suppresses errexit. Capture and + # check the board read before entering the loop, or a 504 (including one + # after partial pagination) reports a full pass over a truncated board + # (#257). + if ! guarded_read issue_numbers gh api --paginate \ + "repos/$REPO/issues?state=open&per_page=100" \ + --jq '.[] | select(has("pull_request") | not) | .number'; then + log "could not read the issue board: $(read_failure_reason "$READ_FAILURE_STDERR")" + return 1 + fi + if [ -z "$issue_numbers" ]; then + log "no open issues." + else + while IFS= read -r n; do + [ -n "$n" ] && reconcile_issue_pass "$n" + done <<<"$issue_numbers" + fi log "reconciled." # The job stays green (D7): an hourly sweep over a hundred-issue board meets # transient 504s as a matter of course, and reddening the whole run for one diff --git a/changelog.d/257.md b/changelog.d/257.md new file mode 100644 index 0000000..b49ecfa --- /dev/null +++ b/changelog.d/257.md @@ -0,0 +1,3 @@ +### Fixed + +- Abort issue-flow reconciliation when the board read fails instead of reporting a complete pass over an empty or partial result (#257). diff --git a/test/issueflow-reconcile.test.sh b/test/issueflow-reconcile.test.sh index 605577e..fc0ed67 100644 --- a/test/issueflow-reconcile.test.sh +++ b/test/issueflow-reconcile.test.sh @@ -1624,6 +1624,51 @@ sweep_run() { bash "$ROOT/actions/issueflow-reconcile/issueflow-reconcile.sh" 2>&1 } +# The board read is a precondition for the whole pass (#257). A failed read +# cannot be inferred from an empty result: its status is the only fact that +# separates an unreadable board from a clean one. Drive both through main(), +# including gh's partial-pagination shape where stdout is non-empty on error. +board_fixture="$SWEEP/repos_owner_repo_issues_state_open_per_page_100.json" +printf '%s\n' "$GH_STUB_ERROR_BODY" >"$board_fixture.http-error" +board_504_out="$(sweep_run)" +board_504_rc=$? +check "an issue-list 504 aborts the whole pass" 0 "" test "$board_504_rc" -eq 1 +check "...names the board read's stderr" 0 \ + "issueflow: could not read the issue board: $GH_STUB_STDERR" \ + printf '%s\n' "$board_504_out" +check "...writes no issue edit or comment" 1 "" test -s "$SWEEP/edits" +check "...never reports the pass reconciled" 1 "" \ + grep -qF 'issueflow: reconciled.' <<<"$board_504_out" + +board_silent_out="$(GH_STUB_STDERR="" sweep_run)" +board_silent_rc=$? +check "a silent issue-list failure still aborts" 0 "" test "$board_silent_rc" -eq 1 +check "...renders the empty stderr as a fact" 0 \ + 'issueflow: could not read the issue board: no error output' \ + printf '%s\n' "$board_silent_out" +check "...also writes nothing" 1 "" test -s "$SWEEP/edits" + +printf '[{"number":71}]\n' >"$board_fixture.http-error" +partial_board_out="$(sweep_run)" +partial_board_rc=$? +check "partial pagination aborts the whole pass" 0 "" \ + test "$partial_board_rc" -eq 1 +check "...does not reconcile the returned first page" 1 "" \ + grep -qF 'issue edit 71' "$SWEEP/edits" +check "...does not report the truncated pass reconciled" 1 "" \ + grep -qF 'issueflow: reconciled.' <<<"$partial_board_out" + +rm -f "$board_fixture.http-error" +sweep_board '[]' +empty_board_out="$(sweep_run)" +empty_board_rc=$? +check "a successful empty board stays green" 0 "" test "$empty_board_rc" -eq 0 +check "...writes nothing" 1 "" test -s "$SWEEP/edits" +check "...names the empty-board outcome" 0 'issueflow: no open issues.' \ + printf '%s\n' "$empty_board_out" +check "...still ends with byte-identical reconciled." 0 \ + 'issueflow: reconciled.' printf '%s\n' "$(tail -n1 <<<"$empty_board_out")" + sweep_board '[{"number":70},{"number":71}]' sweep_out="$(sweep_run)" sweep_rc=$?