-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathinit-submodules
More file actions
executable file
·99 lines (90 loc) · 4.73 KB
/
Copy pathinit-submodules
File metadata and controls
executable file
·99 lines (90 loc) · 4.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#!/usr/bin/env bash
################################################################################
#
# Materialize the *config-managed* submodules a fresh working tree is missing, so
# agent assets resolve.
#
# `git worktree add` — and some shallow CI / cloud checkouts — populate only the
# superproject's own tracked files; registered submodules are left UNinitialized.
# In a Spine repo that means the `config` and `.agents/shared` submodules are
# empty, the `.agents/skills` -> `.agents/shared/skills` symlink dangles, and no
# agent skills, scripts, or guidelines can be found.
#
# This script is the bootstrap that has to run BEFORE `./config/pull`: `pull`
# lives inside the `config` submodule, so on a fresh worktree it does not yet
# exist. `init-submodules`, by contrast, is a plain tracked file at the repo root
# (distributed by `config`), so `git worktree add` always checks it out — it can
# therefore bring `config` itself into existence.
#
# It initializes ONLY submodules that are BOTH:
#
# * not yet checked out — those `git submodule status` marks with a leading `-`,
# at the commit the branch pins; and
#
# * config-managed — `config` itself (the bootstrap target `pull` lives inside,
# which carries no tracked `branch` in a consumer's `.gitmodules`), plus every
# submodule that declares a tracked `branch` in `.gitmodules`. This is exactly
# the rule `./config/pull` uses to decide what it floats, so the two scripts
# can never disagree about what is shared.
#
# Consumer-owned submodules (a Hugo theme, a vendored library, documentation
# examples, ...) declare no tracked branch and are deliberately left untouched.
# Because a `SessionStart` hook runs this script automatically on every session,
# initializing them would mean trying to clone — or failing on credentials for —
# a submodule this project does not manage, on every single start. They are
# skipped (noted on stderr).
#
# Submodules already present are left exactly as they are, so a tree that floated
# `config` / `.agents/shared` to a branch tip via `./config/pull` is never
# silently rewound to the pin. That makes the script idempotent and safe to run on
# every session start.
#
# It does NOT float submodules to their branch tips — run `./config/pull`
# afterwards for that. Unlike `pull`, it depends on no pre-existing `config`
# submodule, so it can bootstrap a bare worktree where `./config/pull` does not
# yet exist.
#
################################################################################
set -u
root=$(git rev-parse --show-toplevel 2>/dev/null) || exit 0
cd "$root" || exit 0
# Nothing to do in a repo without submodules.
[ -f .gitmodules ] || exit 0
# The set of config-managed submodule paths: `config` itself (handled specially —
# it carries no tracked branch, exactly as in `./config/pull`), plus every
# submodule declaring a tracked `branch` in `.gitmodules`. Mirrors `pull`'s rule.
config_managed_paths() {
printf '%s\n' 'config'
git config -f .gitmodules --get-regexp '^submodule\..*\.branch$' 2>/dev/null \
| while read -r key _branch; do
name=${key#submodule.}; name=${name%.branch}
git config -f .gitmodules --get "submodule.$name.path" 2>/dev/null
done
}
managed=$(config_managed_paths | sort -u)
# `git submodule status` prefixes each uninitialized submodule with `-`; an
# initialized one starts with a space (at the pinned commit) or `+` (ahead of it).
# Act only on the `-` lines, taking the path from the second field, and only when
# that path is config-managed.
git submodule status 2>/dev/null | awk '$1 ~ /^-/ { print $2 }' | while read -r path; do
[ -n "$path" ] || continue
if printf '%s\n' "$managed" | grep -qxF -- "$path"; then
echo "init-submodules: initializing '$path'"
git submodule update --init --recursive -- "$path" \
|| echo "init-submodules: WARNING — could not initialize '$path' (offline?)." >&2
else
echo "init-submodules: skipping consumer-owned '$path' (not config-managed)." >&2
fi
done
# Route Git hooks to the shared hooks directory so the secret-scan `pre-commit`
# hook is active even in a brand-new worktree, before `./config/pull` runs. The
# path floats with the `.agents/shared` submodule; until that submodule is
# initialized the hook simply does not fire (Git skips a missing hook). Set only
# when unset or already ours — never override a repo's own `core.hooksPath`.
desired_hooks=".agents/scripts/git-hooks"
current_hooks=$(git config --local --get core.hooksPath 2>/dev/null || true)
if [ -z "$current_hooks" ] || [ "$current_hooks" = "$desired_hooks" ]; then
git config --local core.hooksPath "$desired_hooks" \
&& echo "init-submodules: Git hooks routed to '$desired_hooks' (secret-scan pre-commit active)."
fi
exit 0