diff --git a/.github/workflows/check_code_formating.yml b/.github/workflows/check_code_formating.yml new file mode 100644 index 00000000..e398db98 --- /dev/null +++ b/.github/workflows/check_code_formating.yml @@ -0,0 +1,41 @@ +name: Clang-Format 21 Check + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + clang-format: + runs-on: ubuntu-22.04 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install clang-format 21 via LLVM APT repo + run: | + # Import LLVM GPG key properly + wget -qO - https://apt.llvm.org/llvm-snapshot.gpg.key | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/llvm-snapshot.gpg > /dev/null + + # Add LLVM repo for clang-format 21 (Debian bullseye repo works on Ubuntu 22.04) + echo "deb http://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-21 main" | sudo tee /etc/apt/sources.list.d/llvm-21.list + + # Update package lists + sudo apt-get update --allow-releaseinfo-change + + # Install clang-format 21 + sudo apt-get install -y clang-format-21 + + # Set clang-format 21 as default + sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-21 210 + sudo update-alternatives --set clang-format /usr/bin/clang-format-21 + + # Verify + clang-format --version + + - name: Run clang-format check + run: python3 check_clang_format.py \ No newline at end of file diff --git a/check_clang_format.py b/check_clang_format.py new file mode 100644 index 00000000..f8b99a43 --- /dev/null +++ b/check_clang_format.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 + +import os +import subprocess +import sys + +PROJECT_DIRECTORY = os.path.abspath(os.path.dirname(__file__)) + +FILE_LOCATIONS = [ + os.path.join(PROJECT_DIRECTORY, "core"), + os.path.join(PROJECT_DIRECTORY, "core_hd_mapping"), + os.path.join(PROJECT_DIRECTORY, "apps"), + os.path.join(PROJECT_DIRECTORY, "pybind"), + os.path.join(PROJECT_DIRECTORY, "shared"), +] + +FILE_EXTENSIONS = [ + ".cpp", + ".hpp", + ".c", + ".h", +] + + +def get_files(input_paths: list[str], extensions: list[str]): + files = [] + for input_path in input_paths: + for dirpath, _, filenames in os.walk(input_path): + for filename in filenames: + if filename.endswith(tuple(extensions)): + files.append(os.path.normpath(os.path.join(dirpath, filename))) + return files + + +def run(cmd): + return subprocess.run( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + ) + + +def main(): + formatted_files = set(get_files(FILE_LOCATIONS, FILE_EXTENSIONS)) + + formatted_files = { + os.path.relpath(path, PROJECT_DIRECTORY) + for path in formatted_files + } + + result = run([sys.executable, "run_clang_format.py"]) + if result.returncode != 0: + print("ERROR: run_clang_format.py failed") + print(result.stderr) + sys.exit(result.returncode) + + status = run(["git", "status", "--porcelain"]) + if status.returncode != 0: + print("ERROR: git status failed") + print(status.stderr) + sys.exit(status.returncode) + + changed_files = set() + + for line in status.stdout.splitlines(): + path = line[3:] + changed_files.add(os.path.normpath(path)) + + offending_files = sorted(formatted_files & changed_files) + + if offending_files: + print("ERROR: clang-format produced changes in the following files:") + for f in offending_files: + print(f" {f}") + sys.exit(1) + + print("clang-format check passed") + + +if __name__ == "__main__": + main()