check / check (push) Successful in 17s
One command fetches the vendored files from prompts at a commit, keeps a repo's own entries, commits and pushes. The canonical .dockerignore gains the closing comment the script needs. Model: opus-5-5
189 lines
5.7 KiB
Bash
Executable File
189 lines
5.7 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
|
|
# - .golangci.yml: the entries the repo added to the deny list of the
|
|
# test-support rule
|
|
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
|
|
}
|
|
|
|
# keep_lines_below_last <fetched> <local>: append to the fetched file
|
|
# whatever follows its last line in the local file.
|
|
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
|
|
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.
|
|
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
|
|
}
|
|
|
|
/^ *test-support:/ { in_rule = 1 }
|
|
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"
|
|
}
|
|
|
|
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
|
|
|
|
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"
|
|
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"
|
|
exit 0
|
|
fi
|
|
|
|
git commit -m "Update policy files from prompts $commit" -- $carried
|
|
git push origin HEAD ||
|
|
die "push failed; the commit exists locally, so fix that and run git push"
|
|
}
|
|
|
|
main "$@"
|