Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions .ci/check-format.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#!/usr/bin/env bash

# The -e is not set because we want to get all the mismatch format at once

set -u -o pipefail

# In CI environment, require all formatting tools to be present
# Set CI=true to enforce tool presence (GitHub Actions sets this automatically)
REQUIRE_TOOLS="${CI:-false}"

C_FORMAT_EXIT=0
SH_FORMAT_EXIT=0

# On pull requests, check only changed files so pre-existing formatting
# debt does not block unrelated PRs. On push (to main) or local runs
# without GITHUB_BASE_REF, check the full tree.
if [ -n "${GITHUB_BASE_REF:-}" ]; then
echo "PR mode: checking only files changed vs origin/${GITHUB_BASE_REF}"
if git fetch --depth=1 origin "${GITHUB_BASE_REF}" 2> /dev/null \
&& CHANGED=$(git diff --name-only --diff-filter=d "origin/${GITHUB_BASE_REF}"...HEAD 2> /dev/null); then
# If formatting config changed, check the full tree against the new rules.
if printf '%s\n' "$CHANGED" | grep -qE '^\.(clang-format|editorconfig)$'; then
echo "Formatting config changed; checking full tree"
CHANGED=$(git ls-files)
fi
else
echo "Warning: cannot compute PR diff, falling back to full-tree check" >&2
CHANGED=$(git ls-files)
fi
else
CHANGED=$(git ls-files)
fi

