From c44dd678eff46c5d45cfb9d1f0f0e25aa724f5bc Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Thu, 17 Sep 2026 20:02:42 +0000 Subject: [PATCH] =?UTF-8?q?fix(ci):=20drift=20detector=20=E2=80=94=20real?= =?UTF-8?q?=20repo=20slugs,=20no=20banner=20pollution,=20error=E2=89=A0dri?= =?UTF-8?q?ft=20(refs=20#708=20residuals)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three residual defects from #708, re-measured at HEAD and in the 2026-09-15 artifact (351/351 rows said _w; 67 of 418 'drifted entries' were [drift] clean banner lines): 1. Anonymised rows: the script wrote $(basename "$REPO_DIR") = _w. It now takes an optional REPO_SLUG arg 2 (documented in the header, stdout-versus-stderr contract included), and the workflow passes it the real repo slug. 2. Banner pollution: the trailing '[drift] clean' echo went to STDOUT, landing in drift-report.tsv and inflating the row count. It now goes to stderr (stdout is data-only by contract), and the workflow's rows count counts tab-bearing lines minus the header, so a future banner can never inflate the number again. 3. Error-as-drift: exit 2 (usage/env error) was counted identically to exit 1 (drift). Now split: rc=1 → drifted, anything else → errors, with the count surfaced in the step summary AND the tracking-issue table ('check errors (rc≠0,1)' row). Validation: bash -n on script + extracted sweep block; PyYAML parse; fixture test — drifted fixture emits 'owner/myrepoci.yml…' and exits 1; clean fixture emits empty stdout with banner on stderr and exits 0. Does NOT close #708 (estate re-run pending) but completes its standards-side remainder. --- .github/workflows/lockfile-drift-detect.yml | 24 +++++++++++++++++---- scripts/check-lockfile-drift.sh | 16 ++++++++++---- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/.github/workflows/lockfile-drift-detect.yml b/.github/workflows/lockfile-drift-detect.yml index 0785ccbc8..782c98dea 100644 --- a/.github/workflows/lockfile-drift-detect.yml +++ b/.github/workflows/lockfile-drift-detect.yml @@ -75,7 +75,7 @@ jobs: mv r.tmp repos.txt fi - scanned=0; withlock=0; drifted=0 + scanned=0; withlock=0; drifted=0; errors=0 while read -r r; do scanned=$((scanned+1)) # Cheap probe first: skip repos with no lockfile (that is failure @@ -90,21 +90,35 @@ jobs: "https://x-access-token:${GH_TOKEN}@github.com/hyperpolymath/$r.git" _w 2>/dev/null; then continue fi - if ! bash scripts/check-lockfile-drift.sh _w >> drift-report.tsv 2>/dev/null; then + # Exit-code honesty (issue #708): 1 = drift found, 2 = usage/env + # error. The old `if !` folded both into 'drifted', so a broken + # clone counted as a drifted repo. The slug arg gives column 1 a + # real identity; without it every row said `_w`. + rc=0 + bash scripts/check-lockfile-drift.sh _w "$r" >> drift-report.tsv 2>> drift-errors.log || rc=$? + if [ "$rc" -eq 1 ]; then drifted=$((drifted+1)) + elif [ "$rc" -ne 0 ]; then + errors=$((errors+1)) + echo "::warning::$r: drift check errored (rc=$rc) — see drift-errors.log" fi rm -rf _w done < repos.txt - rows=$(( $(wc -l < drift-report.tsv) - 1 )) + # Count data rows only: lines bearing a TAB, minus the header. + # (wc -l − 1 counted banner lines as drift entries when banners + # still leaked into stdout — 67 of 418 'entries' in run 2026-09-15 + # were `[drift] clean` lines, issue #708.) + rows=$(( $(grep -c $'\t' drift-report.tsv || true) - 1 )) { echo "total=$TOTAL" echo "scanned=$scanned" echo "withlock=$withlock" echo "drifted=$drifted" + echo "errors=$errors" echo "rows=$rows" } >> "$GITHUB_OUTPUT" - echo "scanned=$scanned withlock=$withlock drifted=$drifted rows=$rows" + echo "scanned=$scanned withlock=$withlock drifted=$drifted errors=$errors rows=$rows" - name: Upload report if: always() @@ -126,6 +140,7 @@ jobs: echo "| scanned | ${{ steps.sweep.outputs.scanned }} |" echo "| carrying a lockfile | ${{ steps.sweep.outputs.withlock }} |" echo "| **with drift** | **${{ steps.sweep.outputs.drifted }}** |" + echo "| check errors (rc≠0,1) | ${{ steps.sweep.outputs.errors }} |" echo "| drifted entries | ${{ steps.sweep.outputs.rows }} |" echo echo '```' @@ -163,6 +178,7 @@ jobs: echo "| scanned | ${{ steps.sweep.outputs.scanned }} |" echo "| carrying a lockfile | ${{ steps.sweep.outputs.withlock }} |" echo "| **with drift** | **${{ steps.sweep.outputs.drifted }}** |" + echo "| check errors (rc≠0,1) | ${{ steps.sweep.outputs.errors }} |" echo "| drifted entries | ${{ steps.sweep.outputs.rows }} |" } > issue-body.md body="$(cat issue-body.md)" diff --git a/scripts/check-lockfile-drift.sh b/scripts/check-lockfile-drift.sh index 67be4bf74..bd01e5d19 100755 --- a/scripts/check-lockfile-drift.sh +++ b/scripts/check-lockfile-drift.sh @@ -38,13 +38,21 @@ set -eo pipefail # not "this is why CI is down". The strong claim is the converse and it holds: # every workflow that WAS dead had a drifted entry. # -# Usage: check-lockfile-drift.sh [REPO_DIR] (default: .) -# Output: TSV — repo workflow requested locked +# Usage: check-lockfile-drift.sh [REPO_DIR] [REPO_SLUG] +# REPO_DIR default: . — the checkout to inspect +# REPO_SLUG default: basename(REPO_DIR) — the identity written to +# column 1. The caller MUST pass this when REPO_DIR is a +# throwaway clone (e.g. `_w`): otherwise every row says +# `_w` and the report is untraceable (issue #708). +# Output: TSV on stdout — repo workflow requested locked +# (all banners/notices go to stderr — stdout is data ONLY, so the +# file can be appended straight into the report) # Exit: 0 = no drift (or no lockfile — not this script's business) # 1 = drift found # 2 = usage / environment error REPO_DIR="${1:-.}" +REPO_SLUG="${2:-$(basename "$REPO_DIR")}" LOCK="$REPO_DIR/.github/workflows/actions.lock" WFDIR="$REPO_DIR/.github/workflows" @@ -111,7 +119,7 @@ for wf in "$WFDIR"/*.yml "$WFDIR"/*.yaml; do [ "$resolved" = "$ref" ] && continue # same commit, different notation fi - printf '%s\t%s\t%s\t%s\n' "$(basename "$REPO_DIR")" "$base" "$want" "$have" + printf '%s\t%s\t%s\t%s\n' "$REPO_SLUG" "$base" "$want" "$have" drift=$((drift + 1)) done < /tmp/_drift_want.$$ @@ -124,5 +132,5 @@ if [ "$drift" -gt 0 ]; then exit 1 fi -echo "[drift] clean — $checked workflow(s) checked in $REPO_DIR" +echo "[drift] clean — $checked workflow(s) checked in $REPO_DIR" >&2 exit 0