From 8efb25a9d4e867b19d37fc114ef09874ff135d2c Mon Sep 17 00:00:00 2001 From: BrandonPacewic <92102436+BrandonPacewic@users.noreply.github.com> Date: Sun, 1 Mar 2026 14:42:00 -0800 Subject: [PATCH] feat: pre commit --- .github/workflows/lint.yml | 30 ++++++++++--- Makefile | 7 ++- scripts/lint.sh | 89 ++++++++++++++++++++++++++++++++++++++ scripts/pre-commit | 3 ++ 4 files changed, 121 insertions(+), 8 deletions(-) create mode 100755 scripts/lint.sh create mode 100755 scripts/pre-commit diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 53a0ffa..09e40f3 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -10,12 +10,28 @@ jobs: lint: runs-on: ubuntu-latest steps: - - name: Checkout Code - uses: actions/checkout@v3 + - name: Checkout + uses: actions/checkout@v4 + + - name: Install dependencies + run: sudo apt-get install -y clang-format cmake ccache + + - name: Restore ccache + uses: actions/cache@v4 with: - fetch-depth: 0 + path: ~/.cache/ccache + key: ccache-${{ runner.os }}-${{ hashFiles('cpl/inc/**', 'tests/**/*.cpp', 'CMakeLists.txt', 'cpl/CMakeLists.txt', 'tests/CMakeLists.txt') }} + restore-keys: ccache-${{ runner.os }}- + + - name: Configure ccache + run: ccache --set-config max_size=500M - - name: Check Formatting - run: | - find cpl/inc cpl/src tests/cpl -regex '.*\.\(cpp\|h\|hpp\)' -exec clang-format -i --style=file {} + - git diff --exit-code || (echo 'Formatting issues found. Please run clang-format.' && exit 1) + - name: Run lint + run: scripts/lint.sh + + - name: Save ccache + uses: actions/cache/save@v4 + if: always() + with: + path: ~/.cache/ccache + key: ccache-${{ runner.os }}-${{ hashFiles('cpl/inc/**', 'tests/**/*.cpp', 'CMakeLists.txt', 'cpl/CMakeLists.txt', 'tests/CMakeLists.txt') }} diff --git a/Makefile b/Makefile index a96f156..d6f48be 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ CMAKE_FLAGS = \ -DCMAKE_EXPORT_COMPILE_COMMANDS=$(COMPILE_COMMANDS) .PHONY: build test bench tools \ - format clean distclean help + format install-hooks clean distclean help configure: @mkdir -p "$(BUILD_DIR)" @@ -53,6 +53,10 @@ tools: build format: @clang-format -i -style=file $(shell git ls-files '*.hpp' '*.h' '*.cpp') +install-hooks: + @ln -sf ../../scripts/pre-commit .git/hooks/pre-commit + @echo "Pre-commit hook installed" + clean: @cmake --build "$(BUILD_DIR)" --target clean || true @@ -72,6 +76,7 @@ help: @echo " bench-out - output the most recent benchmark run" @echo " tools - build project-defined tools (BUILD_TOOLS=ON)" @echo " format - run clang-format on all source files" + @echo " install-hooks - install pre-commit hook (calls scripts/lint.sh)" @echo " clean - clean build artifacts" @echo " distclean - clean entire build dir" @echo "" diff --git a/scripts/lint.sh b/scripts/lint.sh new file mode 100755 index 0000000..c16be80 --- /dev/null +++ b/scripts/lint.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash + +# Copyright (c) Brandon Pacewic +# SPDX-License-Identifier: MIT + +set -e + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" +ROOT="$DIR/../" +cd $ROOT + +RED='\033[0;31m' +GREEN='\033[0;32m' +NC='\033[0m' + +FAILED=0 +FAST=0 + +while [[ $# -gt 0 ]]; do + case $1 in + --fast) + FAST=1 + shift + ;; + *) + echo "Unknown option: $1" + echo "Usage: $0 [--fast]" + echo " --fast skip build and tests (format + file checks only)" + exit 1 + ;; + esac +done + +function run_check() { + local name="$1" + local cmd="$2" + + local name_len=${#name} + local dots_len=$((46 - name_len)) + local dots=$(printf '%*s' "$dots_len" | tr ' ' '.') + + printf "%s%s" "$name" "$dots" + + set +e + log=$(eval "$cmd" 2>&1) + result=$? + set -e + + if [[ $result -eq 0 ]]; then + echo -e "[${GREEN}ok${NC}]" + else + echo -e "[${RED}FAIL${NC}]" + echo "$log" + FAILED=1 + fi +} + +function check_large_files() { + local limit=$((250 * 1024)) + local found=0 + while IFS= read -r file; do + local size + size=$(wc -c < "$file") + if [[ "$size" -gt "$limit" ]]; then + echo "Large file ($(( size / 1024 ))KB): $file" + found=1 + fi + done < <(git ls-files) + return "$found" +} + +run_check "check_default_branch" "[[ \$(git rev-parse --abbrev-ref HEAD) != mega ]]" +run_check "clang_format" "git ls-files '*.cpp' '*.h' '*.hpp' | xargs clang-format --dry-run --Werror" +run_check "check_large_files" "check_large_files" +run_check "check_merge_conflicts" "! git ls-files | xargs grep -lP '^(<{7}|={7}|>{7})' 2>/dev/null | grep -q ." + +if [[ "$FAST" -eq 0 ]]; then + run_check "build" \ + "cmake -B build -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Debug && cmake --build build -j\$(nproc)" + run_check "tests" "ctest --test-dir build --output-on-failure" +fi + +echo "" +if [[ $FAILED -eq 1 ]]; then + echo -e "${RED}Lint failed${NC}" + exit 1 +else + echo -e "${GREEN}All checks passed${NC}" +fi diff --git a/scripts/pre-commit b/scripts/pre-commit new file mode 100755 index 0000000..1535e7b --- /dev/null +++ b/scripts/pre-commit @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +set -e +scripts/lint.sh