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
69 changes: 62 additions & 7 deletions scripts/apply-workflow-pins-remote.sh
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@
# (6 of 9 campaign heads unsigned). An unsigned applier opens PRs that can
# never merge, which is worse than opening none.
# * Shell-only: no Python, no Ruby.
# * THE LOCK FOLLOWS THE PINS. A repo's actions.lock is fetched with its
# workflows and re-keyed by scripts/relock-standards-pin.sh in the same
# commit; a lock that cannot follow (fail-closed refusal) means the repo is
# reported LOCK-REFUSED and not landed. Pins without their lock stop the
# workflows at startup, which is worse than leaving them behind.
#
# Usage:
# bash scripts/apply-workflow-pins-remote.sh [options]
Expand Down Expand Up @@ -76,6 +81,10 @@
OUT_FILE=""
LIMIT=0
BRANCH_NAME="chore/re-point-standards-workflow-pins"
# The standards checkout this script runs from: the relock reads reusables and
# standards' own actions.lock AT THE TARGET from it.
STANDARDS_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
RELOCK="$(dirname "${BASH_SOURCE[0]}")/relock-standards-pin.sh"

log() { printf '%s\n' "$*" >&2; }

Expand Down Expand Up @@ -260,6 +269,16 @@
echo " FAIL illegal repair did not yield a legal pin: $(cat "$d/illegal.yml")" >&2; rc=1
fi

# The relock is what keeps a fix-mode PR from desyncing actions.lock; a
# missing relock must stop the run, not silently land pins without locks.
# Its own known-answer suite is scripts/tests/relock-standards-pin-test.sh.
echo "control: the lock relock is present"
if [ -f "$RELOCK" ] && bash -n "$RELOCK"; then

Check failure on line 276 in scripts/apply-workflow-pins-remote.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaE6GuUvJdyNn6mh5ZhD&open=AaE6GuUvJdyNn6mh5ZhD&pullRequest=1254
echo " PASS $(basename "$RELOCK") present and parses"
else
echo " FAIL $RELOCK missing or unparseable" >&2; rc=1
fi

rm -rf "$d"
return $rc
}
Expand Down Expand Up @@ -345,14 +364,19 @@
rm -f "$all" "$part"
}

