diff options
Diffstat (limited to 'scripts/sweep-gitignore-tooling.sh')
| -rwxr-xr-x | scripts/sweep-gitignore-tooling.sh | 57 |
1 files changed, 48 insertions, 9 deletions
diff --git a/scripts/sweep-gitignore-tooling.sh b/scripts/sweep-gitignore-tooling.sh index 63fc066..f04d3cd 100755 --- a/scripts/sweep-gitignore-tooling.sh +++ b/scripts/sweep-gitignore-tooling.sh @@ -10,13 +10,19 @@ # # For each AI project (a directory with .ai/protocols.org) under the search # roots, if it's a git checkout in gitignore mode (.ai/ already appears in its -# .gitignore), ensure .ai/, .claude/, CLAUDE.md, and AGENTS.md are all ignored. -# Append only the missing lines, so a re-run is a no-op. +# .gitignore, in either the unanchored `.ai/` or anchored `/.ai/` form), ensure +# .ai/, .claude/, CLAUDE.md, and AGENTS.md are all ignored. Append only the +# missing lines, in whichever style the file already uses, so a re-run is a +# no-op. # # Track-mode projects (.ai/ NOT in .gitignore) are skipped by design: they # track their tooling on purpose — team repos sharing config with teammates who # don't run rulesets, or private-remote personal repos where the history IS the -# project. +# project. But a track-mode project whose tracked tooling is reachable from a +# non-cjennings.net remote gets a loud WARN: per convention the tooling set is +# gitignored anywhere the repo can reach a public host, and a server-side +# mirror hook can publish even a "private" remote (the 2026-06-30 .emacs.d +# exposure rode exactly that). # # A line added here only stops *future* commits. If a target path is already # tracked, the ignore has no effect until it's untracked; the sweep warns so @@ -30,6 +36,14 @@ set -euo pipefail IGNORE_SET=('.ai/' '.claude/' 'CLAUDE.md' 'AGENTS.md') +# A pattern counts as present in either the unanchored (`.ai/`) or anchored +# (`/.ai/`) form — both ignore the root-level path; treating them as different +# is what silently skipped anchored-style projects. +has_ignore() { + local pat="$1" gi="$2" + grep -qFx "$pat" "$gi" || grep -qFx "/$pat" "$gi" +} + dry_run=0 roots=() for arg in "$@"; do @@ -72,16 +86,39 @@ for project in "${projects[@]}"; do continue fi - # Gitignore mode iff .ai/ is already ignored. Otherwise track-mode: leave it. - if [ ! -f "$gi" ] || ! grep -qFx '.ai/' "$gi"; then - echo "skip $name — track-mode (.ai/ not gitignored)" + # Gitignore mode iff .ai/ is already ignored (either style). Otherwise + # track-mode: leave the .gitignore alone, but warn when tracked tooling can + # reach a non-cjennings.net remote — a track-mode repo on a public host (or + # behind an invisible server-side mirror) is the exposure the convention + # exists to prevent. + if [ ! -f "$gi" ] || ! has_ignore '.ai/' "$gi"; then + tracked_tooling=() + for pat in "${IGNORE_SET[@]}"; do + path="${pat%/}" + if git -C "$project" ls-files --error-unmatch "$path" >/dev/null 2>&1; then + tracked_tooling+=("$path") + fi + done + public_remote="$(git -C "$project" remote -v 2>/dev/null \ + | awk '{print $2}' | grep -v 'cjennings\.net' | sort -u | head -1 || true)" + if [ "${#tracked_tooling[@]}" -gt 0 ] && [ -n "$public_remote" ]; then + echo "skip $name — track-mode (.ai/ not gitignored)" + echo " WARN $name: tracked tooling (${tracked_tooling[*]}) is publicly reachable via $public_remote — gitignore the set and 'git -C $project rm --cached -r <path>' unless this is a deliberate team-shared config" + else + echo "skip $name — track-mode (.ai/ not gitignored)" + fi skipped=$((skipped + 1)) continue fi + # Append in the style the file already uses: anchored if its .ai/ marker + # line is the anchored form. + prefix="" + grep -qFx '/.ai/' "$gi" && prefix="/" + needed=() for pat in "${IGNORE_SET[@]}"; do - grep -qFx "$pat" "$gi" || needed+=("$pat") + has_ignore "$pat" "$gi" || needed+=("${prefix}${pat}") done if [ "${#needed[@]}" -eq 0 ]; then @@ -103,9 +140,11 @@ for project in "${projects[@]}"; do swept=$((swept + 1)) # Warn on any newly-ignored path that's already tracked — the ignore won't - # untrack it. + # untrack it. Strip the anchored prefix before asking git: the pattern + # `/CLAUDE.md` is the repo-relative path `CLAUDE.md`. for pat in "${needed[@]}"; do - path="${pat%/}" + path="${pat#/}" + path="${path%/}" if git -C "$project" ls-files --error-unmatch "$path" >/dev/null 2>&1; then echo " WARN $name: $path is currently tracked — 'git -C $project rm --cached -r $path' to untrack" fi |
