Repository navigation
feat(scripts): land actions.lock repair generators with mutation-checked tests #1116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| #!/usr/bin/env bash | ||
| # SPDX-License-Identifier: MPL-2.0 | ||
| # SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk> | ||
| # | ||
| # close-lock.sh — make actions.lock TRANSITIVELY CLOSED. | ||
| # | ||
| # MEASURED on hyperpolymath/cicd-squabbler#101: GitHub rejects a run at startup | ||
| # (jobs=0) when the lockfile contains a DANGLING edge — a ref named under | ||
| # `workflows:` or inside another record's nested `uses:` that has no top-level | ||
| # record of its own. Three workflows that ran with jobs on `pull_request` on | ||
| # 2026-09-21 went startup_failure once a job-level ref was listed without | ||
| # closure, and adding the record but not its nested refs' records did not cure | ||
| # it. metadatastician/burble's working lockfile has zero dangling edges. | ||
| # | ||
| # Leaf records are written without a nested `uses:` list, so one pass suffices. | ||
| # | ||
| # Lock repair order: gh actions-lock → relock-sha-keys.sh → complete-job-refs.sh | ||
| # → close-lock.sh → prune-stale.sh (see complete-job-refs.sh). | ||
| # | ||
| # Every value taken from the API is SHAPE-checked before it is written: a 40-hex | ||
| # commit and two integer ids. On an HTTP error `gh api` can put the error body | ||
| # on stdout, and a record built from `{"message"…` would be a present-but- | ||
| # unresolvable entry, which is fatal where an absent one is merely dangling. | ||
| # A ref that cannot be resolved is SKIPPED, reported, and makes the exit 3. | ||
| # GH_BIN overrides the `gh` binary (the test suite uses an offline stub). | ||
| set -euo pipefail | ||
| WF="${1:-.github/workflows}" | ||
| L="$WF/actions.lock" | ||
| GH_BIN="${GH_BIN:-gh}" | ||
| [ -f "$L" ] || { echo "close-lock: no $L" >&2; exit 1; } | ||
| SC="$(mktemp -d)"; trap 'rm -rf "$SC"' EXIT | ||
| # keys: list the dependency record keys already present in the lock. | ||
| keys() { awk '/^dependencies:/{f=1;next} f&&match($0,/^ '\''[^'\'']+'\'':$/){k=$0;sub(/^ '\''/,"",k);sub(/'\'':$/,"",k);print k}' "$L" | LC_ALL=C sort -u; } | ||
| # nested: list the refs that dependency records themselves depend on. | ||
| nested() { awk '/^dependencies:/{f=1} f&&match($0,/^ - '\''[^'\'']+'\''/){k=$0;sub(/^ - '\''/,"",k);sub(/'\''.*$/,"",k);print k}' "$L" | LC_ALL=C sort -u; } | ||
| # wfrefs: list every ref named under the workflows: section. | ||
| wfrefs() { awk '/^workflows:/{f=1;next} f&&/^[a-z_]+:/{f=0} f&&match($0,/^ - '\''[^'\'']+'\''/){k=$0;sub(/^ - '\''/,"",k);sub(/'\''.*$/,"",k);print k}' "$L" | LC_ALL=C sort -u; } | ||
|
|
||
| skipped=0 | ||
| for pass in 1 2 3 4 5; do | ||
| keys > "$SC/have" | ||
| { nested; wfrefs; } | LC_ALL=C sort -u > "$SC/want" | ||
| LC_ALL=C comm -23 "$SC/want" "$SC/have" > "$SC/miss" | ||
| n=$(wc -l < "$SC/miss") | ||
| echo "close-lock: pass $pass — dangling refs: $n" | ||
| [ "$n" -eq 0 ] && break | ||
| : > "$SC/new" | ||
| while read -r k; do | ||
| [ -n "$k" ] || continue | ||
| owner="${k%%/*}"; rest="${k#*/}"; name="${rest%@*}"; ref="${k##*@}" | ||
| ids=$("$GH_BIN" api "repos/$owner/$name" --jq '"\(.owner.id) \(.id)"') || ids="" | ||
| if ! printf '%s' "$ids" | grep -qxE '[0-9]+ [0-9]+'; then | ||
| echo " SKIP $k (repo lookup failed or malformed)"; skipped=$((skipped + 1)); continue | ||
| fi | ||
| sha="" | ||
| if printf '%s' "$ref" | grep -qxE '[0-9a-f]{40}'; then sha="$ref" | ||
| else sha=$("$GH_BIN" api "repos/$owner/$name/commits/$ref" --jq .sha) || sha=""; fi | ||
| if ! printf '%s' "$sha" | grep -qxE '[0-9a-f]{40}'; then | ||
| echo " SKIP $k (cannot resolve commit)"; skipped=$((skipped + 1)); continue | ||
| fi | ||
| printf " '%s':\n ref: '%s'\n commit: 'sha1-%s'\n owner_id: %s\n repo_id: %s\n" \ | ||
| "$k" "$ref" "$sha" "${ids%% *}" "${ids##* }" >> "$SC/new" | ||
| echo " + $k" | ||
| done < "$SC/miss" | ||
| [ -s "$SC/new" ] || break | ||
| awk -v newf="$SC/new" ' | ||
| BEGIN { nn=0; while ((getline l < newf) > 0) { | ||
| if (l ~ /^ '\''[^'\'']+'\'':$/) { nn++; k=l; sub(/^ '\''/,"",k); sub(/'\'':$/,"",k); NK[nn]=k; NB[nn]=l } | ||
| else NB[nn] = NB[nn] "\n" l } } | ||
| # emit_lt: print each pending new record whose key sorts before key. | ||
| function emit_lt(key, i) { for (i=1;i<=nn;i++) if (!used[i] && NK[i] < key) { print NB[i]; used[i]=1 } } | ||
| /^dependencies:[[:space:]]*$/ { print; indep=1; next } | ||
| indep && /^[a-z_]+:/ { for (i=1;i<=nn;i++) if (!used[i]) { print NB[i]; used[i]=1 } indep=0; print; next } | ||
| indep && /^ '\''[^'\'']+'\'':$/ { k=$0; sub(/^ '\''/,"",k); sub(/'\'':$/,"",k); emit_lt(k); print; next } | ||
| { print } | ||
| END { if (indep) for (i=1;i<=nn;i++) if (!used[i]) print NB[i] }' "$L" > "$SC/new.lock" && cat "$SC/new.lock" > "$L" | ||
| done | ||
| if [ "$skipped" -gt 0 ]; then | ||
| echo "close-lock: $skipped dangling ref(s) could not be resolved; lock is NOT closed" >&2 | ||
| exit 3 | ||
| fi | ||
| echo "close-lock: done" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| #!/usr/bin/env bash | ||
| # SPDX-License-Identifier: MPL-2.0 | ||
| # SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk> | ||
| # | ||
| # complete-job-refs.sh — add job-level reusable-workflow refs to actions.lock. | ||
| # `gh actions-lock` v0.1.6 is blind to job-level `uses:` (upstream #129), so the | ||
| # entries it cannot write are written here. Idempotent; run from a repo root. | ||
| # | ||
| # Lock repair order (each step idempotent, run from a repo root): | ||
| # gh actions-lock → scripts/relock-sha-keys.sh → scripts/complete-job-refs.sh | ||
| # → scripts/close-lock.sh → scripts/prune-stale.sh | ||
| # relock-sha-keys re-keys tag-spelled entries to the inline SHA the YAML pins | ||
| # (GitHub compares lock and YAML as LITERAL strings); complete-job-refs adds the | ||
| # job-level reusable refs the tool cannot see; close-lock adds a record for every | ||
| # dangling edge; prune-stale drops per-workflow refs nothing uses. | ||
| set -euo pipefail | ||
| WF="${1:-.github/workflows}" | ||
| L="$WF/actions.lock" | ||
| [ -f "$L" ] || { echo "complete-job-refs: no $L" >&2; exit 1; } | ||
| TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT | ||
| WANT="$TMP/want"; NEW="$TMP/new.lock" | ||
| # 1. enumerate job-level refs actually present in the YAML, as "path<TAB>owner/repo@ref" | ||
| : > "$WANT" | ||
| for f in "$WF"/*.yml "$WF"/*.yaml; do | ||
| [ -e "$f" ] || continue | ||
| b=".github/workflows/$(basename "$f")" | ||
| set +e | ||
| sed 's/[[:space:]]\+#.*$//' "$f" \ | ||
| | grep -E '^[[:space:]]*(-[[:space:]]+)?uses:[[:space:]]' \ | ||
| | sed -E 's/^[[:space:]]*(-[[:space:]]+)?uses:[[:space:]]+//' \ | ||
| | tr -d '"'"'"'' | awk '{print $1}' \ | ||
| | grep -E '/\.github/workflows/' \ | ||
| | while read -r u; do | ||
| case "$u" in ./*|\$/*|docker://*|'') continue;; esac | ||
| case "$u" in *@*) : ;; *) continue;; esac | ||
| p="${u%@*}"; r="${u##*@}" | ||
| o=$(printf '%s' "$p" | cut -d/ -f1); n=$(printf '%s' "$p" | cut -d/ -f2) | ||
| printf '%s\t%s/%s@%s\n' "$b" "$o" "$n" "$r" | ||
| done | ||
| set -e | ||
| done | LC_ALL=C sort -u >> "$WANT" | ||
|
|
||
| # 2. rewrite the workflows: section, merging wanted job refs into each path entry | ||
| LC_ALL=C awk -v want="$WANT" ' | ||
| BEGIN { while ((getline l < want) > 0) { split(l, w, "\t"); WANT[w[1]] = WANT[w[1]] SUBSEP w[2] } } | ||
| # flush: write the current workflow entry, merged with its wanted refs, sorted and deduplicated. | ||
| function flush( i, n, arr, j, k, seen, out, c) { | ||
| if (cur == "") return | ||
| SEEN[cur] = 1 | ||
| n = split(items, arr, SUBSEP) | ||
| c = 0; delete seen; delete out | ||
| for (i = 1; i <= n; i++) if (arr[i] != "" && !(arr[i] in seen)) { seen[arr[i]] = 1; out[++c] = arr[i] } | ||
| if (cur in WANT) { n = split(WANT[cur], arr, SUBSEP) | ||
| for (i = 1; i <= n; i++) if (arr[i] != "" && !(arr[i] in seen)) { seen[arr[i]] = 1; out[++c] = arr[i] } } | ||
| if (c == 0) { printf " '\''%s'\'': []\n", cur; cur = ""; items = ""; return } | ||
| for (i = 1; i < c; i++) for (j = i + 1; j <= c; j++) if (out[j] < out[i]) { k = out[i]; out[i] = out[j]; out[j] = k } | ||
| printf " '\''%s'\'':\n", cur | ||
| for (i = 1; i <= c; i++) printf " - '\''%s'\''\n", out[i] | ||
| cur = ""; items = "" | ||
| } | ||
| # rest: create entries for wanted workflow paths the lock has no entry for yet. | ||
| function rest( k, ks, c, i, j, t) { | ||
| c = 0; for (k in WANT) if (!(k in SEEN)) ks[++c] = k | ||
| for (i = 1; i < c; i++) for (j = i + 1; j <= c; j++) if (ks[j] < ks[i]) { t = ks[i]; ks[i] = ks[j]; ks[j] = t } | ||
| for (i = 1; i <= c; i++) { cur = ks[i]; items = ""; flush() } | ||
| } | ||
| /^workflows:[[:space:]]*$/ { print; inwf = 1; sawwf = 1; next } | ||
| inwf && /^[a-z_]+:/ { flush(); rest(); inwf = 0; print; next } | ||
| inwf { | ||
| if (match($0, /^ '\''[^'\'']+'\'':/)) { flush() | ||
| cur = $0; sub(/^ '\''/, "", cur); sub(/'\''.*$/, "", cur) | ||
| items = "" | ||
| next | ||
| } | ||
| if (match($0, /^ - '\''[^'\'']+'\''[[:space:]]*$/)) { | ||
| it = $0; sub(/^ - '\''/, "", it); sub(/'\''[[:space:]]*$/, "", it) | ||
| items = items SUBSEP it; next | ||
| } | ||
| next | ||
| } | ||
| { print } | ||
| END { | ||
| if (inwf) { flush(); rest() } | ||
| if (!sawwf) for (k in WANT) { print "complete-job-refs: lock has no workflows: section" > "/dev/stderr"; exit 2 } | ||
| } | ||
| ' "$L" > "$NEW" | ||
|
|
||
| added=$(LC_ALL=C comm -13 <(grep -oE "^ - '[^']+'" "$L" | sed "s/.*- '//;s/'$//" | LC_ALL=C sort -u) \ | ||
| <(grep -oE "^ - '[^']+'" "$NEW" | sed "s/.*- '//;s/'$//" | LC_ALL=C sort -u) | wc -l) | ||
| cat "$NEW" > "$L" | ||
| echo "complete-job-refs: distinct refs newly present in workflows section: $added" | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,173 @@ | ||
| #!/usr/bin/env bash | ||
| # SPDX-License-Identifier: MPL-2.0 | ||
| # SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk> | ||
| # | ||
| # prune-stale.sh — remove workflow-entry refs from .github/workflows/actions.lock | ||
| # that no `uses:` in that workflow references. | ||
| # | ||
| # ── Why pruning, and not demoting the check ───────────────────────────────── | ||
| # The per-repo lock-sync gate (check-lock-sync.sh) clause 2 hard-FAILs on | ||
| # "stale lockfile entries, no uses: references them". Measured 2026-09-22 on | ||
| # hyperpolymath/my-lang, that condition is NOT fatal to GitHub: six workflows there share | ||
| # the identical stale entry 'hyperpolymath/standards@08586a12…' and produce | ||
| # THREE different outcomes — | ||
| # secret-scanner + spark-theatre-gate success x3, governance + hypatia-scan | ||
| # failure x3 (jobs>0), mirror + scorecard startup_failure x3. One property, | ||
| # three outcomes, so the property is not the cause. | ||
| # | ||
| # So the gate reds on a harmless condition. There are two cures; this is the | ||
| # one that repairs rather than silences: make the lock true, and leave the gate | ||
| # strict. A `::warning::` or a demotion to advisory would be a vacuous gate. | ||
| # | ||
| # ── Why pruning is safe ───────────────────────────────────────────────────── | ||
| # A workflow entry is a per-path list of the refs GitHub validates for THAT | ||
| # workflow. Transitive dependencies live in the top-level `dependencies:` map, | ||
| # never in a per-workflow list, so removing a per-workflow ref cannot open a | ||
| # dangling edge — and check-lock-sync clause 3 re-verifies closure afterwards | ||
| # regardless. An entry whose ref is referenced by no `uses:` in its own file | ||
| # cannot be satisfying anything there. | ||
| # | ||
| # ── Two independent oracles must agree before a ref is dropped ────────────── | ||
| # 1. the PARSER: no `uses:` in that workflow normalises to this owner/repo@ref | ||
| # (same norm()/ck() semantics as check-lock-sync.sh); | ||
| # 2. the LITERAL GREP: the exact ref string does not occur anywhere in the | ||
| # workflow file, comments included (`grep -F`). | ||
| # Disagreement means KEEP. A parser blind spot therefore cannot delete a needed | ||
| # entry (scripts/tests/prune-stale-test.sh plants exactly that blind spot). | ||
| # | ||
| # Lock repair order: gh actions-lock → relock-sha-keys.sh → complete-job-refs.sh | ||
| # → close-lock.sh → prune-stale.sh (see complete-job-refs.sh). | ||
| # | ||
| # Exit 0 whether or not anything was pruned; exit 1 only on a structural error. | ||
|
|
||
| set -euo pipefail | ||
|
|
||
| WF_DIR="${1:-.github/workflows}" | ||
| LOCK="$WF_DIR/actions.lock" | ||
|
|
||
| AWK="" | ||
| for cand in gawk awk; do | ||
| if command -v "$cand" >/dev/null 2>&1 \ | ||
| && echo x | "$cand" '{ if (match($0, /(x)/, m) && m[1] == "x") exit 0; exit 1 }' 2>/dev/null; then | ||
| AWK="$cand"; break | ||
| fi | ||
| done | ||
| [ -n "$AWK" ] || { echo "prune-stale: FATAL: need gawk (3-arg match)" >&2; exit 1; } | ||
| [ -f "$LOCK" ] || { echo "prune-stale: FATAL: no lockfile at $LOCK" >&2; exit 1; } | ||
|
|
||
| shopt -s nullglob | ||
| mapfile -t WORKFLOWS < <(printf '%s\n' "$WF_DIR"/*.yml "$WF_DIR"/*.yaml | sort -u) | ||
| [ "${#WORKFLOWS[@]}" -gt 0 ] || { echo "prune-stale: FATAL: no workflows under $WF_DIR" >&2; exit 1; } | ||
|
|
||
| read -r -d '' CAND <<'AWK' || true | ||
| function norm(r, at, path, ref, n, parts) { | ||
| at = 0 | ||
| for (n = length(r); n > 0; n--) { if (substr(r, n, 1) == "@") { at = n; break } } | ||
| if (at == 0) return "" | ||
| path = substr(r, 1, at - 1); ref = substr(r, at + 1) | ||
| if (path == "" || ref == "") return "" | ||
| if (substr(path, 1, 2) == "./" || substr(path, 1, 2) == "$/") return "" | ||
| if (split(path, parts, "/") < 2) return "" | ||
| return parts[1] "/" parts[2] "@" ref | ||
| } | ||
| function ck(r, at, s) { | ||
| at = 0 | ||
| for (s = length(r); s > 0; s--) { if (substr(r, s, 1) == "@") { at = s; break } } | ||
| if (at == 0) return tolower(r) | ||
| return tolower(substr(r, 1, at - 1)) substr(r, at) | ||
| } | ||
| FILENAME == lockfile { | ||
| if ($0 ~ /^workflows:[[:space:]]*$/) { inwf = 1; next } | ||
| if ($0 ~ /^[a-z_]+:/) { inwf = 0; next } | ||
| if (!inwf) next | ||
| if (match($0, /^ '([^']+)':/, m)) { cur = m[1]; next } | ||
| if (match($0, /^ - '([^']+)'[[:space:]]*$/, m) && cur != "") { | ||
| lockref[cur, ck(m[1])] = m[1] | ||
| } | ||
| next | ||
| } | ||
| FNR == 1 { wf = FILENAME; key = wf; sub(/.*\//, "", key); key = ".github/workflows/" key; pathof[wf] = key } | ||
| { | ||
| line = $0 | ||
| sub(/[[:space:]]+#.*$/, "", line) | ||
| if (match(line, /^[[:space:]]*-?[[:space:]]*uses:[[:space:]]*(.+)$/, m)) { | ||
| raw = m[1]; gsub(/^["']|["']$/, "", raw); gsub(/[[:space:]]+$/, "", raw) | ||
| n = norm(raw) | ||
| if (n != "") used[pathof[wf], ck(n)] = 1 | ||
| } | ||
| } | ||
| END { | ||
| for (k in lockref) { | ||
| split(k, kp, SUBSEP) | ||
| if (!(k in used)) printf "%s\t%s\n", kp[1], lockref[k] | ||
| } | ||
| } | ||
| AWK | ||
|
|
||
| TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT | ||
| "$AWK" -v lockfile="$LOCK" "$CAND" "$LOCK" "${WORKFLOWS[@]}" | LC_ALL=C sort -u > "$TMP/cand.tsv" | ||
|
|
||
| if [ ! -s "$TMP/cand.tsv" ]; then | ||
| echo "prune-stale: no stale workflow-entry refs; lockfile unchanged" | ||
| exit 0 | ||
| fi | ||
|
|
||
| # ── oracle 2: the literal grep. Disagreement => KEEP. ────────────────────── | ||
| : > "$TMP/drop.tsv" | ||
| kept_by_oracle=0 | ||
| while IFS=$'\t' read -r path ref; do | ||
| wf="$WF_DIR/${path##*/}" | ||
| if [ -f "$wf" ] && command grep -Fq -- "$ref" "$wf"; then | ||
| printf ' ! oracle disagrees, KEEPING %s in %s (literal string present)\n' "$ref" "$path" | ||
| kept_by_oracle=$((kept_by_oracle + 1)) | ||
| continue | ||
| fi | ||
| printf '%s\t%s\n' "$path" "$ref" >> "$TMP/drop.tsv" | ||
| done < "$TMP/cand.tsv" | ||
|
|
||
| if [ ! -s "$TMP/drop.tsv" ]; then | ||
| echo "prune-stale: every candidate was vetoed by the literal-grep oracle; lockfile unchanged" | ||
| exit 0 | ||
| fi | ||
|
|
||
| read -r -d '' APPLY <<'AWK' || true | ||
| NR == FNR { drop[$1 SUBSEP $2] = 1; next } | ||
| { | ||
| if ($0 ~ /^workflows:[[:space:]]*$/) { inwf = 1; print; next } | ||
| if ($0 ~ /^[a-z_]+:/) { inwf = 0; print; next } | ||
| if (!inwf) { print; next } | ||
| if (match($0, /^ '([^']+)':/, m)) { cur = m[1]; print; next } | ||
| if (match($0, /^ - '([^']+)'[[:space:]]*$/, m) && cur != "") { | ||
| if ((cur SUBSEP m[1]) in drop) { pruned++; next } | ||
| } | ||
| } | ||
| END { printf "prune-stale: pruned %d stale workflow-entry ref(s)\n", pruned + 0 > "/dev/stderr" } | ||
| AWK | ||
|
|
||
| "$AWK" -F'\t' "$APPLY" "$TMP/drop.tsv" "$LOCK" > "$TMP/new.lock" | ||
|
|
||
| # An entry left with no items must become an explicit empty list, or the YAML | ||
| # key would take a null value and the schema would reject it. | ||
| "$AWK" ' | ||
| # Scoped strictly to the workflows: section. A dependencies: record uses the | ||
| # same " '\''key'\'':" shape but its children are " ref:" style, so an | ||
| # unscoped pass would rewrite every dependency record to ": []" and orphan its | ||
| # children. That bug was caught by the gitbot-fleet test, not by review. | ||
| { lines[NR] = $0 } | ||
| END { | ||
| inwf = 0 | ||
| for (i = 1; i <= NR; i++) { | ||
| if (lines[i] ~ /^workflows:[[:space:]]*$/) { inwf = 1; print lines[i]; continue } | ||
| else if (lines[i] ~ /^[a-z_]+:/) { inwf = 0; print lines[i]; continue } | ||
| if (inwf && match(lines[i], /^ '\''[^'\'']+'\'':[[:space:]]*$/) \ | ||
| && (i == NR || lines[i+1] !~ /^ - /)) { | ||
| sub(/:[[:space:]]*$/, ": []", lines[i]) | ||
| } | ||
| print lines[i] | ||
| } | ||
| }' "$TMP/new.lock" > "$TMP/new2.lock" | ||
|
|
||
| if command -v diff >/dev/null 2>&1; then diff -u "$LOCK" "$TMP/new2.lock" | sed 's/^/ | /' || true; fi | ||
| cat "$TMP/new2.lock" > "$LOCK" | ||
| echo "prune-stale: done (oracle vetoes: $kept_by_oracle)" |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
sed -n '1,110p' scripts/complete-job-refs.shRepository: hyperpolymath/standards
Length of output: 4476
🏁 Script executed:
Repository: hyperpolymath/standards
Length of output: 38878
🏁 Script executed:
Repository: hyperpolymath/standards
Length of output: 44962
🏁 Script executed:
Repository: hyperpolymath/standards
Length of output: 10501
Do not hide pipeline failures in ref enumeration.
set +ecovers the full per-file pipeline. Ifsedcannot read a workflow file, the failure is ignored and the script can write an incompleteWANTset. Keep errexit enabled and allow only the expected no-match status fromgrep.♻️ Suggested fix
📝 Committable suggestion
🤖 Prompt for AI Agents