# fetch_workflows <repo> <destdir> — download .github/workflows/*.y*ml.
# fetch_workflows <owner/repo> <destdir> — download .github/workflows/*.yml,
# *.yaml and actions.lock, when present, into destdir.
# 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).
# Returns 0 for a successful GraphQL response, including an empty tree. Blobs
# without text are skipped; decoding failures remove the destination file
# without changing the success status. In the REST fallback, returns 0 for
# a missing directory (HTTP 404), and 1 for other listing or download failures;
# files already downloaded remain in destdir on failure. 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 All @@ -379,7 +403,7 @@
done < <(jq -r '
(.data.repository.object.entries // [])[]
| select(.type == "blob")
| select(.name | test("\\.ya?ml$"))
| select((.name | test("\\.ya?ml$")) or .name == "actions.lock")
| select(.object.text != null)
| "\(.name)\t\(.object.text | @base64)"' "$resp" 2>/dev/null)
rm -f "$resp"
Expand All @@ -399,7 +423,7 @@
case "$listing" in *"HTTP 404"*) return 0 ;; esac
return 1
fi
for fname in $(printf '%s\n' "$listing" | grep -E '\.ya?ml$'); do
for fname in $(printf '%s\n' "$listing" | grep -E '\.ya?ml$|^actions\.lock$'); 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; }
Expand Down Expand Up @@ -475,6 +499,14 @@
# ---------------------------------------------------------------------------
# MAIN
# ---------------------------------------------------------------------------
# main [options] — audit remote standards pins and write a TSV census to
# stdout, or to --out. Under --fix, fetch the target commit locally if needed
# and create remote branches, commits and PRs for changed workflows and locks.
# Fetch, relock and PR failures become census statuses and processing continues;
# a relock failure prevents that repository's changes from being submitted.
# Recorded repository failures do not cause a non-zero exit status.
# Exits 1 on failed controls, authentication, target validation/fetch or repository
# enumeration, and 2 on an unknown option; --self-test exits with its test status.
main() {
while [ $# -gt 0 ]; do
case "$1" in
Expand Down Expand Up @@ -505,6 +537,12 @@
TARGET_SHA=$(resolve_target)
[ -n "$TARGET_SHA" ] || { log "FATAL: could not resolve a target SHA."; exit 1; }
validate_target "$TARGET_SHA" || exit 1
# The relock reads the target's reusables and lock from this checkout; a
# shallow CI checkout may hold a different commit, so fetch it if needed.
if [ "$DO_FIX" -eq 1 ] && ! git -C "$STANDARDS_ROOT" cat-file -e "${TARGET_SHA}^{commit}" 2>/dev/null; then

Check failure on line 542 in scripts/apply-workflow-pins-remote.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaE6GuUvJdyNn6mh5ZhE&open=AaE6GuUvJdyNn6mh5ZhE&pullRequest=1254
git -C "$STANDARDS_ROOT" fetch -q --depth=1 origin "$TARGET_SHA" 2>/dev/null \
|| { log "FATAL: ${TARGET_SHA} is not in ${STANDARDS_ROOT} and could not be fetched; the lock relock needs it."; exit 1; }
fi
log "== target: ${TARGET_SHA} (reachable from ${STANDARDS_REPO} main) =="
[ "$DO_FIX" -eq 1 ] && log "== MODE: --fix (will open PRs) ==" || log "== MODE: audit (writes nothing) =="

Expand Down Expand Up @@ -557,6 +595,23 @@
shopt -u nullglob
[ "$found" -eq 0 ] && continue

# The lock must follow the pins. Re-pointing `uses:` alone leaves
# actions.lock keyed to the old standards commit: check-lock-sync.sh
# rejects it and GitHub refuses the run at startup (jobs=0). Measured
# 2026-10-14: 200 of 368 governance callers carry a lock, and this applier
# used to fetch and commit only *.yml, so every fix-mode PR to one of them
# would have stopped its reusable-calling workflows. relock is fail-closed;
# a repo whose lock cannot follow is reported and NOT landed.
if [ "$DO_FIX" -eq 1 ] && [ "${#changed[@]}" -gt 0 ] && [ -f "$rdir/actions.lock" ]; then

Check failure on line 605 in scripts/apply-workflow-pins-remote.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaE6GuUvJdyNn6mh5ZhF&open=AaE6GuUvJdyNn6mh5ZhF&pullRequest=1254

Check failure on line 605 in scripts/apply-workflow-pins-remote.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaE6GuUvJdyNn6mh5ZhG&open=AaE6GuUvJdyNn6mh5ZhG&pullRequest=1254

Check failure on line 605 in scripts/apply-workflow-pins-remote.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaE6GuUvJdyNn6mh5ZhH&open=AaE6GuUvJdyNn6mh5ZhH&pullRequest=1254
local lock_before; lock_before=$(cat "$rdir/actions.lock")
if ! bash "$RELOCK" --to "$TARGET_SHA" --standards "$STANDARDS_ROOT" "$rdir" >&2; then
log " LOCK-REFUSED ${repo}: actions.lock cannot follow the new pins; NOT landed"
printf '%s\t-\tLOCK-REFUSED\t-\n' "$repo" >> "$tsv"
continue
fi
[ "$lock_before" != "$(cat "$rdir/actions.lock")" ] && changed+=("actions.lock")

Check failure on line 612 in scripts/apply-workflow-pins-remote.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaE6GuUvJdyNn6mh5ZhI&open=AaE6GuUvJdyNn6mh5ZhI&pullRequest=1254
fi

if [ "$DO_FIX" -eq 1 ] && [ "${#changed[@]}" -gt 0 ]; then
local url
if url=$(land_pr "$repo" "$rdir" "${changed[@]}"); then
Expand Down
42 changes: 41 additions & 1 deletion scripts/propagate-workflow-pins.sh
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
# ---------------------------------------------------------------------------

FIX_MODE=false
LOCK_REFUSED=0
TARGET_SHA="${STANDARDS_TARGET_SHA:-}"
BRANCH="chore/bump-standards-pins"
TARGET_PATH="."
Expand Down Expand Up @@ -174,14 +175,26 @@
# Per-repo processing
# ---------------------------------------------------------------------------

# process_repo REPO — print an audit of matching standards SHA pins in the
# local checkout's workflows; do nothing if its workflows directory is absent.
# Under --fix, rewrite pins to TARGET_SHA and relock an existing actions.lock
# when workflows change. A relock failure increments LOCK_REFUSED for the
# script's final exit status, restores each rewritten workflow to its content
# before this run (uncommitted edits included), and skips branch selection and
# staging. Otherwise, selects BRANCH and stages the entire workflows directory
# when pins change in a Git checkout; branch/staging failures are ignored.
# Returns 0 after processing without committing or pushing. Temporary directory
# creation, workflow backup, rewrite or restoration errors terminate the script.
process_repo() {
local repo="$1" name
name="$(basename "$repo")"
local wfdir="$repo/.github/workflows"
[ -d "$wfdir" ] || return 0

local total_behind=0 files_touched=0
local f base behind
local f base behind orig=""
# The originals of the files rewritten below, restored if the lock refuses.
if $FIX_MODE; then orig="$(mktemp -d)"; fi
for f in "$wfdir"/*.yml "$wfdir"/*.yaml; do
[ -f "$f" ] || continue
grep -qE "$PIN_RE" "$f" || continue
Expand All @@ -192,13 +205,35 @@
total_behind=$((total_behind + behind))
files_touched=$((files_touched + 1))
if $FIX_MODE; then
cp -p "$f" "$orig/$base"
rewrite_file "$f"
fi
else
printf '%-30s\t%-28s\tFRESH\t-> %s\n' "$name" "$base" "${TARGET_SHA:0:12}"
fi
done

# The lock must follow the pins. Rewriting `uses:` alone leaves actions.lock
# keyed to the old standards commit, which check-lock-sync.sh rejects and
# GitHub refuses at startup (jobs=0). relock-standards-pin.sh is fail-closed:
# it leaves the lock untouched and exits 1 when it cannot relock correctly.
if $FIX_MODE && [ "$files_touched" -gt 0 ] && [ -f "$wfdir/actions.lock" ]; then

Check failure on line 220 in scripts/propagate-workflow-pins.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaE6GuWUJdyNn6mh5Zhc&open=AaE6GuWUJdyNn6mh5Zhc&pullRequest=1254

Check failure on line 220 in scripts/propagate-workflow-pins.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaE6GuWUJdyNn6mh5Zhd&open=AaE6GuWUJdyNn6mh5Zhd&pullRequest=1254
if bash "$(dirname "${BASH_SOURCE[0]}")/relock-standards-pin.sh" --to "$TARGET_SHA" "$wfdir" >&2; then
printf '%-30s\t%-28s\t%s\t-> %s\n' "$name" "actions.lock" "RELOCKED" "${TARGET_SHA:0:12}"
else
printf '%-30s\t%-28s\t%s\t-> %s\n' "$name" "actions.lock" "LOCK-REFUSED" "${TARGET_SHA:0:12}"
LOCK_REFUSED=$((LOCK_REFUSED + 1))
Comment thread
coderabbitai[bot] marked this conversation as resolved.
# New pins over the old lock is the state the relock exists to prevent:
# put this repo's workflows back as they were, and leave its index and
# branch untouched.
cp -p "$orig"/* "$wfdir"/
rm -rf "$orig"
log " [not staged] $name — lock refused; workflows restored"
return 0
fi
fi
[ -z "$orig" ] || rm -rf "$orig"

Check failure on line 235 in scripts/propagate-workflow-pins.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaE6L-gXDFmiq9E4aV6n&open=AaE6L-gXDFmiq9E4aV6n&pullRequest=1254

if $FIX_MODE && [ "$files_touched" -gt 0 ] && git -C "$repo" rev-parse --git-dir >/dev/null 2>&1; then
local current_branch
current_branch=$(git -C "$repo" branch --show-current 2>/dev/null || echo "")
Expand Down Expand Up @@ -243,3 +278,8 @@
else
echo "# Re-run with --fix to stage the bump on a branch (no commit/push)."
fi
if [ "$LOCK_REFUSED" -gt 0 ]; then

Check failure on line 281 in scripts/propagate-workflow-pins.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=hyperpolymath_standards&issues=AaE6GuWUJdyNn6mh5Zhe&open=AaE6GuWUJdyNn6mh5Zhe&pullRequest=1254
echo "# LOCK-REFUSED in $LOCK_REFUSED repo(s): actions.lock could not follow the pins; workflows restored, staging skipped."
echo "# See relock-standards-pin.sh's message above."
exit 1
fi
Loading
Loading