# Filter changed files into language-specific sets
C_SOURCES=()
while IFS= read -r file; do
[ -n "$file" ] || continue
case "$file" in
include/*.h | src/*.c | src/*.h | ports/*.c | ports/*.h | \
ports/*/*.c | ports/*/*.h | \
ports/*/*/*.c | ports/*/*/*.h)
C_SOURCES+=("$file")
;;
esac
done <<< "$CHANGED"

if [ ${#C_SOURCES[@]} -gt 0 ]; then
if command -v clang-format-20 > /dev/null 2>&1; then
echo "Checking C files with clang-format-20..."
clang-format-20 -n --Werror "${C_SOURCES[@]}"
C_FORMAT_EXIT=$?
elif command -v clang-format > /dev/null 2>&1; then
echo "Checking C files with clang-format..."
clang-format -n --Werror "${C_SOURCES[@]}"
C_FORMAT_EXIT=$?
else
if [ "$REQUIRE_TOOLS" = "true" ]; then
echo "ERROR: clang-format not found (required in CI)" >&2
C_FORMAT_EXIT=1
else
echo "Skipping C format check: clang-format not found" >&2
fi
fi
fi

SH_SOURCES=()
while IFS= read -r file; do
[ -n "$file" ] || continue
case "$file" in
*.sh | .ci/*.sh | scripts/*.sh)
SH_SOURCES+=("$file")
;;
esac
done <<< "$CHANGED"

if [ ${#SH_SOURCES[@]} -gt 0 ]; then
if command -v shfmt > /dev/null 2>&1; then
echo "Checking shell scripts..."
MISMATCHED_SH=$(shfmt -l "${SH_SOURCES[@]}")
if [ -n "$MISMATCHED_SH" ]; then
echo "The following shell scripts are not formatted correctly:"
printf '%s\n' "$MISMATCHED_SH"
shfmt -d "${SH_SOURCES[@]}"
SH_FORMAT_EXIT=1
fi
else
if [ "$REQUIRE_TOOLS" = "true" ]; then
echo "ERROR: shfmt not found (required in CI)" >&2
SH_FORMAT_EXIT=1
else
echo "Skipping shell script format check: shfmt not found" >&2
fi
fi
fi

# Use logical OR to avoid exit code overflow (codes are mod 256)
if [ $C_FORMAT_EXIT -ne 0 ] || [ $SH_FORMAT_EXIT -ne 0 ]; then
exit 1
fi
exit 0
30 changes: 30 additions & 0 deletions .ci/check-newline.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env bash

set -e -u -o pipefail

ret=0
show=0

# Check all tracked text files for trailing newlines
# Excludes: externals/ and cloned RTOS trees (third-party code with own style)
# Reference: https://medium.com/@alexey.inkin/how-to-force-newline-at-end-of-files-and-why-you-should-do-it-fdf76d1d090e
while IFS= read -rd '' f; do
# Skip third-party directories
case "$f" in
externals/* | threadx/* | freertos-kernel/* | tools/kconfig/*) continue ;;
esac

# Skip empty files
[ -s "$f" ] || continue

if file --mime-encoding "$f" | grep -qv binary; then
tail -c1 < "$f" | read -r _ || show=1
if [ $show -eq 1 ]; then
echo "Warning: No newline at end of file $f"
ret=1
show=0
fi
fi
done < <(git ls-files -z)

exit $ret
67 changes: 67 additions & 0 deletions .ci/common.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Bash strict mode (enabled only when executed directly, not sourced)
if ! (return 0 2> /dev/null); then
set -euo pipefail
fi

# Expect host is Linux/x86_64, Linux/aarch64, macOS/arm64

MACHINE_TYPE=$(uname -m)
OS_TYPE=$(uname -s)

check_platform()
{
case "${MACHINE_TYPE}/${OS_TYPE}" in
x86_64/Linux | aarch64/Linux | arm64/Darwin) ;;

*)
echo "Unsupported platform: ${MACHINE_TYPE}/${OS_TYPE}"
exit 1
;;
esac
}

if [ "${OS_TYPE}" = "Linux" ]; then
PARALLEL=-j$(nproc)
else
PARALLEL=-j$(sysctl -n hw.logicalcpu)
fi

# Color output helpers
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

print_success()
{
echo -e "${GREEN}[SUCCESS]${NC} $1"
}

print_error()
{
echo -e "${RED}[ERROR]${NC} $1" >&2
}

print_warning()
{
echo -e "${YELLOW}[WARNING]${NC} $1"
}

# Cleanup function registry
CLEANUP_FUNCS=()

register_cleanup()
{
CLEANUP_FUNCS+=("$1")
}

cleanup()
{
local func
for func in "${CLEANUP_FUNCS[@]-}"; do
[ -n "${func}" ] || continue
eval "${func}" || true
done
}

trap cleanup EXIT
83 changes: 83 additions & 0 deletions .ci/install-deps.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#!/usr/bin/env bash

# Install build dependencies for CI
# Usage: .ci/install-deps.sh [posix|cortex-m|format|analysis]

set -euo pipefail

source "$(dirname "$0")/common.sh"

MODE="${1:-posix}"

# Setup LLVM 20 APT repository (shared by format and analysis modes)
setup_llvm_repo()
{
LLVM_KEYRING=/usr/share/keyrings/llvm-archive-keyring.gpg
LLVM_KEY_FP="6084F3CF814B57C1CF12EFD515CF4D18AF4F7421"
TMPKEY=$(mktemp)
curl -fsSL https://apt.llvm.org/llvm-snapshot.gpg.key -o "$TMPKEY"
ACTUAL_FP=$(gpg --with-fingerprint --with-colons "$TMPKEY" 2> /dev/null | grep fpr | head -1 | cut -d: -f10)
if [ "$ACTUAL_FP" != "$LLVM_KEY_FP" ]; then
print_error "LLVM key fingerprint mismatch!"
print_error "Expected: $LLVM_KEY_FP"
print_error "Got: $ACTUAL_FP"
rm -f "$TMPKEY"
exit 1
fi
sudo gpg --dearmor -o "$LLVM_KEYRING" < "$TMPKEY"
rm -f "$TMPKEY"

CODENAME=$(lsb_release -cs)
echo "deb [signed-by=${LLVM_KEYRING}] https://apt.llvm.org/${CODENAME}/ llvm-toolchain-${CODENAME}-20 main" | sudo tee /etc/apt/sources.list.d/llvm-20.list
sudo apt-get update -q=2
}

case "$MODE" in
posix)
# POSIX host builds only need a C compiler (pre-installed) and python3.
if [ "$OS_TYPE" = "Linux" ]; then
sudo apt-get update -q=2
sudo apt-get install -y -q=2 --no-install-recommends python3
else
brew install python3
fi
;;
cortex-m)
# Cortex-M QEMU builds: cross-compiler + emulator.
if [ "$OS_TYPE" != "Linux" ]; then
print_error "Cortex-M CI only supported on Linux"
exit 1
fi
sudo apt-get update -q=2
sudo apt-get install -y -q=2 --no-install-recommends \
gcc-arm-none-eabi libnewlib-arm-none-eabi \
qemu-system-arm python3
;;
format)
if [ "$OS_TYPE" != "Linux" ]; then
print_error "Formatting tools only supported on Linux"
exit 1
fi
sudo apt-get update -q=2
sudo apt-get install -y -q=2 --no-install-recommends shfmt gnupg ca-certificates lsb-release
setup_llvm_repo
sudo apt-get install -y -q=2 --no-install-recommends clang-format-20
;;
analysis)
if [ "$OS_TYPE" != "Linux" ]; then
print_error "Static analysis only supported on Linux"
exit 1
fi
sudo apt-get update -q=2
sudo apt-get install -y -q=2 --no-install-recommends gnupg ca-certificates lsb-release python3
setup_llvm_repo
sudo apt-get install -y -q=2 --no-install-recommends clang-20 clang-tools-20
;;
*)
print_error "Unknown mode: $MODE"
echo "Usage: $0 [posix|cortex-m|format|analysis]"
exit 1
;;
esac

print_success "Dependencies installed for mode: $MODE"
14 changes: 14 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Top-level EditorConfig file
root = true

# Shell script-specific settings
[*.sh]
indent_style = space
indent_size = 4
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
function_next_line = true
switch_case_indent = true
space_redirects = true
binary_next_line = true
Loading