Skip to content

Commit a44e4e8

Browse files
committed
Enforce consistent coding style
This applies clang-format to entire codebase: - 4-space indentation (no tabs) - Linux kernel brace style (functions: new line, control: same line) - Right-aligned pointer declarations - Consistent operator spacing CI infrastructure: - Add GitHub Actions workflow for automated style checking - Add .ci/ scripts for format validation (C, shell, Python) - Configure clang-format-20, shfmt, and black checks
1 parent b5bac12 commit a44e4e8

File tree

213 files changed

+18215
-15618
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

213 files changed

+18215
-15618
lines changed

.ci/check-format.sh

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
#!/usr/bin/env bash
2+
# Copyright (c) 2025 The F9 Microkernel Project. All rights reserved.
3+
# Use of this source code is governed by a BSD-style license that can be
4+
# found in the LICENSE file.
5+
#
6+
# Format validation for C, shell, and Python files
7+
# The -e is not set because we want to collect all format mismatches at once
8+
9+
set -u -o pipefail
10+
11+
# In CI environment, require all formatting tools to be present
12+
# GitHub Actions sets CI=true automatically (also accepts CI=1, CI=yes, etc.)
13+
REQUIRE_TOOLS="false"
14+
if [ -n "${CI:-}" ]; then
15+
REQUIRE_TOOLS="true"
16+
fi
17+
18+
C_FORMAT_EXIT=0
19+
SH_FORMAT_EXIT=0
20+
PY_FORMAT_EXIT=0
21+
22+
# Use git ls-files to exclude submodules and untracked files
23+
# F9 Microkernel structure: include/, kernel/, platform/, user/, board/, loader/
24+
# Exclude: externals/ (third-party code with own style)
25+
C_SOURCES=()
26+
while IFS= read -r file; do
27+
[ -n "$file" ] && C_SOURCES+=("$file")
28+
done < <(git ls-files -- 'include/*.h' 'include/**/*.h' 'kernel/*.c' 'platform/*.c' 'platform/**/*.c' 'platform/*.h' 'platform/**/*.h' 'user/*.c' 'user/**/*.c' 'user/*.h' 'user/**/*.h' 'board/*.c' 'board/**/*.c' 'board/*.h' 'board/**/*.h' 'loader/*.c' 'loader/**/*.c' 'loader/*.h' 'loader/**/*.h' | grep -v '^externals/' || true)
29+
30+
if [ ${#C_SOURCES[@]} -gt 0 ]; then
31+
# Try clang-format with version suffix first, then generic
32+
CLANG_FORMAT=""
33+
for cmd in clang-format-21 clang-format-20 clang-format-19 clang-format; do
34+
if command -v "$cmd" >/dev/null 2>&1; then
35+
CLANG_FORMAT="$cmd"
36+
break
37+
fi
38+
done
39+
40+
if [ -n "$CLANG_FORMAT" ]; then
41+
echo "Checking ${#C_SOURCES[@]} C/C++ files with $CLANG_FORMAT..."
42+
$CLANG_FORMAT -n --Werror "${C_SOURCES[@]}"
43+
C_FORMAT_EXIT=$?
44+
if [ $C_FORMAT_EXIT -eq 0 ]; then
45+
echo " ✓ All C/C++ files properly formatted"
46+
else
47+
echo " ✗ C/C++ format violations found" >&2
48+
fi
49+
else
50+
if [ "$REQUIRE_TOOLS" = "true" ]; then
51+
echo "ERROR: clang-format not found (required in CI)" >&2
52+
C_FORMAT_EXIT=1
53+
else
54+
echo "Skipping C format check: clang-format not found" >&2
55+
fi
56+
fi
57+
else
58+
echo "No C/C++ files to check"
59+
fi
60+
61+
SH_SOURCES=()
62+
while IFS= read -r file; do
63+
[ -n "$file" ] && SH_SOURCES+=("$file")
64+
done < <(git ls-files -- '*.sh' '.ci/*.sh' 'scripts/*.sh' | grep -v '^externals/' || true)
65+
66+
if [ ${#SH_SOURCES[@]} -gt 0 ]; then
67+
if command -v shfmt >/dev/null 2>&1; then
68+
echo "Checking ${#SH_SOURCES[@]} shell scripts with shfmt..."
69+
MISMATCHED_SH=$(shfmt -l "${SH_SOURCES[@]}")
70+
if [ -n "$MISMATCHED_SH" ]; then
71+
echo " ✗ The following shell scripts need formatting:" >&2
72+
printf ' %s\n' $MISMATCHED_SH >&2
73+
echo " Run: shfmt -w \$(git ls-files '*.sh' '.ci/*.sh' 'scripts/*.sh')" >&2
74+
SH_FORMAT_EXIT=1
75+
else
76+
echo " ✓ All shell scripts properly formatted"
77+
fi
78+
else
79+
if [ "$REQUIRE_TOOLS" = "true" ]; then
80+
echo "ERROR: shfmt not found (required in CI)" >&2
81+
SH_FORMAT_EXIT=1
82+
else
83+
echo "Skipping shell script format check: shfmt not found" >&2
84+
fi
85+
fi
86+
else
87+
echo "No shell scripts to check"
88+
fi
89+
90+
PY_SOURCES=()
91+
while IFS= read -r file; do
92+
[ -n "$file" ] && PY_SOURCES+=("$file")
93+
done < <(git ls-files -- 'scripts/*.py' | grep -v '^externals/' || true)
94+
95+
if [ ${#PY_SOURCES[@]} -gt 0 ]; then
96+
if command -v black >/dev/null 2>&1; then
97+
echo "Checking ${#PY_SOURCES[@]} Python files with black..."
98+
black --check --quiet "${PY_SOURCES[@]}" 2>&1
99+
PY_FORMAT_EXIT=$?
100+
if [ $PY_FORMAT_EXIT -eq 0 ]; then
101+
echo " ✓ All Python files properly formatted"
102+
else
103+
echo " ✗ Python format violations found" >&2
104+
echo " Run: black scripts/*.py" >&2
105+
fi
106+
else
107+
if [ "$REQUIRE_TOOLS" = "true" ]; then
108+
echo "ERROR: black not found (required in CI)" >&2
109+
PY_FORMAT_EXIT=1
110+
else
111+
echo "Skipping Python format check: black not found" >&2
112+
fi
113+
fi
114+
else
115+
echo "No Python files to check"
116+
fi
117+
118+
# Summary
119+
echo ""
120+
echo "Format check summary:"
121+
echo " C/C++: $([ $C_FORMAT_EXIT -eq 0 ] && echo "✓ PASS" || echo "✗ FAIL")"
122+
echo " Shell: $([ $SH_FORMAT_EXIT -eq 0 ] && echo "✓ PASS" || echo "✗ FAIL")"
123+
echo " Python: $([ $PY_FORMAT_EXIT -eq 0 ] && echo "✓ PASS" || echo "✗ FAIL")"
124+
125+
# Use logical OR to avoid exit code overflow (codes are mod 256)
126+
if [ $C_FORMAT_EXIT -ne 0 ] || [ $SH_FORMAT_EXIT -ne 0 ] || [ $PY_FORMAT_EXIT -ne 0 ]; then
127+
echo ""
128+
echo "Format check FAILED"
129+
exit 1
130+
fi
131+
132+
echo ""
133+
echo "Format check PASSED"
134+
exit 0

.ci/check-newline.sh

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#!/usr/bin/env bash
2+
# Copyright (c) 2025 The F9 Microkernel Project. All rights reserved.
3+
# Use of this source code is governed by a BSD-style license that can be
4+
# found in the LICENSE file.
5+
#
6+
# Check all tracked text files for trailing newlines
7+
# Excludes: externals/ (third-party code), binary files, build artifacts
8+
# Reference: https://medium.com/@alexey.inkin/how-to-force-newline-at-end-of-files-and-why-you-should-do-it-fdf76d1d090e
9+
10+
set -e -u -o pipefail
11+
12+
ret=0
13+
show=0
14+
checked=0
15+
failed=0
16+
echo "Checking for missing trailing newlines..."
17+
18+
while IFS= read -rd '' f; do
19+
# Skip excluded directories and files
20+
case "$f" in
21+
externals/*) continue ;; # Third-party code
22+
.config | .config.* | build/*) continue ;; # Build artifacts
23+
*.bin | *.elf | *.o | *.a) continue ;; # Binaries
24+
esac
25+
26+
# Skip empty files (e.g., __init__.py markers)
27+
[ -s "$f" ] || continue
28+
29+
# Only check text files
30+
if file --mime-encoding "$f" | grep -qv binary; then
31+
checked=$((checked + 1))
32+
tail -c1 <"$f" | read -r _ || show=1
33+
if [ $show -eq 1 ]; then
34+
echo " ✗ Missing newline: $f" >&2
35+
failed=$((failed + 1))
36+
ret=1
37+
show=0
38+
fi
39+
fi
40+
done < <(git ls-files -z)
41+
42+
# Summary
43+
echo ""
44+
echo "Newline check summary:"
45+
echo " Files checked: $checked"
46+
if [ $ret -eq 0 ]; then
47+
echo " Result: ✓ PASS - All files end with newline"
48+
else
49+
echo " Files failed: $failed"
50+
echo " Result: ✗ FAIL - Some files missing trailing newline" >&2
51+
echo ""
52+
echo "To fix: Add newline to end of each file listed above" >&2
53+
fi
54+
55+
exit $ret

0 commit comments

Comments
 (0)