Repository navigation
ci: keep generated HQ snapshot and ACTIVITY.md off branches #370
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
Changes from all commits
c10cdf6
5491ddd
a3bc472
4b626f3
f5ce8aa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| #!/usr/bin/env bash | ||
| # hotspot-guard (CI-CONFLICT-01): a branch must not change the generated HQ | ||
| # snapshot or the shared journal. Rules and reasons: scripts/lib/hotspots.sh. | ||
| # | ||
| # Env: HOTSPOT_BRANCH (branch name override), HOTSPOT_BASE (base commit; the | ||
| # default is the merge-base with origin/main - no base is a failure, not a pass). | ||
| set -uo pipefail | ||
|
|
||
| ROOT="$(CDPATH= cd -- "$(dirname "$0")/../.." && pwd)" | ||
| # shellcheck source=scripts/lib/hotspots.sh | ||
| . "$ROOT/scripts/lib/hotspots.sh" | ||
| cd "$ROOT" || exit 1 | ||
|
|
||
| branch="$(hs_branch "$ROOT")" | ||
| if [ -n "$branch" ] && hs_may_touch "$branch"; then | ||
| echo "hotspot-guard: branch '$branch' may change the generated files - nothing to check" | ||
| exit 0 | ||
| fi | ||
|
|
||
| base="${HOTSPOT_BASE:-$(git merge-base origin/main HEAD 2> /dev/null)}" | ||
| if [ -z "$base" ]; then | ||
| echo "hotspot-guard: no base commit (git fetch origin, or set HOTSPOT_BASE)" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| hit="$(git diff --name-only "$base" HEAD -- "${HS_FILES[@]}")" || exit 1 | ||
| if [ -z "$hit" ]; then | ||
| echo "hotspot-guard: ok - no hotspot file changed on '${branch:-detached HEAD}'" | ||
| exit 0 | ||
| fi | ||
|
|
||
| echo "hotspot-guard: branch '${branch:-detached HEAD}' changes files that conflict across PRs:" >&2 | ||
| printf ' %s\n' $hit >&2 | ||
| echo "These are written on main only (AGENTS.md, Checks). Drop the change and keep the branch:" >&2 | ||
| echo " git checkout $base -- $(printf '%s ' $hit)" >&2 | ||
| echo " git commit -m 'chore: drop generated hotspot files'" >&2 | ||
| echo "A journal note belongs in the PR text; the snapshot is refreshed by the coordinator on a hq-snapshot branch." >&2 | ||
| exit 1 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| #!/usr/bin/env bash | ||
| # Shared rules for the merge-conflict hotspots (CI-CONFLICT-01). Functions | ||
| # only - nothing runs when sourced. | ||
| # | ||
| # GitHub and Mergify compute mergeability without our local merge driver, so | ||
| # every branch that commits one of these files re-conflicts with every other | ||
| # one the moment the first lands. Only main and a dedicated snapshot branch | ||
| # (`<vendor>/hq-snapshot...`, made by the coordinator) may commit them. Queue | ||
| # branches may carry such a commit while Mergify validates it before merging. | ||
|
|
||
| HS_FILES=(docs/dev-hq/data.js docs/dev-hq/data.json .pa/ACTIVITY.md) | ||
|
|
||
| # Branch name to judge: explicit override, then the CI refs, then git. Empty | ||
| # (detached HEAD, nothing known) is not a branch that may touch the files. | ||
| hs_branch() { # [root] | ||
| if [ -n "${HOTSPOT_BRANCH:-}" ]; then | ||
| printf '%s' "$HOTSPOT_BRANCH" | ||
| elif [ -n "${GITHUB_HEAD_REF:-}" ]; then | ||
| printf '%s' "$GITHUB_HEAD_REF" | ||
| elif [ "${GITHUB_ACTIONS:-}" = "true" ] && [ -n "${GITHUB_REF_NAME:-}" ]; then | ||
| printf '%s' "$GITHUB_REF_NAME" | ||
| else | ||
| git -C "${1:-.}" branch --show-current 2> /dev/null | ||
| fi | ||
| } | ||
|
|
||
| hs_may_touch() { # branch | ||
| case "$1" in | ||
| main | master | gh-readonly-queue/* | mergify/merge-queue/* | hq-snapshot* | */hq-snapshot*) return 0 ;; | ||
| esac | ||
| return 1 | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,10 @@ | |
| # Hängt einen Aktivitäts-Eintrag an .pa/ACTIVITY.md an. Zeitstempel, | ||
| # Branch und Uncommitted-Liste werden automatisch aus git ermittelt. | ||
| # Neue Top-Level-Dateien/Ordner in der Zusammenfassung namentlich nennen. | ||
| # Nur auf main (und hq-snapshot-Zweigen) landet der Eintrag in der | ||
| # getrackten Datei; auf jedem anderen Branch in .pa/ACTIVITY.local.md | ||
| # (ungetrackt) - die getrackte Datei ist ein Konfliktherd (CI-CONFLICT-01). | ||
| # Die Zusammenfassung gehoert dort in den PR-Text. | ||
|
|
||
| set -u | ||
|
|
||
|
|
@@ -17,12 +21,17 @@ if [ -z "$ROOT" ]; then | |
| echo "sync.sh: nicht in einem git-Repository" >&2 | ||
| exit 1 | ||
| fi | ||
| ACTIVITY="$ROOT/.pa/ACTIVITY.md" | ||
| # shellcheck source=scripts/lib/hotspots.sh | ||
| . "$ROOT/scripts/lib/hotspots.sh" | ||
| TRACKED="$ROOT/.pa/ACTIVITY.md" | ||
| ACTIVITY="$TRACKED" | ||
| branch="$(hs_branch "$ROOT")" | ||
| hs_may_touch "$branch" || ACTIVITY="$ROOT/.pa/ACTIVITY.local.md" | ||
|
|
||
| print_activity_tail() { | ||
| if [ -f "$ACTIVITY" ]; then | ||
| if [ -f "$TRACKED" ]; then | ||
| echo "== .pa/ACTIVITY.md (letzte Einträge) ==" | ||
| tail -n 40 "$ACTIVITY" | ||
| tail -n 40 "$TRACKED" | ||
|
Comment on lines
+32
to
+34
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Auf jedem normalen Arbeitsbranch schreibt AGENTS.md reference: AGENTS.md:L294-L297 Useful? React with 👍 / 👎. |
||
| else | ||
| echo "== .pa/ACTIVITY.md existiert noch nicht ==" | ||
| fi | ||
|
|
@@ -109,10 +118,18 @@ case "$cmd" in | |
| fi | ||
| } >> "$ACTIVITY" | ||
| echo "Eintrag an $ACTIVITY angehängt." | ||
| if [ "$ACTIVITY" != "$TRACKED" ]; then | ||
| echo "Hinweis: Branch '${branch:-detached}' darf .pa/ACTIVITY.md nicht committen (Konfliktherd)." | ||
| echo " Der Eintrag liegt in der ungetrackten Datei .pa/ACTIVITY.local.md; die Zusammenfassung gehört in den PR-Text." | ||
| fi | ||
| ;; | ||
|
|
||
| where) | ||
| echo "$ACTIVITY" | ||
| ;; | ||
|
|
||
| *) | ||
| echo "Aufruf: bash scripts/sync.sh start|note" >&2 | ||
| echo "Aufruf: bash scripts/sync.sh start|note|where" >&2 | ||
| exit 1 | ||
| ;; | ||
| esac | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| #!/usr/bin/env bash | ||
| # Self-test for CI-CONFLICT-01: branches must not commit the generated HQ | ||
| # snapshot or .pa/ACTIVITY.md (scripts/ci/hotspot-guard.sh, scripts/sync.sh). | ||
| # Each case runs in a fresh temp repo; passing cases print `ok <name>`. | ||
| set -uo pipefail | ||
|
|
||
| HERE="$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)" | ||
| tmp="$(mktemp -d "${TMPDIR:-/tmp}/hotspot-guard.XXXXXX")" | ||
| trap 'rm -rf "$tmp"' EXIT | ||
| fails=0 | ||
|
|
||
| pass() { echo "ok $1"; } | ||
| fail() { echo "FEHLER $1"; fails=$((fails + 1)); } | ||
|
|
||
| # A repo that carries the scripts under test, one base commit on main. | ||
| repo() { # name | ||
| local d="$tmp/$1" | ||
| mkdir -p "$d/scripts/ci" "$d/scripts/lib" "$d/docs/dev-hq" "$d/.pa" | ||
| cp "$HERE/scripts/sync.sh" "$d/scripts/" | ||
| cp "$HERE/scripts/ci/hotspot-guard.sh" "$d/scripts/ci/" 2> /dev/null | ||
| cp "$HERE/scripts/lib/hotspots.sh" "$d/scripts/lib/" 2> /dev/null | ||
| ( | ||
| cd "$d" || exit 1 | ||
| git init -q -b main . && git config user.email t@t && git config user.name t | ||
| printf 'a\n' > docs/dev-hq/data.js; printf '{}\n' > docs/dev-hq/data.json | ||
| printf '# ACTIVITY\n' > .pa/ACTIVITY.md; printf 'x\n' > other.txt | ||
| git add -A && git commit -qm base | ||
| ) | ||
| echo "$d" | ||
| } | ||
|
|
||
| guard() { # dir -> exit code of the guard in dir (CI env stripped) | ||
| (cd "$1" && env -u GITHUB_ACTIONS -u GITHUB_HEAD_REF -u GITHUB_REF_NAME -u HOTSPOT_BRANCH \ | ||
| HOTSPOT_BASE="$(git rev-parse main)" bash scripts/ci/hotspot-guard.sh > "$1/out" 2>&1) | ||
| } | ||
|
|
||
| # branch-with-change <name> <branch> <file>: commit a change to <file> on <branch> | ||
| branch_change() { | ||
| local d | ||
| d="$(repo "$1")" | ||
| (cd "$d" && git checkout -q -b "$2" && printf 'changed\n' >> "$3" && git commit -qam change) | ||
| echo "$d" | ||
| } | ||
|
|
||
| for f in docs/dev-hq/data.js docs/dev-hq/data.json .pa/ACTIVITY.md; do | ||
| d="$(branch_change "rej-$(basename "$f")" claude/some-work "$f")" | ||
| if guard "$d"; then fail "guard rejects package branch touching $f"; else | ||
| grep -q "$f" "$d/out" && grep -q "git checkout" "$d/out" && pass "guard rejects package branch touching $f" || | ||
| fail "guard rejects package branch touching $f (message lacks file or fix)" | ||
| fi | ||
| done | ||
|
|
||
| d="$(branch_change other claude/some-work other.txt)" | ||
| guard "$d" && pass "guard accepts branch touching other files only" || fail "guard accepts branch touching other files only" | ||
|
|
||
| d="$(repo mainc)" | ||
| (cd "$d" && printf 'n\n' >> .pa/ACTIVITY.md && git commit -qam note) | ||
| (cd "$d" && env -u GITHUB_ACTIONS -u GITHUB_HEAD_REF HOTSPOT_BASE="$(git rev-parse HEAD~1)" bash scripts/ci/hotspot-guard.sh > out 2>&1) && | ||
| pass "guard accepts main touching the hotspot files" || fail "guard accepts main touching the hotspot files" | ||
|
|
||
| d="$(branch_change snap claude/hq-snapshot-refresh docs/dev-hq/data.json)" | ||
| guard "$d" && pass "guard accepts a dedicated hq-snapshot branch" || fail "guard accepts a dedicated hq-snapshot branch" | ||
|
|
||
| d="$(branch_change queue mergify/merge-queue/0123456789 docs/dev-hq/data.json)" | ||
| guard "$d" && pass "guard accepts a Mergify merge queue branch" || fail "guard accepts a Mergify merge queue branch" | ||
|
|
||
| d="$(branch_change nobase claude/some-work docs/dev-hq/data.js)" | ||
| (cd "$d" && env -u HOTSPOT_BASE -u GITHUB_ACTIONS -u GITHUB_HEAD_REF bash scripts/ci/hotspot-guard.sh > out 2>&1) && | ||
| fail "guard fails closed without a base" || pass "guard fails closed without a base" | ||
|
|
||
| # sync.sh note: on a branch the tracked journal stays untouched. | ||
| d="$(repo note-branch)" | ||
| (cd "$d" && git checkout -q -b claude/some-work) | ||
| out="$(cd "$d" && env -u HOTSPOT_BRANCH -u GITHUB_ACTIONS -u GITHUB_HEAD_REF bash scripts/sync.sh note test "summary" 2>&1)" | ||
| if [ -z "$(git -C "$d" diff --name-only -- .pa/ACTIVITY.md)" ] && grep -q summary "$d/.pa/ACTIVITY.local.md" 2> /dev/null && | ||
| printf '%s' "$out" | grep -q "ACTIVITY.local.md"; then | ||
| pass "sync note on a package branch leaves the tracked ACTIVITY.md alone" | ||
| else | ||
| fail "sync note on a package branch leaves the tracked ACTIVITY.md alone" | ||
| fi | ||
|
|
||
| d="$(repo note-main)" | ||
| (cd "$d" && env -u HOTSPOT_BRANCH -u GITHUB_ACTIONS -u GITHUB_HEAD_REF bash scripts/sync.sh note test "summary" > /dev/null 2>&1) | ||
| grep -q summary "$d/.pa/ACTIVITY.md" && pass "sync note on main appends to the tracked ACTIVITY.md" || | ||
| fail "sync note on main appends to the tracked ACTIVITY.md" | ||
|
|
||
| [ "$fails" -eq 0 ] || { echo "$fails Fehler"; exit 1; } | ||
| echo "alle Hotspot-Faelle ok" |
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.
Auf einem
<vendor>/hq-snapshot-*-Branch lieferths_may_touchErfolg, weshalb diese Zuweisung.pa/ACTIVITY.mdals Ziel behält; zugleich beendethotspot-guardfür denselben Branch seine Prüfung vorzeitig. Ein vorgeschriebenersync.sh noteverändert dadurch das getrackte Journal, obwohl der dedizierte Snapshot-PR laut Regel nur die beidendocs/dev-hq/data.*-Dateien enthalten soll. Die Snapshot-Ausnahme muss für das Journal getrennt von der Berechtigung zur Snapshot-Regeneration behandelt werden.AGENTS.md reference: AGENTS.md:L148-L154
Useful? React with 👍 / 👎.