forked from BasedHardware/omi
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpre-commit
More file actions
executable file
·214 lines (196 loc) · 10.2 KB
/
Copy pathpre-commit
File metadata and controls
executable file
·214 lines (196 loc) · 10.2 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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#!/bin/sh
# Fail before minting a commit if this clone still has a test-fixture Git identity.
# A leftover repo-local user.email (Ratchet Test / *.invalid) overrides the global
# identity, and GitHub then will not attribute the commit to the person who pushed.
ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
python3 "$ROOT/.github/scripts/check_git_author_identity.py" --pending || exit 1
# Universal source hygiene: fail fast on unresolved merge conflict markers.
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)
if [ -n "$STAGED_FILES" ]; then
conflict_found=0
while IFS= read -r file; do
[ -n "$file" ] || continue
if git grep --cached -n -I -E '^(<<<<<<<|>>>>>>>)' -- "$file"; then
conflict_found=1
fi
done <<EOF
$STAGED_FILES
EOF
if [ "$conflict_found" -eq 1 ]; then
echo "Unresolved merge conflict markers found in staged files" >&2
exit 1
fi
fi
# Dart formatting for app/
STAGED_DART_FILES=$(git diff --cached --name-only --diff-filter=ACM "app/**.dart" | grep -v -e '\.gen\.dart$' -e '\.g\.dart$' -e '^app/lib/l10n/app_localizations.*\.dart$')
if [ -n "$STAGED_DART_FILES" ] && [ "${OMI_SKIP_DART_FORMAT:-}" = "1" ]; then
echo "OMI_SKIP_DART_FORMAT=1: leaving staged Dart files unformatted."
STAGED_DART_FILES=""
fi
if [ -n "$STAGED_DART_FILES" ]; then
ROOT="$(git rev-parse --show-toplevel)"
PINNED_FLUTTER=$(git show :.github/workflows/repo-checks.yml 2>/dev/null | sed -n 's/^[[:space:]]*flutter-version:[[:space:]]*//p' | head -1 | tr -d "\"' ")
if [ -z "$PINNED_FLUTTER" ]; then
PINNED_FLUTTER=$(sed -n 's/^[[:space:]]*flutter-version:[[:space:]]*//p' "$ROOT/.github/workflows/repo-checks.yml" 2>/dev/null | head -1 | tr -d "\"' ")
fi
if [ -z "$PINNED_FLUTTER" ]; then
echo "Could not read the pinned flutter-version from .github/workflows/repo-checks.yml." >&2
echo "Refusing to run 'dart format' with an unknown toolchain. Set OMI_SKIP_DART_FORMAT=1 to commit unformatted." >&2
exit 1
fi
if command -v flutter >/dev/null 2>&1; then
# Git exports linked-worktree metadata to hooks. Flutter resolves its own
# SDK version through Git, so those variables must not leak into the probe.
LOCAL_FLUTTER=$(
unset GIT_DIR GIT_WORK_TREE
flutter --version 2>/dev/null | sed -n 's/^Flutter \([0-9][^ ]*\).*/\1/p' | head -1
)
else
LOCAL_FLUTTER=""
fi
if [ "$LOCAL_FLUTTER" != "$PINNED_FLUTTER" ]; then
echo "Dart formatter toolchain mismatch: repo pins Flutter $PINNED_FLUTTER, found ${LOCAL_FLUTTER:-no flutter on PATH}." >&2
echo "A different Dart SDK reformats untouched files into a shape CI rejects, so formatting is refused." >&2
echo "Install Flutter $PINNED_FLUTTER (fvm use $PINNED_FLUTTER), or set OMI_SKIP_DART_FORMAT=1 to commit unformatted." >&2
exit 1
fi
DART_BIN="$(dirname "$(command -v flutter)")/dart"
if [ ! -x "$DART_BIN" ]; then
echo "Dart formatter sibling not found: expected $DART_BIN next to the validated flutter." >&2
echo "Refusing to run an unrelated 'dart' from PATH. Set OMI_SKIP_DART_FORMAT=1 to commit unformatted." >&2
exit 1
fi
echo "Formatting staged Dart files in app/ with Flutter $PINNED_FLUTTER..."
echo "$STAGED_DART_FILES" | xargs "$DART_BIN" format --line-length 120
echo "$STAGED_DART_FILES" | xargs git add
fi
# Python formatting for backend/
STAGED_PYTHON_FILES=$(git diff --cached --name-only --diff-filter=ACM "backend/**.py")
if [ -n "$STAGED_PYTHON_FILES" ]; then
echo "Formatting staged Python files in backend/ with the pinned formatter..."
echo "$STAGED_PYTHON_FILES" | xargs "$ROOT/scripts/backend-python-format" --write
echo "$STAGED_PYTHON_FILES" | xargs git add
fi
# ARB (l10n JSON) formatting for app/lib/l10n/
STAGED_ARB_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '\.arb$')
if [ -n "$STAGED_ARB_FILES" ]; then
if command -v jq >/dev/null 2>&1; then
echo "Formatting ARB files to 4-space JSON indent..."
for f in $STAGED_ARB_FILES; do
jq --indent 4 '.' "$f" > "$f.tmp" && mv "$f.tmp" "$f"
done
echo "$STAGED_ARB_FILES" | xargs git add
else
echo "jq is not installed. Please run 'sudo apt install jq' or 'brew install jq'" >&2
exit 1
fi
fi
# Web (JS/TS/JSX/TSX) formatting with Prettier
# Auto-generated TypeScript (e.g. the admin OpenAPI client) is excluded: a
# regenerated artifact must not be swept through Prettier's format-and-add.
STAGED_WEB_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(js|jsx|ts|tsx)$' | grep -E '^web/' | grep -v '\.generated\.ts$')
if [ -n "$STAGED_WEB_FILES" ] && [ "${OMI_SKIP_WEB_FORMAT:-}" = "1" ]; then
echo "OMI_SKIP_WEB_FORMAT=1: leaving staged web files unformatted."
STAGED_WEB_FILES=""
fi
if [ -n "$STAGED_WEB_FILES" ]; then
echo "Formatting staged web files with Prettier..."
for dir in $(echo "$STAGED_WEB_FILES" | cut -d/ -f1-2 | sort -u); do
DIR_FILES=$(echo "$STAGED_WEB_FILES" | grep "^$dir/")
if [ -z "$DIR_FILES" ]; then
continue
fi
PINNED=$(git show :"$dir/package.json" 2>/dev/null | sed -n 's/.*"prettier"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1)
if [ -z "$PINNED" ]; then
PINNED=$(sed -n 's/.*"prettier"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$dir/package.json" 2>/dev/null | head -1)
fi
if [ -z "$PINNED" ]; then
echo "$dir/package.json does not pin prettier; refusing to format $dir." >&2
echo "Pin prettier there, or set OMI_SKIP_WEB_FORMAT=1 to commit unformatted." >&2
exit 1
fi
PRETTIER_BIN="$dir/node_modules/.bin/prettier"
if [ -x "$PRETTIER_BIN" ]; then
LOCAL_VERSION=$("$PRETTIER_BIN" --version 2>/dev/null)
else
LOCAL_VERSION=""
fi
RESOLVED_VERSION=$(git show :"$dir/package-lock.json" 2>/dev/null | python3 -c "import json,sys; d=json.load(sys.stdin); print((d.get('packages',{}).get('node_modules/prettier') or {}).get('version',''))" 2>/dev/null)
if [ -n "$RESOLVED_VERSION" ]; then
if [ -z "$LOCAL_VERSION" ] || [ "$LOCAL_VERSION" != "$RESOLVED_VERSION" ]; then
echo "Prettier toolchain mismatch in $dir: package-lock resolves $RESOLVED_VERSION, found ${LOCAL_VERSION:-no $dir/node_modules/.bin/prettier}." >&2
echo "A floating prettier reformats files into a shape CI rejects, so formatting is refused." >&2
echo "Run 'npm ci' in $dir, or set OMI_SKIP_WEB_FORMAT=1 to commit unformatted." >&2
exit 1
fi
else
echo "Prettier lockfile pin missing or unresolvable in $dir: refusing to format $dir." >&2
echo "A missing, empty, or unparseable package-lock.json lets a floating prettier reformat files into a shape CI rejects." >&2
echo "Run 'npm ci' in $dir, or set OMI_SKIP_WEB_FORMAT=1 to commit unformatted." >&2
exit 1
fi
for plugin in prettier-plugin-tailwindcss; do
PLUGIN_RESOLVED=$(git show :"$dir/package-lock.json" 2>/dev/null | python3 -c "import json,sys; d=json.load(sys.stdin); print((d.get('packages',{}).get('node_modules/' + sys.argv[1]) or {}).get('version',''))" "$plugin" 2>/dev/null)
PLUGIN_DECLARED=$(git show :"$dir/package.json" 2>/dev/null | sed -n "s/.*\"$plugin\"[[:space:]]*:[[:space:]]*\"\([^\"]*\)\".*/\1/p" | head -1)
if [ -z "$PLUGIN_DECLARED" ]; then
PLUGIN_DECLARED=$(sed -n "s/.*\"$plugin\"[[:space:]]*:[[:space:]]*\"\([^\"]*\)\".*/\1/p" "$dir/package.json" 2>/dev/null | head -1)
fi
if [ -z "$PLUGIN_RESOLVED" ]; then
if [ -n "$PLUGIN_DECLARED" ]; then
echo "Prettier plugin toolchain unresolved in $dir: package.json declares $plugin $PLUGIN_DECLARED, package-lock.json has no entry for it." >&2
echo "Prettier auto-loads any installed plugin, so a stale or missing one reformats files into a shape CI rejects; formatting is refused." >&2
echo "Run 'npm ci' in $dir, or set OMI_SKIP_WEB_FORMAT=1 to commit unformatted." >&2
exit 1
fi
continue
fi
PLUGIN_PKG="$dir/node_modules/$plugin/package.json"
if [ -f "$PLUGIN_PKG" ]; then
PLUGIN_LOCAL=$(python3 -c "import json,sys; print(json.load(open(sys.argv[1])).get('version',''))" "$PLUGIN_PKG" 2>/dev/null)
else
PLUGIN_LOCAL=""
fi
if [ -z "$PLUGIN_LOCAL" ] || [ "$PLUGIN_LOCAL" != "$PLUGIN_RESOLVED" ]; then
echo "Prettier plugin toolchain mismatch in $dir: package-lock resolves $plugin $PLUGIN_RESOLVED, found ${PLUGIN_LOCAL:-no $dir/node_modules/$plugin}." >&2
echo "A missing or stale plugin reformats files into a shape CI rejects, so formatting is refused." >&2
echo "Run 'npm ci' in $dir, or set OMI_SKIP_WEB_FORMAT=1 to commit unformatted." >&2
exit 1
fi
done
for file in $DIR_FILES; do
if ! git diff --quiet -- "$file"; then
echo "$file has unstaged edits; refusing to format-and-add because it would sweep them into the commit." >&2
echo "Stage or commit the remaining edits first, or set OMI_SKIP_WEB_FORMAT=1 to commit unformatted." >&2
exit 1
fi
done
echo "$DIR_FILES" | xargs "$PRETTIER_BIN" --write
echo "$DIR_FILES" | xargs git add
done
fi
# C/C++ formatting for firmware/
STAGED_C_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(c|cpp|cc|cxx|h|hpp)$' | grep -E '^(omi/|omiGlass/)')
if [ -n "$STAGED_C_FILES" ]; then
echo "Formatting staged C/C++ files in firmware..."
if ! command -v clang-format >/dev/null 2>&1; then
echo "clang-format is not installed. Please install it first" >&2
exit 1
fi
echo "$STAGED_C_FILES" | xargs clang-format -i
echo "$STAGED_C_FILES" | xargs git add
fi
# Swift formatting for desktop/macos/Desktop/
STAGED_SWIFT_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '^desktop/macos/Desktop/.*\.swift$' | grep -v '/Generated/' || true)
if [ -n "$STAGED_SWIFT_FILES" ]; then
echo "Formatting staged Swift files in desktop/macos/Desktop/..."
if ! command -v xcrun >/dev/null 2>&1; then
echo "xcrun not found — Swift formatting requires macOS with Xcode" >&2
echo "Bootstrap: desktop/macos/scripts/swift-format-wrapper.sh bootstrap" >&2
exit 1
fi
ROOT="$(git rev-parse --show-toplevel)"
WRAPPER="$ROOT/desktop/macos/scripts/swift-format-wrapper.sh"
echo "$STAGED_SWIFT_FILES" | xargs "$WRAPPER" format -i
echo "$STAGED_SWIFT_FILES" | xargs git add
fi
exit 0