aboutsummaryrefslogtreecommitdiff
path: root/scripts/sweep-gitignore-tooling.sh
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/sweep-gitignore-tooling.sh')
-rwxr-xr-xscripts/sweep-gitignore-tooling.sh57
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