diff --git a/AGENTS.md b/AGENTS.md index ab1f3d9..db8e485 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -48,7 +48,7 @@ shellcheck dev/dev-poll.sh dev/dev-agent.sh dev/phase-test.sh \ review/review-poll.sh review/review-pr.sh \ gardener/gardener-poll.sh \ supervisor/supervisor-poll.sh supervisor/update-prompt.sh \ - lib/env.sh lib/ci-debug.sh lib/load-project.sh \ + lib/env.sh lib/ci-debug.sh lib/ci-helpers.sh lib/load-project.sh \ lib/parse-deps.sh lib/matrix_listener.sh # Run phase protocol test diff --git a/dev/dev-poll.sh b/dev/dev-poll.sh index 19053ea..8ce28f5 100755 --- a/dev/dev-poll.sh +++ b/dev/dev-poll.sh @@ -18,17 +18,7 @@ set -euo pipefail # Load shared environment (with optional project TOML override) export PROJECT_TOML="${1:-}" source "$(dirname "$0")/../lib/env.sh" - - -# Helper: check if CI is passing (or no CI configured) -ci_passed() { - local state="$1" - if [ "$state" = "success" ]; then return 0; fi - if [ "${WOODPECKER_REPO_ID:-2}" = "0" ] && { [ -z "$state" ] || [ "$state" = "pending" ] || [ "$state" = "unknown" ]; }; then - return 0 # no CI configured - fi - return 1 -} +source "$(dirname "$0")/../lib/ci-helpers.sh" # Track CI fix attempts per PR to avoid infinite respawn loops CI_FIX_TRACKER="${FACTORY_ROOT}/dev/ci-fixes-${PROJECT_NAME:-harb}.json" diff --git a/lib/ci-helpers.sh b/lib/ci-helpers.sh new file mode 100644 index 0000000..1356acc --- /dev/null +++ b/lib/ci-helpers.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# ci-helpers.sh — Shared CI helper functions +# +# Source from any script: source "$(dirname "$0")/../lib/ci-helpers.sh" +# Requires: WOODPECKER_REPO_ID (from env.sh / project config) + +# ci_passed — check if CI is passing (or no CI configured) +# Returns 0 if state is "success", or if no CI is configured and +# state is empty/pending/unknown. +ci_passed() { + local state="$1" + if [ "$state" = "success" ]; then return 0; fi + if [ "${WOODPECKER_REPO_ID:-2}" = "0" ] && { [ -z "$state" ] || [ "$state" = "pending" ] || [ "$state" = "unknown" ]; }; then + return 0 # no CI configured + fi + return 1 +} diff --git a/review/review-poll.sh b/review/review-poll.sh index 677e3df..128e885 100755 --- a/review/review-poll.sh +++ b/review/review-poll.sh @@ -10,6 +10,7 @@ set -euo pipefail # Usage: review-poll.sh [projects/harb.toml] export PROJECT_TOML="${1:-}" source "$(dirname "$0")/../lib/env.sh" +source "$(dirname "$0")/../lib/ci-helpers.sh" # shellcheck disable=SC2034 @@ -167,15 +168,10 @@ while IFS= read -r line; do "${API_BASE}/commits/${PR_SHA}/status" | jq -r '.state // "unknown"') # Skip if CI is running/failed. Allow "success" or no CI configured (empty/pending with no pipelines) - if [ "$CI_STATE" != "success" ]; then - # Projects without CI (woodpecker_repo_id=0) treat empty/pending as pass - if [ "${WOODPECKER_REPO_ID:-2}" = "0" ] && { [ "$CI_STATE" = "" ] || [ "$CI_STATE" = "pending" ]; }; then - : # no CI configured, proceed to review - else - log " #${PR_NUM} CI=${CI_STATE}, skip" - SKIPPED=$((SKIPPED + 1)) - continue - fi + if ! ci_passed "$CI_STATE"; then + log " #${PR_NUM} CI=${CI_STATE}, skip" + SKIPPED=$((SKIPPED + 1)) + continue fi # Check formal Codeberg reviews (not comment markers) diff --git a/review/review-pr.sh b/review/review-pr.sh index 61d2967..35d7a7e 100755 --- a/review/review-pr.sh +++ b/review/review-pr.sh @@ -23,6 +23,7 @@ set -euo pipefail # Load shared environment source "$(dirname "$0")/../lib/env.sh" +source "$(dirname "$0")/../lib/ci-helpers.sh" # Auto-pull factory code to pick up merged fixes before any logic runs git -C "$FACTORY_ROOT" pull --ff-only origin main 2>/dev/null || true @@ -179,14 +180,9 @@ status "checking CI" CI_STATE=$(curl -sf -H "Authorization: token ${CODEBERG_TOKEN}" \ "${API_BASE}/commits/${PR_SHA}/status" | jq -r '.state // "unknown"') -if [ "$CI_STATE" != "success" ]; then - # Projects without CI (woodpecker_repo_id=0) treat empty/pending as pass - if [ "${WOODPECKER_REPO_ID:-2}" = "0" ] && { [ -z "$CI_STATE" ] || [ "$CI_STATE" = "pending" ] || [ "$CI_STATE" = "unknown" ]; }; then - log "no CI configured, proceeding without CI gate" - else - log "SKIP: CI=${CI_STATE}" - exit 0 - fi +if ! ci_passed "$CI_STATE"; then + log "SKIP: CI=${CI_STATE}" + exit 0 fi # --- Check for existing reviews --- diff --git a/supervisor/supervisor-poll.sh b/supervisor/supervisor-poll.sh index 920111a..34894c3 100755 --- a/supervisor/supervisor-poll.sh +++ b/supervisor/supervisor-poll.sh @@ -13,6 +13,7 @@ # Log: tail -f /path/to/disinto/supervisor/supervisor.log source "$(dirname "$0")/../lib/env.sh" +source "$(dirname "$0")/../lib/ci-helpers.sh" LOGFILE="${FACTORY_ROOT}/supervisor/supervisor.log" STATUSFILE="/tmp/supervisor-status" @@ -462,7 +463,7 @@ check_project() { CI_STATE=$(codeberg_api GET "/commits/${PR_SHA}/status" 2>/dev/null | jq -r '.state // "unknown"' 2>/dev/null || true) MERGEABLE=$(echo "$PR_JSON" | jq -r '.mergeable // true') - if [ "$MERGEABLE" = "false" ] && [ "$CI_STATE" = "success" ]; then + if [ "$MERGEABLE" = "false" ] && ci_passed "$CI_STATE"; then p3 "${proj_name}: PR #${pr}: CI pass but merge conflict — needs rebase" elif [ "$CI_STATE" = "failure" ] || [ "$CI_STATE" = "error" ]; then UPDATED=$(echo "$PR_JSON" | jq -r '.updated_at // ""') @@ -472,7 +473,7 @@ check_project() { AGE_MIN=$(( (NOW_EPOCH - UPDATED_EPOCH) / 60 )) [ "$AGE_MIN" -gt 30 ] && p3 "${proj_name}: PR #${pr}: CI=${CI_STATE}, stale ${AGE_MIN}min" fi - elif [ "$CI_STATE" = "success" ]; then + elif ci_passed "$CI_STATE"; then HAS_REVIEW=$(codeberg_api GET "/issues/${pr}/comments?limit=50" 2>/dev/null | \ jq -r --arg sha "$PR_SHA" '[.[] | select(.body | contains("