Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 24 additions & 11 deletions scripts/apply-workflow-pins-remote.sh
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,10 @@ list_repos() {
# The REMOTE content is the only evidence: a local checkout can be arbitrarily
# stale, and reading one is what produced a false "285 callers track main"
# census on 2026-09-15.
# Returns non-zero when the answer is UNKNOWN (rate limit, 5xx, a file that
# would not download). Only a 404 is "no workflows". Returning 0 on failure
# made a rate-limited repo vanish from the census while `walked N` still
# counted it (measured 2026-10-02, shared 5,000/h token exhausted mid-run).
fetch_workflows() {
local repo="$1" dest="$2" owner="${1%%/*}" name="${1##*/}" resp
mkdir -p "$dest"
Expand Down Expand Up @@ -346,15 +350,17 @@ fetch_workflows() {
# query cannot express. The REMOTE content is the only evidence either way —
# reading a local checkout is what produced a false "285 callers track main"
# census on 2026-09-15.
local fname
gh api "repos/${repo}/contents/.github/workflows" \
--jq '.[] | select(.type == "file") | .name' 2>/dev/null \
| grep -E '\.ya?ml$' \
| while IFS= read -r fname; do
gh api "repos/${repo}/contents/.github/workflows/${fname}" \
-H 'Accept: application/vnd.github.raw' > "${dest}/${fname}" 2>/dev/null \
|| rm -f "${dest}/${fname}"
done
local fname listing
if ! listing=$(gh api "repos/${repo}/contents/.github/workflows" \
--jq '.[] | select(.type == "file") | .name' 2>&1); then
case "$listing" in *"HTTP 404"*) return 0 ;; esac
return 1
fi
for fname in $(printf '%s\n' "$listing" | grep -E '\.ya?ml$'); do
gh api "repos/${repo}/contents/.github/workflows/${fname}" \
-H 'Accept: application/vnd.github.raw' > "${dest}/${fname}" 2>/dev/null \
|| { rm -f "${dest}/${fname}"; return 1; }
done
return 0
}

Expand Down Expand Up @@ -449,13 +455,18 @@ main() {
WORKDIR=$(mktemp -d); trap 'rm -rf "${WORKDIR:-}"' EXIT
local repos n=0
if [ -n "$ONLY_REPO" ]; then repos="$ONLY_REPO"; else repos=$(list_repos); fi
[ -n "$repos" ] || { log "FATAL: enumerated zero repositories for ${OWNERS} (rate limit or auth?). Refusing to report an empty census."; exit 1; }

local repo
for repo in $repos; do
[ "$LIMIT" -gt 0 ] && [ "$n" -ge "$LIMIT" ] && break
n=$((n+1))
local rdir="${WORKDIR}/$(echo "$repo" | tr '/' '_')"
fetch_workflows "$repo" "$rdir"
if ! fetch_workflows "$repo" "$rdir"; then
log " FETCH-FAILED ${repo}: workflows could not be read; NOT classified"
printf '%s\t-\tFETCH-FAILED\t-\n' "$repo" >> "$tsv"
continue
fi
local found=0 changed=() wf base st detail
shopt -s nullglob
for wf in "$rdir"/*.yml "$rdir"/*.yaml; do
Expand Down Expand Up @@ -503,4 +514,6 @@ main() {
rm -f "$tsv"
}

main "$@"
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
main "$@"
fi
49 changes: 49 additions & 0 deletions tests/test_apply_workflow_pins_remote.sh
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,55 @@ kill_mutant illegal_repair_still_illegal \
's@\@\$\{target\}@\@@g' \
"FAIL illegal repair"

# --- 3. fetch_workflows fails CLOSED -----------------------------------------
# A rate-limited repo used to come back as "no workflows": fetch_workflows
# returned 0 on every failure, so the census dropped the repo while `walked N`
# still counted it (2026-10-02). Only a 404 may read as "nothing here".
echo "== 3. fetch failures are reported, not swallowed =="
STUB="$TMP/stub"; mkdir -p "$STUB"
cat > "$STUB/gh" <<'EOF'
#!/usr/bin/env bash
case "$*" in
"api graphql"*) exit 1 ;; # force the REST fallback
*contents/.github/workflows/*)
[ "${FILE_FAIL:-}" = 1 ] && { echo "gh: rate limit (HTTP 403)" >&2; exit 1; }
echo 'on: push'; exit 0 ;;
*contents/.github/workflows*)
case "${LIST:-200}" in
200) echo ci.yml; exit 0 ;;
404) echo "gh: Not Found (HTTP 404)" >&2; exit 1 ;;
*) echo "gh: API rate limit exceeded (HTTP 403)" >&2; exit 1 ;;
esac ;;
esac
exit 1
EOF
chmod +x "$STUB/gh"
# fetch_case <label> <want-rc> <env...> — run fetch_workflows against the stub.
fetch_case() {
local label="$1" want="$2"; shift 2
local got
env PATH="$STUB:$PATH" "$@" bash -c 'source "$1"; fetch_workflows o/r "$2"' _ \
"${APPLIER_UNDER_TEST:-$APPLIER}" "$TMP/fetch.$label" >/dev/null 2>&1
got=$?
if [ "$got" = "$want" ]; then pass "fetch: $label (rc=$got)"; else fail "fetch: $label — expected rc=$want, got rc=$got"; fi
}
fetch_case "listing ok" 0 LIST=200
fetch_case "no workflows dir (404)" 0 LIST=404
fetch_case "rate-limited listing" 1 LIST=403
fetch_case "file download fails" 1 LIST=200 FILE_FAIL=1
[ -s "$TMP/fetch.listing ok/ci.yml" ] && pass "fetch: file content written" || fail "fetch: ci.yml not written"

# Mutant: restore the old `return 0` on a failed listing. It must turn red.
M="$TMP/mutant_fail_open.sh"; cp "$APPLIER" "$M"
sed -i 's@^ return 1$@ return 0@' "$M"
if cmp -s "$APPLIER" "$M" || ! bash -n "$M"; then
fail "fail-open mutant did not apply cleanly"
else
out=$(APPLIER_UNDER_TEST="$M" fetch_case "MUTANT rate-limited listing" 1 LIST=403 2>&1)
case "$out" in *FAIL*) pass "mutant 'fetch_fail_open' killed by the rate-limit control" ;;
*) fail "mutant 'fetch_fail_open' stayed GREEN" ;; esac
fi

echo
if [ "$rc" -ne 0 ]; then echo "RESULT: FAILED" >&2; else echo "RESULT: all checks passed"; fi
exit $rc
Loading