check / check (push) Successful in 31s
A line an earlier canonical commit held counted as the repo's own, so the script stopped on the outdated lines a re-vendor exists to replace. It now clones prompts and also treats every line removed from the file in its history as canonical. The .golangci.yml stop message no longer tells anyone to delete a line. The tests use a local git repository in place of prompts and gain one scenario each for .golangci.yml and .gitignore. Model: opus-5-5
223 lines
7.5 KiB
Bash
Executable File
223 lines
7.5 KiB
Bash
Executable File
#!/bin/sh
|
|
# script/update-policy: replace this repo's vendored policy files with
|
|
# the canonical ones from the prompts repo, commit them and push.
|
|
# Identical in all repos. Our own extension to
|
|
# scripts-to-rule-them-all.
|
|
#
|
|
# Usage: script/update-policy [full commit hash of prompts]
|
|
# Without a hash it uses the head of the prompts main branch.
|
|
#
|
|
# The script writes nothing until every file has been fetched and
|
|
# merged. A repo's own entries are kept:
|
|
# - .gitignore, .editorconfig, .dockerignore: the lines below the
|
|
# canonical file's last line. An entry above that line which the
|
|
# canonical file has never held stops the script.
|
|
# - .golangci.yml: the entries the repo added to the deny list of the
|
|
# test-support rule. Any other line the canonical file has never
|
|
# held stops the script, unless it is blank or a comment.
|
|
set -eu
|
|
|
|
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
|
PROMPTS_URL="https://git.eeqj.de/sneak/prompts"
|
|
|
|
# Paths in the prompts repo. A file this repo does not carry is skipped.
|
|
VENDORED_FILES="
|
|
.dockerignore
|
|
.editorconfig
|
|
.gitea/workflows/check.yml
|
|
.gitignore
|
|
.golangci.yml
|
|
.prettierrc
|
|
prompts/API_POLICIES.md
|
|
prompts/CODE_STYLEGUIDE.md
|
|
prompts/CODE_STYLEGUIDE_GO.md
|
|
prompts/CODE_STYLEGUIDE_JS.md
|
|
prompts/CODE_STYLEGUIDE_PYTHON.md
|
|
prompts/EXISTING_REPO_CHECKLIST.md
|
|
prompts/GO_HTTP_SERVER_CONVENTIONS.md
|
|
prompts/LLM_PROSE_TELLS.md
|
|
prompts/NEW_REPO_CHECKLIST.md
|
|
prompts/REPO_POLICIES.md
|
|
"
|
|
|
|
die() {
|
|
echo "update-policy: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
# local_path <path in prompts>: print where this repo keeps the file, or
|
|
# nothing if git does not track it here. A prompts/NAME.md lands at
|
|
# docs/NAME.md or NAME.md.
|
|
local_path() {
|
|
name="${1#prompts/}"
|
|
if [ "$name" != "$1" ] && [ -n "$(git ls-files -- "docs/$name")" ]; then
|
|
echo "docs/$name"
|
|
elif [ -n "$(git ls-files -- "$name")" ]; then
|
|
echo "$name"
|
|
fi
|
|
}
|
|
|
|
# local_only_lines <fetched> <local> [end]: print each line of the local
|
|
# file, above line end if given, that the canonical file has never held:
|
|
# not in the fetched file or in <fetched>.former, the lines earlier
|
|
# commits removed from it. Blank lines and comments do not count.
|
|
local_only_lines() {
|
|
cat "$1" "$1.former" | awk -v end="${3:-}" '
|
|
NR == FNR { held[$0]; next }
|
|
(end == "" || FNR < end) && !/^[ \t]*(#|$)/ && !($0 in held)
|
|
' - "$2"
|
|
}
|
|
|
|
# keep_lines_below_last <fetched> <local>: append to the fetched file
|
|
# whatever follows its last line in the local file. Stop if the canonical
|
|
# file has never held a line above that one, unless it is blank or a
|
|
# comment: replacing the file would delete it.
|
|
keep_lines_below_last() {
|
|
last="$(tail -n 1 "$1")"
|
|
number="$(grep -n -F -x -- "$last" "$2" | head -n 1 | cut -d: -f1)"
|
|
if [ -z "$last" ] || [ -z "$number" ]; then
|
|
die "$2 lacks this line; add it and put the repo's own entries below it: $last"
|
|
fi
|
|
own="$(local_only_lines "$1" "$2" "$number")"
|
|
if [ -n "$own" ]; then
|
|
die "$2 has entries above its last canonical line that the canonical file lacks; move each below that line to keep it, or delete it:
|
|
$own"
|
|
fi
|
|
tail -n "+$((number + 1))" "$2" >> "$1"
|
|
}
|
|
|
|
# keep_deny_entries <fetched> <local>: add to the deny list of the
|
|
# test-support rule in the fetched file the entries only the local file
|
|
# has. An entry is a "- pkg:" line, the lines under it and any comment
|
|
# above it. Stop if the local file still has a line that neither the
|
|
# result nor the canonical file has ever held, unless it is blank or a
|
|
# comment: replacing the file would delete it.
|
|
keep_deny_entries() {
|
|
awk '
|
|
function indent(line) { return match(line, /[^ ]/) - 1 }
|
|
function print_local_entries(i) {
|
|
for (i = 1; i <= count; i++)
|
|
if (!(pkg[i] in fetched_pkg))
|
|
printf "%s", entry[i]
|
|
}
|
|
|
|
FNR == 1 { in_rule = 0; in_list = 0 }
|
|
|
|
in_list && /[^ ]/ && indent($0) <= list_indent {
|
|
in_list = 0
|
|
if (FILENAME == ARGV[2]) print_local_entries()
|
|
}
|
|
|
|
in_list {
|
|
is_entry = ($1 == "-" && $2 == "pkg:")
|
|
if (FILENAME == ARGV[2]) {
|
|
if (is_entry) fetched_pkg[$3]
|
|
print
|
|
} else if (is_entry) {
|
|
count++
|
|
pkg[count] = $3
|
|
entry[count] = comment $0 "\n"
|
|
comment = ""
|
|
} else if (/^ *#/) {
|
|
comment = comment $0 "\n"
|
|
} else if (count) {
|
|
entry[count] = entry[count] $0 "\n"
|
|
}
|
|
next
|
|
}
|
|
|
|
in_rule && /^ *[^ #]/ && indent($0) <= rule_indent { in_rule = 0 }
|
|
/^ *test-support:/ { in_rule = 1; rule_indent = indent($0) }
|
|
in_rule && /^ *deny:/ {
|
|
in_rule = 0
|
|
in_list = 1
|
|
list_indent = indent($0)
|
|
if (FILENAME == ARGV[2]) found = 1
|
|
}
|
|
FILENAME == ARGV[2] { print }
|
|
|
|
END {
|
|
if (in_list) print_local_entries()
|
|
exit !found
|
|
}
|
|
' "$2" "$1" > "$1.merged" ||
|
|
die "the fetched $2 has no deny list in its test-support rule"
|
|
mv "$1.merged" "$1"
|
|
own="$(local_only_lines "$1" "$2")"
|
|
if [ -n "$own" ]; then
|
|
die "$2 has lines the canonical file has never held; only the deny list of the test-support rule may differ from canonical:
|
|
$own"
|
|
fi
|
|
}
|
|
|
|
main() {
|
|
cd "$ROOT"
|
|
|
|
[ "$#" -le 1 ] || die "usage: script/update-policy [full commit hash]"
|
|
|
|
project="$(script/projectname)"
|
|
if [ "$project" = "prompts" ]; then
|
|
die "this repo is the source of the policy files"
|
|
fi
|
|
|
|
commit="${1:-}"
|
|
if [ -z "$commit" ]; then
|
|
head_line="$(git ls-remote "$PROMPTS_URL.git" refs/heads/main)"
|
|
commit="${head_line%%[[:space:]]*}"
|
|
fi
|
|
printf '%s\n' "$commit" | grep -Eqx '[0-9a-f]{40}' ||
|
|
die "not a full commit hash: '$commit'"
|
|
|
|
tmp="$(mktemp -d)"
|
|
trap 'rm -rf "$tmp"' EXIT
|
|
|
|
git clone --quiet --bare "$PROMPTS_URL.git" "$tmp/prompts.git" ||
|
|
die "cannot clone $PROMPTS_URL.git"
|
|
|
|
carried=""
|
|
for file in $VENDORED_FILES; do
|
|
path="$(local_path "$file")"
|
|
[ -n "$path" ] || continue
|
|
if [ -n "$(git status --porcelain -- "$path")" ]; then
|
|
die "$path has uncommitted changes"
|
|
fi
|
|
mkdir -p "$tmp/$(dirname "$path")"
|
|
curl --fail --silent --show-error --max-time 60 \
|
|
--output "$tmp/$path" \
|
|
"$PROMPTS_URL/raw/commit/$commit/$file" ||
|
|
die "cannot fetch $file at $commit"
|
|
git -C "$tmp/prompts.git" log -p --format= "$commit" -- "$file" |
|
|
sed -n 's/^-//p' > "$tmp/$path.former"
|
|
carried="$carried $path"
|
|
done
|
|
[ -n "$carried" ] || die "this repo carries none of the vendored files"
|
|
|
|
for path in $carried; do
|
|
case "$path" in
|
|
.gitignore | .editorconfig | .dockerignore)
|
|
keep_lines_below_last "$tmp/$path" "$path"
|
|
;;
|
|
.golangci.yml)
|
|
keep_deny_entries "$tmp/$path" "$path"
|
|
;;
|
|
esac
|
|
done
|
|
|
|
for path in $carried; do
|
|
cp "$tmp/$path" "$path"
|
|
git add -- "$path"
|
|
done
|
|
|
|
if git diff --cached --quiet -- $carried; then
|
|
echo "update-policy: already at prompts $commit, nothing to commit"
|
|
else
|
|
git commit -m "Update policy files from prompts $commit" -- $carried
|
|
fi
|
|
|
|
# Also pushes when there was nothing to commit, so a run after a
|
|
# failed push delivers the commit that push left behind.
|
|
git push origin HEAD || die "push failed; fix that and run this again"
|
|
}
|
|
|
|
main "$@"
|