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
32 changes: 32 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Runs the same checks CI does, before CI does — install once with `prek install` (or
# `pre-commit install`, prek reads this same file) and this runs automatically on every
# `git commit`. Skip it for one commit with `git commit --no-verify`; run it on demand
# against everything with `prek run --all-files`.
#
# Scoped to the files in the commit, not the whole tree: CI's `clang-format` job checks
# every source file on every push, but a commit here should live or die on the files it
# actually changed, not on some unrelated file elsewhere that happened to already be
# unformatted — that file is CI's problem to catch (on whatever commit touches it), not a
# reason to block this one.
#
# A local hook, not one of the upstream clang-format mirrors: those track whatever
# clang-format version they were packaged against, and scripts/format.sh already pins the
# exact version CI uses (22.1.8) so a contributor's hook and CI's job never disagree about
# what "formatted" means. Install that exact version with
# `pip install clang-format==22.1.8` — not `brew install clang-format`, whose formula
# tracks upstream's latest release rather than this pin, and will eventually drift ahead
# of it.
repos:
- repo: local
hooks:
- id: clang-format
name: clang-format (scripts/format.sh)
description: >-
Reformats the C/C++ source files in this commit in place with the pinned
clang-format, matching what CI's `clang-format` job would say about those
specific files. If it changes anything, the commit is blocked — `git add -u`
the reformatted files and commit again.
entry: scripts/format.sh
language: system
files: \.(c|h|cpp|hpp)$

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The pre-commit hook is skipped for commits without C/C++ files, creating a discrepancy with CI which always runs the formatter.
Severity: LOW

Suggested Fix

Add always_run: true to the clang-format hook configuration in .pre-commit-config.yaml. This will ensure the formatting script runs on every commit, regardless of the file types being committed, aligning local behavior with the CI process.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: .pre-commit-config.yaml#L24

Potential issue: The pre-commit hook in `.pre-commit-config.yaml` is configured with a
`files` filter for C/C++ files but lacks the `always_run: true` setting. Consequently,
if a developer commits only non-C/C++ files (e.g., `README.md`), the formatting hook is
skipped locally. However, the CI pipeline unconditionally runs the formatter on all
C/C++ files. This disparity can cause a commit to pass local checks but fail in CI if
any unrelated C/C++ file in the repository is unformatted, defeating the goal of
catching formatting issues before pushing.

pass_filenames: true
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,26 @@ cd examples/wifi_basic && pio run # compile-check against every ESP32 varia
Building the example on all four variants is the portability gate — a change that breaks
RISC-V or single-core builds fails there rather than on someone's bench.

**Formatting runs in CI (`clang-format`, pinned to 22.1.8) and fails the build if a file
isn't formatted.** Catch it before pushing instead of after:

```bash
pip install clang-format==22.1.8 # exact version — brew's formula tracks upstream
# latest and will eventually drift off this pin
brew install prek # or: pipx install prek — https://prek.j178.dev
prek install # one-time; also works with `pre-commit install`
```

This installs a git hook from [`.pre-commit-config.yaml`](.pre-commit-config.yaml) that
runs `scripts/format.sh` — the exact script and clang-format version CI uses — on the
C/C++ files in each `git commit`. Deliberately narrower than CI, which checks the whole
tree on every push: this only fails your commit over files *you* touched, not some
already-unformatted file elsewhere that CI would also catch on its own. If it reformats
anything, the commit is blocked; `git add -u` the reformatted files and commit again. Run
it on demand with `prek run --all-files`, or skip it for one commit with
`git commit --no-verify`. (Git hooks live in the repository, not the checkout —
installing from one `git worktree` of this repo enables it for all of them.)

**Touching anything that uses an Arduino API? Build C6 too:**

```bash
Expand Down
50 changes: 34 additions & 16 deletions scripts/format.sh
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
#!/usr/bin/env bash
#
# Format (or check) every source file in the repo.
# Format (or check) source files — every one in the repo by default, or just the ones
# named on the command line.
#
# scripts/format.sh rewrite files in place
# scripts/format.sh --check exit non-zero if anything is unformatted (what CI runs)
# scripts/format.sh rewrite every file in place
# scripts/format.sh --check exit non-zero if anything is unformatted (what CI runs)
# scripts/format.sh foo.c bar.h rewrite only those files
# scripts/format.sh --check foo.c check only that file
#
# The explicit-file form exists for the pre-commit hook (.pre-commit-config.yaml): it should
# format only what a commit actually touches, not fail it over some unrelated file elsewhere
# in the tree that CI's whole-repo check would also catch on its own.
#
# clang-format's output shifts between major versions, so a contributor on a different
# version would otherwise "fix" formatting on every file they touch. CI pins 22.1.8 via pip;
Expand All @@ -20,25 +27,36 @@ if ! command -v "$CLANG_FORMAT" >/dev/null 2>&1; then
exit 1
fi

# Only our own sources: -prune keeps this out of .pio/, which holds the entire Arduino core
# and ESP-IDF once anything has been built.
# (A read loop rather than `mapfile`, which needs bash 4 — macOS still ships bash 3.2.)
files=()
while IFS= read -r file; do
files+=("$file")
done < <(
find . \
\( -name .git -o -name .pio -o -name build \) -prune -o \
\( -name '*.c' -o -name '*.h' -o -name '*.cpp' -o -name '*.hpp' \) -print |
sort
)
CHECK=0
if [ "${1:-}" = "--check" ]; then
CHECK=1
shift
fi

if [ "$#" -gt 0 ]; then
# Explicit files, e.g. from pre-commit: format/check exactly those, in the order given.
files=("$@")
else
# Only our own sources: -prune keeps this out of .pio/, which holds the entire Arduino
# core and ESP-IDF once anything has been built.
# (A read loop rather than `mapfile`, which needs bash 4 — macOS still ships bash 3.2.)
files=()
while IFS= read -r file; do
files+=("$file")
done < <(
find . \
\( -name .git -o -name .pio -o -name build \) -prune -o \
\( -name '*.c' -o -name '*.h' -o -name '*.cpp' -o -name '*.hpp' \) -print |
sort
)
fi

if [ ${#files[@]} -eq 0 ]; then
echo "no sources found" >&2
exit 1
fi

if [ "${1:-}" = "--check" ]; then
if [ "$CHECK" -eq 1 ]; then
"$CLANG_FORMAT" --dry-run -Werror "${files[@]}"
echo "${#files[@]} files are correctly formatted."
else
Expand Down
Loading