Comments that pointed at other YAML as `file.yaml:N` went stale on any edit above the cited line; three drift fixes had been merged by hand (REN-181). The cited block now carries `# anchor: <name>` and the comment cites `path.yaml#<name>`. - Sweep: all 32 line-number citations in YAML comments converted. Several were already stale at head and now point at the right block (see PR). Vendored files (Flux gotk-components, IMC CRD) get no anchor; those two citations name the object/schema path instead. - Guard: scripts/ci/yaml-citations.sh in ci-manifests-test.yml; fails on a line-number citation or a path#name whose file/anchor is gone. Self-test in ci-scripts-test.yml. - CONTRIBUTING.md: the convention in three lines. Comment-only under kubernetes/: every file parses to identical objects before and after, so Flux applies nothing and no ConfigMap data changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
111 lines
4.0 KiB
Bash
111 lines
4.0 KiB
Bash
#!/bin/sh
|
|
# Guard the citation convention in YAML comments (REN-183, see CONTRIBUTING.md).
|
|
#
|
|
# Run: sh scripts/ci/yaml-citations.sh [repo-root]
|
|
# Needs: sh, find, awk, grep (BusyBox is fine). Offline.
|
|
# Tests: scripts/ci/test-yaml-citations.sh
|
|
#
|
|
# A comment cites another block as `path.yaml#name`, and the cited block
|
|
# carries a marker line `# anchor: name`. `path` is relative to the citing
|
|
# file's directory or to the repo root. This fails when:
|
|
#
|
|
# * a comment cites by line number (`foo.yaml:12`). Line numbers go stale on
|
|
# the first unrelated edit above them, silently; three drift fixes were
|
|
# merged before this guard existed (REN-181).
|
|
# * a `path#name` citation names a file or an anchor that does not exist.
|
|
# * an anchor is not kebab-case, or is declared twice in one file.
|
|
#
|
|
# Existence only. It does not check that the anchored block still says what the
|
|
# citing comment claims — that stays a reviewer's job, and it is why this guard
|
|
# has no false positives from ordinary edits.
|
|
set -eu
|
|
|
|
ROOT="${1:-.}"
|
|
cd "$ROOT"
|
|
|
|
# Same loose-floor reasoning as ci-manifests-test.yml: catch discovery that
|
|
# silently returns nothing, not normal churn. ~650 YAML files exist today.
|
|
FLOOR="${YAML_CITATIONS_FLOOR:-50}"
|
|
|
|
TMP="$(mktemp -d)"
|
|
trap 'rm -rf "$TMP"' EXIT INT TERM
|
|
|
|
# Not `find -printf` (BusyBox lacks it).
|
|
find . -path ./.git -prune -o -type f \( -name '*.yaml' -o -name '*.yml' \) -print \
|
|
| sed 's|^\./||' | sort > "$TMP/files"
|
|
count=$(grep -c . "$TMP/files" || true)
|
|
if [ "$count" -lt "$FLOOR" ]; then
|
|
echo "FAIL: found only $count YAML files; floor is $FLOOR. Discovery is broken."
|
|
exit 1
|
|
fi
|
|
|
|
# One pass over every file. A YAML comment starts at a `#` that begins the line
|
|
# or follows whitespace, so a `#` inside a URL or a quoted value is not one.
|
|
# Emits tab-separated records:
|
|
# L file line text a line-number citation
|
|
# C file line path name a path#name citation
|
|
# A file line name an anchor declaration
|
|
# B file line text a malformed anchor declaration
|
|
while IFS= read -r f; do
|
|
awk -v f="$f" '
|
|
{
|
|
if (!match($0, /(^|[ \t])#/)) next
|
|
c = substr($0, RSTART)
|
|
if (c ~ /^[ \t]*# anchor:/) {
|
|
if (c ~ /^[ \t]*# anchor: [a-z0-9]+(-[a-z0-9]+)*[ \t]*$/) {
|
|
n = c; sub(/^[ \t]*# anchor: /, "", n); sub(/[ \t]*$/, "", n)
|
|
print "A\t" f "\t" NR "\t" n
|
|
} else {
|
|
print "B\t" f "\t" NR "\t" c
|
|
}
|
|
next
|
|
}
|
|
if (c ~ /\.ya?ml:[0-9]/) print "L\t" f "\t" NR "\t" c
|
|
while (match(c, /[A-Za-z0-9_.\/-]+\.ya?ml#[A-Za-z0-9_-]+/)) {
|
|
m = substr(c, RSTART, RLENGTH)
|
|
p = m; sub(/#.*/, "", p)
|
|
n = m; sub(/.*#/, "", n)
|
|
print "C\t" f "\t" NR "\t" p "\t" n
|
|
c = substr(c, RSTART + RLENGTH)
|
|
}
|
|
}' "$f"
|
|
done < "$TMP/files" > "$TMP/records"
|
|
|
|
fail=0
|
|
while IFS="$(printf '\t')" read -r kind f line a b; do
|
|
case "$kind" in
|
|
L)
|
|
echo "FAIL $f:$line cites by line number; use path.yaml#anchor instead"
|
|
echo " $a"
|
|
fail=1 ;;
|
|
B)
|
|
echo "FAIL $f:$line malformed anchor; want '# anchor: kebab-case-name'"
|
|
fail=1 ;;
|
|
C)
|
|
dir=$(dirname "$f")
|
|
if [ -f "$dir/$a" ]; then target="$dir/$a"
|
|
elif [ -f "$a" ]; then target="$a"
|
|
else
|
|
echo "FAIL $f:$line cites $a#$b, but no such file (tried $dir/$a and ./$a)"
|
|
fail=1; continue
|
|
fi
|
|
if ! grep -qE "^[[:space:]]*# anchor: $b[[:space:]]*\$" "$target"; then
|
|
echo "FAIL $f:$line cites $a#$b, but $target has no '# anchor: $b'"
|
|
fail=1
|
|
fi ;;
|
|
esac
|
|
done < "$TMP/records"
|
|
|
|
# Duplicate anchors in one file make a citation ambiguous.
|
|
awk -F '\t' '$1 == "A" { k = $2 "\t" $4; if (k in seen) print $2 ":" $3 " duplicate anchor " $4 " (first at line " seen[k] ")"; else seen[k] = $3 }' \
|
|
"$TMP/records" > "$TMP/dups"
|
|
if [ -s "$TMP/dups" ]; then
|
|
sed 's/^/FAIL /' "$TMP/dups"
|
|
fail=1
|
|
fi
|
|
|
|
anchors=$(grep -c '^A' "$TMP/records" || true)
|
|
cites=$(grep -c '^C' "$TMP/records" || true)
|
|
[ "$fail" -eq 0 ] && echo "ok $count YAML files, $anchors anchors, $cites path#anchor citations, 0 line-number citations"
|
|
exit $fail
|