fix(framework/tools): wrapper hardening — TLS validation, cred-path fallback, no-CI fast-exit (#551)
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/push/publish Pipeline was successful

This commit was merged in pull request #551.
This commit is contained in:
2026-06-20 10:16:38 +00:00
parent 87f561c1f8
commit 57919c38d8
12 changed files with 434 additions and 9 deletions

View File

@@ -72,6 +72,11 @@ elif values and all(v == "success" for v in values):
print("success")
elif any(v in {"pending", "running", "queued", "waiting"} for v in values):
print("pending")
elif not values and not state:
# No pipeline/status of any kind reported for this commit. Distinct from
# "unknown" (an ambiguous/unrecognized status that should keep polling):
# this signals a repo/commit that simply has no CI configured.
print("no-status")
else:
print("unknown")
PY
@@ -142,6 +147,21 @@ gitea_get_commit_status_json() {
curl -fsSL -H "User-Agent: curl/8" -H "Authorization: token ${token}" "$url"
}
gitea_get_default_branch() {
local host="$1"
local repo="$2"
local token="$3"
local url="https://${host}/api/v1/repos/${repo}"
curl -fsSL -H "User-Agent: curl/8" -H "Authorization: token ${token}" "$url" | python3 -c '
import json, sys
print((json.load(sys.stdin) or {}).get("default_branch", ""))
'
}
github_get_default_branch() {
gh api "repos/${OWNER}/${REPO}" --jq '.default_branch'
}
while [[ $# -gt 0 ]]; do
case "$1" in
-n|--number)
@@ -245,6 +265,51 @@ else
exit 1
fi
# No-CI determination is TWO-TIER (primary: CI history; secondary: empty-poll streak).
#
# PRIMARY — "does this repo run CI at all?" Probed once, up front, from the DEFAULT
# BRANCH's commit status. A repo whose default branch carries CI statuses
# demonstrably runs CI, so an EMPTY status on the PR head means the pipeline simply
# has not registered YET (webhook/queue lag) — NOT that the repo is CI-less. In that
# case we must NEVER fast-green; we keep polling until the pipeline registers or the
# timeout fires (both safe). This closes the webhook-lag false-green: a slow-to-
# register pipeline feeding a merge gate can no longer be mistaken for "no CI".
#
# SECONDARY — the empty-poll streak below applies ONLY to genuinely CI-less repos
# (default branch also has no CI history, e.g. device-imaging class), where burning
# the full timeout would be pure waste. There, NO_CI_MAX empty polls => fast-exit 0.
#
# Probe failure is treated conservatively as REPO_HAS_CI=1 (assume CI present): we
# would rather wait-then-timeout than risk a false-green, per the merge-gate priority.
REPO_HAS_CI=1
detect_repo_ci() {
local def_branch def_status
# Every early exit returns 0: a probe miss must leave the conservative
# REPO_HAS_CI=1 default in place, never abort the caller under `set -e`.
if [[ "$PLATFORM" == "github" ]]; then
def_branch=$(github_get_default_branch 2>/dev/null) || {
echo "[pr-ci-wait] WARN: default-branch probe failed; assuming CI-enabled (will not fast-green on empty status)."; return 0; }
[[ -n "$def_branch" ]] || return 0
def_status=$(github_get_commit_status_json "$OWNER" "$REPO" "$def_branch" 2>/dev/null | extract_state_from_status_json) || return 0
else
def_branch=$(gitea_get_default_branch "$HOST" "$OWNER/$REPO" "$TOKEN" 2>/dev/null) || {
echo "[pr-ci-wait] WARN: default-branch probe failed; assuming CI-enabled (will not fast-green on empty status)."; return 0; }
[[ -n "$def_branch" ]] || return 0
def_status=$(gitea_get_commit_status_json "$HOST" "$OWNER/$REPO" "$TOKEN" "$def_branch" 2>/dev/null | extract_state_from_status_json) || return 0
fi
if [[ "$def_status" == "no-status" || -z "$def_status" ]]; then
REPO_HAS_CI=0
echo "[pr-ci-wait] default branch '${def_branch}' has no CI status history — treating repo as CI-less (empty-poll fast-exit enabled)."
else
REPO_HAS_CI=1
echo "[pr-ci-wait] default branch '${def_branch}' has CI history (state=${def_status}) — repo runs CI; empty status on PR head => awaiting registration, will not fast-green."
fi
}
detect_repo_ci || true
NO_CI_STREAK=0
NO_CI_MAX=3
while true; do
NOW_TS=$(date +%s)
if (( NOW_TS > DEADLINE_TS )); then
@@ -272,11 +337,35 @@ while true; do
echo "Error: CI reported ${STATE} for PR #$PR_NUMBER." >&2
exit 1
;;
no-status)
if [[ "$REPO_HAS_CI" == "1" ]]; then
# PRIMARY tier: repo demonstrably runs CI but this commit's pipeline
# has not registered yet (webhook/queue lag). Do NOT fast-green — keep
# polling until it registers or the timeout fires. Reset the streak so
# a later genuine CI-less misread can't accumulate across this state.
NO_CI_STREAK=0
echo "[pr-ci-wait] empty status on PR head but repo runs CI — awaiting pipeline registration (webhook lag), not fast-greening."
else
# SECONDARY tier: genuinely CI-less repo (default branch has no CI
# history either). Empty polls => fast-exit green after NO_CI_MAX.
NO_CI_STREAK=$((NO_CI_STREAK + 1))
if (( NO_CI_STREAK >= NO_CI_MAX )); then
echo "[INFO] no CI configured for this repo/commit (PR #$PR_NUMBER, ${NO_CI_STREAK} consecutive empty polls, default branch also CI-less); treating as green."
exit 0
fi
fi
sleep "$INTERVAL_SEC"
;;
pending|unknown)
# A pipeline exists but hasn't reached a terminal state (or is
# transiently ambiguous) — keep waiting, and reset the no-CI streak
# since this commit is not in the "no CI at all" condition.
NO_CI_STREAK=0
sleep "$INTERVAL_SEC"
;;
*)
echo "[pr-ci-wait] Unrecognized state '${STATE}', continuing to poll..."
NO_CI_STREAK=0
sleep "$INTERVAL_SEC"
;;
esac