diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f3038d9..04d0c6e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,15 +10,22 @@ repos: rev: v2.4.1 hooks: - id: codespell - args: [-w, --config, ./.github/coding-convention-tool/tools/.codespell/.codespellrc] + args: [-w, --config, /action/tools/.codespell/.codespellrc] - repo: https://github.com/pocc/pre-commit-hooks rev: v1.3.5 hooks: - id: uncrustify - args: [-c, ./uncrustify/uncrustify.cfg, -lC, --no-backup, --replace] + args: + [ + -c, + /action/tools/uncrustify/uncrustify.cfg, + -lC, + --no-backup, + --replace, + ] - id: clang-tidy args: - - --config-file=./.clang-tidy + - --config-file=/action/tools/.clang-tidy - --use-color - --extra-arg=-I/inc - --header-filter:'^((?!test).)*$' diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b328591 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,32 @@ +FROM ubuntu:22.04 + +ENV DEBIAN_FRONTEND=noninteractive + +# Install dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + python3 python3-pip python3-venv \ + binutils ca-certificates git cmake make gcc g++ libc6-dev \ + clang-tidy \ + && rm -rf /var/lib/apt/lists/* && \ + python3 -m pip install --no-cache-dir --upgrade pip && \ + python3 -m pip install --no-cache-dir pre-commit + +# Build uncrustify 0.64 +WORKDIR /tmp +RUN git clone -b uncrustify-0.64 --single-branch https://github.com/uncrustify/uncrustify.git uncrustify && \ + cd uncrustify && \ + mkdir build && cd build && \ + cmake -D CMAKE_INSTALL_PREFIX=/usr/local -D CMAKE_BUILD_TYPE=RelWithDebInfo ../ && \ + make -j "$(nproc)" && \ + make install && \ + cd /tmp && rm -rf uncrustify + +# Set up Action resources +WORKDIR /action +COPY .pre-commit-config.yaml /action/ +COPY tools /action/tools +COPY entrypoint.sh /entrypoint.sh + +RUN chmod +x /entrypoint.sh + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/README.md b/README.md index 382cf2b..705cb09 100644 --- a/README.md +++ b/README.md @@ -1,133 +1,78 @@ -# Coding Convention Tool -This is a code formatter tool that helps ensure consistent coding style and detects common issues in source code files. It integrates with [pre-commit](https://pre-commit.com/), allowing you to automate code formatting and checks as part of your development workflow. +# Code Convention Tool -## Features - -- Automatically fixes end-of-file issues. -- Removes trailing whitespace from lines. -- Identifies and suggests fixes for common spelling errors using [codespell](https://github.com/codespell-project/codespell). -- Formats code according to specified [Uncrustify](https://github.com/uncrustify/uncrustify) rules base on [Silabs's coding standard](https://github.com/SiliconLabsSoftware/agreements-and-guidelines/blob/main/coding_standard.md) -- Checks for identifiers naming style mismatch of function, definition, struct, variable using [clang-tidy](https://releases.llvm.org/14.0.0/tools/clang/tools/extra/docs/clang-tidy/index.htmlextensible) - -## Project Structure - -* **tools**: - * **.codespell** - * **exclude-file.txt**: Contains lines to be excluded from Codespell checks. - * **ignore-words.txt**: Contains words to be ignored by Codespell to avoid false positives. - * **uncrustify.cfg**: Configuration file for Uncrustify, specifying the coding style and formatting rules. - * **.clang-tidy**: Configuration file for clang-tidy, specifying checks and options for static analysis. - -* **.pre-commit-config.yaml**: Configuration file for pre-commit, defining the hooks and their settings to be run before commits. -## Installation -### Ubuntu -Recommended operating system: WSL, Ubuntu 22.04. - -Ensure Python3 is installed on your system. Then, install pre-commit clang-format clang-tidy cppcheck by running: -``` -$ pip install pre-commit -$ sudo apt install uncrustify clang-tidy cppcheck -``` - -Recommended version: -- Codespell 2.2.4 -- Uncrustify 0.64.0 // Silabs specific uncrustify.cfg support only this version -- Clang tidy 14.0.0 +Docker-based code formatting and static analysis. Identical checks in CI and locally. -### Windows +## Features +- **Uncrustify v0.64** - Formatting ([Silabs Style](https://github.com/SiliconLabsSoftware/agreements-and-guidelines/blob/main/coding_standard.md)) +- **Clang-Tidy** - Naming conventions & common errors +- **Codespell** - Spell checking +- **Patch** - Generates patch for automated corrections -TBD +## How It Works +This composite action uses Docker to isolate tool dependencies from your repository: -### Exclude Folder +1. **Your workflow** checks out your code to `${{ github.workspace }}` +2. **Action downloads** config files automatically from this repo +3. **Docker image built** with pre-commit, uncrustify, clang-tidy, codespell +4. **Container mounts** your repo as `/src` (read-write volume) +5. **Pre-commit runs** checks against your code, modifying files in-place +6. **Results saved** to your workspace: `CodingConventionTool.txt`, `code-fix.patch` -When using this tool, you may want to skip some folders. You can specify folders to exclude from formatting and checks by replacing the exclude regex pattern with the folders you want to skip. +**Security for Internal Repos:** +- Use `runs-on: [self-hosted, ...]` to keep proprietary code on-premises +- All processing is local; no code transmitted externally +- Only action config files (public tools) are downloaded -Here's how you can exclude folders ```build``` and ```gecko_sdk_xxx```using regex patterns in [.pre-commit-config.yaml](./.pre-commit-config.yaml) file: +## Usage +### GitHub Actions ```yaml -exclude: .*/build/.*|.*/gecko_sdk.*/.* +- name: Checkout + uses: actions/checkout@v4 + +- name: Code Convention Check + uses: SiliconLabsSoftware/devs-coding-convention-tool@main + with: + custom-ignore-words: "" # Optional + custom-exclude-file: "" # Optional + custom-pre-commit-config: "" # Optional ``` -### Exclude File, Ignore Words for Codespell +**Outputs:** `CodingConventionTool.txt` (report), `code-fix.patch` (diff) -When using Codespell, you may encounter false positives or want to exclude specific files or directories from being checked. Codespell provides configuration options to handle these scenarios. +### Local Docker -Here's an example of a codespell configuration file [.codespellrc](tools/.codespell/.codespellrc) +**Quick Start** (from your repo root): +```bash +# Build image +git clone https://github.com/SiliconLabsSoftware/devs-coding-convention-tool.git /tmp/convention-tool +docker build -t convention-tool /tmp/convention-tool +# Run checks +docker run --rm -v "$(pwd):/src" convention-tool ``` -ignore-words = ./tools/.codespell/ignore-words.txt -exclude-file = ./tools/.codespell/exclude-file.txt -check-filenames = -check-hidden = -count = -skip = .git,*.a, -``` - -Explanation of Configuration Options - -* **[ignore-words](tools/.codespell/ignore-words.txt)** : This option points to a file containing words that should be ignored by Codespell. If you encounter a false positive, add the problematic word to ignore-words.txt. Ensure each word is in lowercase and on a new line. -Example ignore-words.txt: - -``` -the -foobar +**With Custom Config:** +```bash +docker run --rm \ + -v "$(pwd):/src" \ + -e CUSTOM_IGNORE_WORDS=".github/formatting_config/ignore-words.txt" \ + -e CUSTOM_EXCLUDE_FILE=".github/formatting_config/exclude-file.txt" \ + -e CUSTOM_PRE_COMMIT_CONFIG=".github/formatting_config/.pre-commit-config.yaml" \ + convention-tool ``` -* **[exclude-file](tools/.codespell/exclude-file.txt)** : This option points to a file containing lines that should be excluded from spell-checking. If a specific line in your code is causing a false positive, copy and paste the entire line into exclude-file.txt. -Example exclude-file.txt: -``` -This is a sample line that should be excluded. -``` -* **check-filenames**: Set this option to true if you want Codespell to check filenames for spelling errors. By default, this is empty (disabled). +Files modified in-place. Review: `git diff` -* **check-hidden**: Set this option to true if you want Codespell to check hidden files for spelling errors. By default, this is empty (disabled). +## Configuration -* **count**: Set this option to true if you want Codespell to display the number of occurrences of each misspelled word. By default, this is empty (disabled). - -* **skip**: This option allows you to specify files or directories that Codespell should skip. You can list multiple entries separated by commas. For example, .git,*.a, will skip the .git directory and all files with the .a extension. - -### Uncrustify - -Automatic source code formatting follows [Silabs's coding standard](https://github.com/SiliconLabsSoftware/agreements-and-guidelines/blob/main/coding_standard.md) -The Uncrustify configuration file is located at [/tools/uncrustify/uncrustify.cfg](./tools/uncrustify/uncrustify.cfg). - - -### Clang-Tidy - -**Checks for identifiers naming style mismatch** - -Clang-Tidy supports checking casing types (UPPER_CASE/lower_case) and prefixes of Function, Struct, Enum, Global Constant. -You can modify the prefix in the file [.clang-tidy](./tools/.clang-tidy). - -For example, config function with lower_case and prefix 'sl_' -``` - - key: readability-identifier-naming.FunctionCase - value: lower_case - - key: readability-identifier-naming.FunctionPrefix - value: 'sl_' -``` - -**Adding Extra Arguments** - -You may need to provide additional paths to header files or define macros for Clang-Tidy to ensure it correctly analyzes your code. This can be done using the ```--extra-arg``` option in the hook configuration. - -To add extra arguments to the clang-tidy hook, modify the args section of the hook configuration in your .pre-commit-config.yaml file. Here's an example of how to add extra arguments: - -```yaml -- repo: https://github.com/pocc/pre-commit-hooks - rev: v1.3.5 - hooks: - - id: clang-tidy - args: - - --config-file=./tools/.clang-tidy - - --use-color - - --extra-arg=-I/inc/ - - --extra-arg=-DMY_MACRO=1 -``` -In this example, we've added two extra arguments to Clang-Tidy: +Default rules embedded at build time: -```-I/inc/```: Specifies an include directory where Clang-Tidy will look for header files. +| Tool | Config Path | Customization | +| -------------- | --------------------------------- | ------------------------------------------ | +| **Uncrustify** | `tools/uncrustify/uncrustify.cfg` | Override via `custom-pre-commit-config` | +| **Clang-Tidy** | `tools/.clang-tidy` | Override via `custom-pre-commit-config` | +| **Codespell** | `tools/.codespell/` | Override via `custom-ignore-words/exclude` | -```-DMY_MACRO=1```: Defines a macro named MY_MACRO with the value 1 for use in the code analysis. +Use `custom-pre-commit-config` to provide your own `.pre-commit-config.yaml` for full control. diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..c61a256 --- /dev/null +++ b/action.yml @@ -0,0 +1,65 @@ +name: "Code Convention Check" +description: "Check code conventions using Docker container" + +inputs: + custom-exclude-file: + description: "Path to custom exclude file (relative to repo root)" + required: false + default: "" + + custom-ignore-words: + description: "Path to custom ignore words file (relative to repo root)" + required: false + default: "" + + custom-pre-commit-config: + description: "Path to custom pre-commit config file (relative to repo root)" + required: false + default: "" + + runner-type: + description: "Runner type (deprecated, now runs in Docker)" + required: false + default: "auto" + +runs: + using: "composite" + steps: + - name: Build Code Convention Docker Image + shell: bash + run: | + docker build -t coding-convention-tool:${{ github.sha }} ${{ github.action_path }} + + - name: Run Code Convention Checks + shell: bash + run: | + docker run --rm \ + -v "${{ github.workspace }}:/src" \ + -e CUSTOM_EXCLUDE_FILE="${{ inputs.custom-exclude-file }}" \ + -e CUSTOM_IGNORE_WORDS="${{ inputs.custom-ignore-words }}" \ + -e CUSTOM_PRE_COMMIT_CONFIG="${{ inputs.custom-pre-commit-config }}" \ + coding-convention-tool:${{ github.sha }} + + - name: Upload Results + uses: actions/upload-artifact@v4 + if: always() + with: + name: CodingConventionResult + path: ${{ github.workspace }}/CodingConventionTool.txt + retention-days: 3 + + - name: Upload Patch + uses: actions/upload-artifact@v4 + if: always() + with: + name: code-fix.patch + path: ${{ github.workspace }}/code-fix.patch + retention-days: 3 + + - name: Check Results + shell: bash + run: | + if grep -iq "Failed" ${{ github.workspace }}/CodingConventionTool.txt; then + echo "Code convention check failed" + exit 1 + fi diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..ffae08d --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,65 @@ +#!/bin/bash +set -e + +SW_REPO_DIR="/src" +ACTION_REPO_DIR="/action" + +# Set cache directories for pre-commit (non-root user needs writable location) +export HOME="$SW_REPO_DIR" +export XDG_CACHE_HOME="$SW_REPO_DIR/.cache" + +cd "$SW_REPO_DIR" || exit 1 + +git config --system --add safe.directory "$SW_REPO_DIR" 2>/dev/null || \ +git config --add safe.directory "$SW_REPO_DIR" + +echo "=== Configuration Status ===" +echo "CUSTOM_EXCLUDE_FILE: ${CUSTOM_EXCLUDE_FILE:-}" +echo "CUSTOM_IGNORE_WORDS: ${CUSTOM_IGNORE_WORDS:-}" +echo "CUSTOM_PRE_COMMIT_CONFIG: ${CUSTOM_PRE_COMMIT_CONFIG:-}" +echo "===========================" + +# Handle custom configuration + +if [ -n "$CUSTOM_PRE_COMMIT_CONFIG" ]; then + if [ -f "$SW_REPO_DIR/$CUSTOM_PRE_COMMIT_CONFIG" ]; then + echo "Applying custom pre-commit config: $CUSTOM_PRE_COMMIT_CONFIG" + cp "$SW_REPO_DIR/$CUSTOM_PRE_COMMIT_CONFIG" "$ACTION_REPO_DIR/.pre-commit-config.yaml" + else + echo "Warning: Custom pre-commit config not found: $CUSTOM_PRE_COMMIT_CONFIG" + fi +fi + +if [ -n "$CUSTOM_EXCLUDE_FILE" ]; then + if [ -f "$SW_REPO_DIR/$CUSTOM_EXCLUDE_FILE" ]; then + echo "Applying custom exclude file: $CUSTOM_EXCLUDE_FILE" + cp "$SW_REPO_DIR/$CUSTOM_EXCLUDE_FILE" "$ACTION_REPO_DIR/tools/.codespell/exclude-file.txt" + else + echo "Warning: Custom exclude file not found: $CUSTOM_EXCLUDE_FILE" + fi +fi + +if [ -n "$CUSTOM_IGNORE_WORDS" ]; then + if [ -f "$SW_REPO_DIR/$CUSTOM_IGNORE_WORDS" ]; then + echo "Applying custom ignore words: $CUSTOM_IGNORE_WORDS" + cp "$SW_REPO_DIR/$CUSTOM_IGNORE_WORDS" "$ACTION_REPO_DIR/tools/.codespell/ignore-words.txt" + else + echo "Warning: Custom ignore words file not found: $CUSTOM_IGNORE_WORDS" + fi +fi + +echo "Installing pre-commit hooks..." +pre-commit install-hooks --config /action/.pre-commit-config.yaml + +echo "Running pre-commit..." +set +e +pre-commit run --config /action/.pre-commit-config.yaml --all-files 2>&1 | tee CodingConventionTool.txt +PC_EXIT=${PIPESTATUS[0]} +set -e + +git diff > code-fix.patch || echo "No changes to patch." + +if [ "$PC_EXIT" -ne 0 ]; then + echo "Pre-commit failed with exit code $PC_EXIT" + exit "$PC_EXIT" +fi diff --git a/project_custom_config_handler.sh b/project_custom_config_handler.sh deleted file mode 100644 index 464ff82..0000000 --- a/project_custom_config_handler.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash -echo "This script checks for custom codespell and pre-commit configurations." - -# Get the directory of the script -SCRIPT_DIR=$(dirname "$(realpath "${BASH_SOURCE[0]}")") - -# Check for ../formatting_config/pre-commit-config.yaml -if [ -f "$SCRIPT_DIR/../formatting_config/pre-commit-config.yaml" ]; then - echo "Custom pre-commit configuration found." - cp "$SCRIPT_DIR/../formatting_config/pre-commit-config.yaml" "$SCRIPT_DIR/.pre-commit-config.yaml" -else - echo "No custom pre-commit configuration found." -fi - -# Check for ../formatting_config/exclude-file.txt -if [ -f "$SCRIPT_DIR/../formatting_config/exclude-file.txt" ]; then - echo "Custom codespell exclude file found." - cp "$SCRIPT_DIR/../formatting_config/exclude-file.txt" "$SCRIPT_DIR/tools/.codespell/exclude-file.txt" -else - echo "No custom codespell exclude file found." -fi - -# Check for ../formatting_config/ignore-words.txt -if [ -f "$SCRIPT_DIR/../formatting_config/ignore-words.txt" ]; then - echo "Custom codespell ignore words file found." - cp "$SCRIPT_DIR/../formatting_config/ignore-words.txt" "$SCRIPT_DIR/tools/.codespell/ignore-words.txt" -else - echo "No custom codespell ignore words file found." -fi - -echo "Custom configuration check completed." diff --git a/tools/.codespell/.codespellrc b/tools/.codespell/.codespellrc index 0a75042..d4f54f2 100644 --- a/tools/.codespell/.codespellrc +++ b/tools/.codespell/.codespellrc @@ -2,8 +2,8 @@ [codespell] # In the event of a false positive, add the problematic word, in all lowercase, to 'ignore-words.txt' (one word per line). # Or copy & paste the whole problematic line to 'exclude-file.txt' -ignore-words = ./.github/coding-convention-tool/tools/.codespell/ignore-words.txt -exclude-file = ./.github/coding-convention-tool/tools/.codespell/exclude-file.txt +ignore-words = /action/tools/.codespell/ignore-words.txt +exclude-file = /action/tools/.codespell/exclude-file.txt check-filenames = check-hidden = count =