diff --git a/.gitattributes b/.gitattributes index e1a14d22..f7c5d6a8 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6,3 +6,5 @@ *.py text eol=lf *.rst text eol=lf *.sh text eol=lf +*.cpp text eol=lf +*.hpp text eol=lf diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 00000000..0d0b1c99 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1 @@ +_extends: .github diff --git a/.github/workflows/test.yml b/.github/workflows/cpp-linter.yml similarity index 69% rename from .github/workflows/test.yml rename to .github/workflows/cpp-linter.yml index f0dc8922..83341576 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/cpp-linter.yml @@ -1,16 +1,17 @@ -name: Test action +name: cpp-linter on: push: + paths-ignore: "docs/**" pull_request: - types: [opened] + paths-ignore: "docs/**" jobs: - test: + cpp-linter: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: shenxianpeng/cpp-linter-action@master + - uses: actions/checkout@v3 + - uses: cpp-linter/cpp-linter-action@master id: linter env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/mkdocs-deploy.yml b/.github/workflows/mkdocs-deploy.yml new file mode 100644 index 00000000..801a6945 --- /dev/null +++ b/.github/workflows/mkdocs-deploy.yml @@ -0,0 +1,25 @@ +name: MkDocs Deploy + +on: + push: + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: 3.x + - name: Install python action for doc extraction + run: pip install -r docs/requirements.txt + - name: check mkdocs build + # if: github.ref != 'refs/heads/master' + run: mkdocs build + # - name: Build docs and deploy to gh-pages + # if: github.ref == 'refs/heads/master' + # run: | + # git config user.name 'github-actions' + # git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + # mkdocs gh-deploy --force diff --git a/.github/workflows/pre-commit-hooks.yml b/.github/workflows/pre-commit-hooks.yml new file mode 100644 index 00000000..e9426fc7 --- /dev/null +++ b/.github/workflows/pre-commit-hooks.yml @@ -0,0 +1,17 @@ +name: Pre-commit + +on: + push: + pull_request: + types: opened + +jobs: + check-source-files: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: '3.x' + - run: python3 -m pip install pre-commit + - run: pre-commit run --all-files diff --git a/.github/workflows/run-test.yml b/.github/workflows/run-test.yml new file mode 100644 index 00000000..a2f7ff7a --- /dev/null +++ b/.github/workflows/run-test.yml @@ -0,0 +1,59 @@ +name: "Test action and package" + +on: + push: + branches: master + paths-ignore: "docs/**" + pull_request: + branches: master + paths-ignore: "docs/**" + +jobs: + add-tag: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: retag latest commit for testing + run: | + git config user.name 'github-actions' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + git tag --delete latest || true + git push --delete origin latest || true + git tag -a latest -m 'Retag latest commit' + git push origin latest + + test-action: + needs: [add-tag] + runs-on: ubuntu-latest + steps: + - uses: convictional/trigger-workflow-and-wait@v1.6.3 + with: + owner: cpp-linter + repo: test-cpp-linter-action + github_token: ${{ secrets.PAT_TOKEN }} + workflow_file_name: cpp-lint-action.yml + ref: master + wait_interval: 10 + client_payload: '{}' + propagate_failure: true + trigger_workflow: true + wait_workflow: true + test-package: + needs: [add-tag] + runs-on: ubuntu-latest + steps: + - uses: convictional/trigger-workflow-and-wait@v1.6.3 + with: + owner: cpp-linter + repo: test-cpp-linter-action + github_token: ${{ secrets.PAT_TOKEN }} + workflow_file_name: cpp-lint-package.yml + ref: master + wait_interval: 10 + client_payload: '{}' + propagate_failure: true + trigger_workflow: true + wait_workflow: true diff --git a/.gitignore b/.gitignore index cb8d0354..e4f88937 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,184 @@ -.cpp_linter_action_changed_files.json -clang_format_report.txt -clang_tidy_report.txt +# local demo specific +.changed_files.json +clang_format*.txt +clang_tidy*.txt +act.exe +clang_tidy_output.yml +clang_format_output.xml +event_payload.json +comments.json + +#### ignores for Python +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +tests/*/test*.json +tests/**/*.c + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# exclude local VSCode's settings folder +.vscode/ + +#### ignores for C++ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# Cmake build-in-source generated stuff +CMakeUserPresets.json +CMakeCache.txt +CPackConfig.cmake +CPackSourceConfig.cmake +CMakeFiles +cmake_install.cmake diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..cc14a830 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,8 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-added-large-files + - id: check-yaml diff --git a/Dockerfile b/Dockerfile index 48cab231..427dfabe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,16 +9,14 @@ LABEL com.github.actions.description="Lint your code with clang-tidy in parallel LABEL com.github.actions.icon="code" LABEL com.github.actions.color="gray-dark" -LABEL repository="https://github.com/shenxianpeng/cpp-linter-action" +LABEL repository="https://github.com/cpp-linter/cpp-linter-action" LABEL maintainer="shenxianpeng <20297606+shenxianpeng@users.noreply.github.com>" -RUN apt-get update -RUN apt-get -y install curl jq +RUN apt-get update && apt-get -y install python3-pip -COPY runchecks.sh /entrypoint.sh -RUN chmod +x /entrypoint.sh +RUN python3 -m pip install cpp-linter # github action args use the CMD option # See https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#runsargs # also https://docs.docker.com/engine/reference/builder/#cmd -ENTRYPOINT [ "/entrypoint.sh" ] +ENTRYPOINT [ "python3", "-m", "cpp_linter.run" ] diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..b049769e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 shenxianpeng + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 0a7c0dbf..33d9fa09 100644 --- a/README.md +++ b/README.md @@ -1,57 +1,268 @@ -# C/C++ Lint Action + -Github Actions for linting the C/C++ code. Integrated clang-tidy, clang-format checks. +# C/C++ Lint Action | clang-format & clang-tidy -## Integration with GitHub Actions +![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/cpp-linter/cpp-linter-action?style=flat-square) +[![GitHub marketplace](https://img.shields.io/badge/marketplace-C%2FC%2B%2B%20Lint%20Action-blue?logo=github&style=flat-square)](https://github.com/marketplace/actions/c-c-lint-action) +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/cpp-linter/cpp-linter-action/cpp-linter?label=cpp-linter&logo=Github&style=flat-square)](https://github.com/cpp-linter/cpp-linter-action/actions/workflows/cpp-linter.yml) +[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/cpp-linter/cpp-linter-action/MkDocs%20Deploy?label=docs&logo=Github&style=flat-square)](https://github.com/cpp-linter/cpp-linter-action/actions/workflows/mkdocs-deploy.yml) +![GitHub](https://img.shields.io/github/license/cpp-linter/cpp-linter-action?label=license&logo=github&style=flat-square) -Just create a `yml` file under your GitHub repository. For example `.github/workflows/cpp-linter.yml` +A Github Action for linting C/C++ code integrating clang-tidy and clang-format to collect feedback provided in the form of thread comments and/or annotations. -!!! Requires `secrets.GITHUB_TOKEN` set to an environment variable named `GITHUB_TOKEN`. +## Usage -```yml +Create a new GitHub Actions workflow in your project, e.g. at [.github/workflows/cpp-linter.yml](https://github.com/cpp-linter/cpp-linter-action/blob/master/.github/workflows/cpp-linter.yml) + +The content of the file should be in the following format. + +```yaml +# Workflow syntax: +# https://help.github.com/en/articles/workflow-syntax-for-github-actions name: cpp-linter -# Triggers the workflow on push or pull request events on: - push: pull_request: - types: [opened] + types: [opened, reopened] # let PR-synchronize events be handled by push events + push: + jobs: cpp-linter: - name: cpp-linter runs-on: ubuntu-latest steps: - - name: C/C++ Lint Action + - uses: actions/checkout@v3 + - uses: cpp-linter/cpp-linter-action@v1 + id: linter env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - uses: shenxianpeng/cpp-linter-action@master + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - style: 'file' + style: file + + - name: Fail fast?! + if: steps.linter.outputs.checks-failed > 0 + run: echo "Some files failed the linting checks!" + # for actual deployment + # run: exit 1 ``` -## Optional Inputs -| Input name | default value | Description | -|------------|---------------|-------------| -| style | 'llvm' | The style rules to use. Set this to 'file' to have clang-format use the closest relative .clang-format file. | -| extensions | 'c,h,C,H,cpp,hpp,cc,hh,c++,h++,cxx,hxx' | The file extensions to run the action against. This is a comma-separated string. | -| tidy-checks | 'boost-\*,bugprone-\*,performance-\*,
readability-\*,portability-\*,
modernize-\*,clang-analyzer-\*,
cppcoreguidelines-\*' | A string of regex-like patterns specifying what checks clang-tidy will use.| -| repo-root | '.' | The relative path to the repository root directory. This path is relative to path designated by the runner's GITHUB_WORKSPACE environment variable. | -| version | '10' | The desired version of the clang tools to use. Accepted options are strings which can be 6.0, 7, 8, 9, 10, 11, 12. | +### Optional Inputs + +#### `style` + +- **Description**: The style rules to use. + - Set this to 'file' to have clang-format use the closest relative .clang-format file. + - Set this to a blank string (`''`) to disable the use of clang-format entirely. +- Default: 'llvm' + +#### `extensions` + +- **Description**: The file extensions to run the action against. This is a comma-separated string. +- Default: 'c,h,C,H,cpp,hpp,cc,hh,c++,h++,cxx,hxx' + +#### `tidy-checks` + +- **Description**: Comma-separated list of globs with optional `-` prefix. Globs are processed in order of appearance in the list. Globs without `-` prefix add checks with matching names to the set, globs with the `-` prefix remove checks with matching names from the set of enabled checks. This option's value is appended to the value of the 'Checks' option in a .clang-tidy file (if any). + - It is possible to disable clang-tidy entirely by setting this option to `'-*'`. + - It is also possible to rely solely on a .clang-tidy config file by specifying this option as a blank string (`''`). +- Default: 'boost-\*,bugprone-\*,performance-\*,readability-\*,portability-\*,modernize-\*,clang-analyzer-\*,cppcoreguidelines-\*' + +#### `repo-root` + +- **Description**: The relative path to the repository root directory. This path is relative to the path designated as the runner's GITHUB_WORKSPACE environment variable. +- Default: '.' + +#### `version` + +- **Description**: The desired version of the [clang-tools](https://hub.docker.com/r/xianpengshen/clang-tools) to use. Accepted options are strings which can be 14, 13, 12, 11, 10, 9, or 8. + - Set this option to a blank string (`''`) to use the platform's default installed version. + - This value can also be a path to where the clang tools are installed (if using a custom install location). Because all paths specified here are converted to absolute, using a relative path as a value may not be compatible when using the docker environment (see [Running without the docker container](#running-without-the-docker-container)). +- Default: '12' + +#### `verbosity` + +- **Description**: This controls the action's verbosity in the workflow's logs. Supported options are defined by the [python logging library's log levels](https://docs.python.org/3/library/logging.html#logging-levels). This option does not affect the verbosity of resulting thread comments or file annotations. +- Default: '10' + +#### `lines-changed-only` + +- **Description**: This controls what part of the files are analyzed. The following values are accepted: + - false: All lines in a file are analyzed. + - true: Only lines in the diff that contain additions are analyzed. + - diff: All lines in the diff are analyzed (including unchanged lines but not subtractions). +- Default: false. + +#### `files-changed-only` + +- **Description**: Set this option to false to analyze any source files in the repo. This is automatically enabled if lines-changed-only is enabled. +- Default: true +- NOTE: The `GITHUB_TOKEN` should be supplied when running on a private repository with this option enabled, otherwise the runner does not not have the privilege to list changed files for an event. See [Authenticating with the GITHUB_TOKEN](https://docs.github.com/en/actions/reference/authentication-in-a-workflow) + +#### `ignore` + +- **Description**: Set this option with string of path(s) to ignore. + - In the case of multiple paths, you can use a pipe character (`|`) + to separate the multiple paths. Multiple lines are forbidden as an input to this option; it must be a single string. + - This can also have files, but the file's relative path has to be specified + as well. + - There is no need to use `./` for each entry; a blank string (`''`) represents + the repo-root path (specified by the `repo-root` input option). + - Submodules are automatically ignored. Hidden directories (beginning with a `.`) are also ignored automatically. + - Prefix a path with a bang (`!`) to make it explicitly _not_ ignored. The order of + multiple paths does _not_ take precedence. The `!` prefix can be applied to + a submodule's path (if desired) but not hidden directories. + - Glob patterns are not supported here. All asterisk characters (`*`) are literal. +- Default: '.github' + +#### `thread-comments` + +- **Description**: Set this option to false to disable the use of thread comments as feedback. + - To use thread comments, the `GITHUB_TOKEN` (provided by Github to each repository) must be declared as an environment + variable. See [Authenticating with the GITHUB_TOKEN](https://docs.github.com/en/actions/reference/authentication-in-a-workflow) +- Default: false +- NOTE: If run on a private repository, then this feature is disabled because the GitHub REST API behaves differently for thread comments on a private repository. + +#### `file-annotations` + +- **Description**: Set this option to false to disable the use of file annotations as feedback. +- Default: true + +#### `database` + +- **Description**: The directory containing compilation database (like compile_commands.json) file. + - This option doesn't seems to work properly from the docker environment. Instead we recommend using this option when see [running without the docker container](#running-without-the-docker-container). +- Default: '' ### Outputs This action creates 1 output variable named `checks-failed`. Even if the linting checks fail for source files this action will still pass, but users' CI workflows can use this action's output to exit the workflow early if that is desired. -## Results of GitHub Actions +## Running without the docker container + +Some Continuous Integration environments require access to non-default compilers +and/or non-standard libraries. To do this properly, the docker container should +not be used due to it's isolated file system. Instead, you should use this action's +python source code as an installed python package (see below). + +### Using the python source code + +This action was originally designed to only be used on a runner with the Ubuntu +Operating System. However, this action's source code (essentially a python package) +can be used on any runner using the Windows, Ubuntu, or possibly even MacOS (untested) +virtual environments. + +Note, some runners already ship with clang-format and/or clang-tidy. As of this writing, the following versions of clang-format and clang-tidy are already available: + +- `ubuntu-latest` ships with v10, v11, and v12. [More details](https://github.com/actions/virtual-environments/blob/ubuntu20/20220508.1/images/linux/Ubuntu2004-Readme.md). +- `windows-latest` ships with v13. [More details](https://github.com/actions/virtual-environments/blob/win22/20220511.2/images/win/Windows2022-Readme.md). +- `macos-latest` ships with v13. [More details](https://github.com/actions/virtual-environments/blob/main/images/macos/macos-11-Readme.md). + +This example makes use of another action +([KyleMayes/install-llvm-action](https://github.com/KyleMayes/install-llvm-action)) +to install a certain version of clang-tidy and clang-format. + +```yml +on: + pull_request: + types: [opened, reopened] # let PR-synchronize events be handled by push events + push: + +jobs: + cpp-linter: + runs-on: windows-latest + + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + + # this step can be skipped if the desired + # version already comes with the runner's OS + - name: Install clang-tools + uses: KyleMayes/install-llvm-action@v1 + with: + # v13 is the recommended minimum for the Visual Studio compiler (on Windows) + version: 14 + # specifying an install path is required (on Windows) because installing + # multiple versions on Windows runners needs non-default install paths. + directory: ${{ runner.temp }}/llvm + + - name: Install linter python package + run: python3 -m pip install cpp-linter + + - name: run linter as a python package + id: linter + # Pass the installed path to the '--version' argument. + # Alternatively, pass the version number. + # Example. run: cpp-linter --version=14 + # Omit the version option if using the default version available in the OS. + run: cpp-linter --version=${{ runner.temp }}/llvm + + - name: Fail fast?! + if: steps.linter.outputs.checks-failed > 0 + run: echo "Some files failed the linting checks!" + # for actual deployment + # run: exit 1 +``` + +All input options listed above are specified by pre-pending a `--`. You can also install this repo locally and run `cpp-linter -h` for more detail. For example: + +```yaml + - uses: cpp-linter/cpp-linter-action@v1 + with: + style: file + tidy-checks: '-*' + files-changed-only: false + ignore: 'dist/third-party-lib' +``` + +is equivalent to + +```yaml + - name: Install linter python package + run: python3 -m pip install cpp-linter + + - name: run linter as a python package + run: | + cpp-linter \ + --style=file \ + --tidy-checks='-*' \ + --files-changed-only=false \ + --ignore='dist/third-party-lib' +``` + +## Example + + + +### Annotations + +![clang-format annotations](https://raw.githubusercontent.com/cpp-linter/cpp-linter-action/master/docs/images/annotations-clang-format.png) + +![clang-tidy annotations](https://raw.githubusercontent.com/cpp-linter/cpp-linter-action/master/docs/images/annotations-clang-tidy.png) + +### Thread Comment + +![sample comment](https://raw.githubusercontent.com/cpp-linter/cpp-linter-action/master/docs/images/comment.png) + + + +## Add C/C++ Lint Action badge in README + +You can show C/C++ Lint Action status with a badge in your repository README + +Example + +```markdown +[![cpp-linter](https://github.com/cpp-linter/cpp-linter-action/actions/workflows/cpp-linter.yml/badge.svg)](https://github.com/cpp-linter/cpp-linter-action/actions/workflows/cpp-linter.yml) +``` -![github-actions bot](./demo/result.png) +[![cpp-linter](https://github.com/cpp-linter/cpp-linter-action/actions/workflows/cpp-linter.yml/badge.svg)](https://github.com/cpp-linter/cpp-linter-action/actions/workflows/cpp-linter.yml) -Behind the scenes, this is because this repository has added `test.yml` under `.github/workflows/`. When an unformatted C/C++ source file was committed and create a Pull Request will automatically recognize and add warning comments. +## Have question or feedback? -Please feel free to commit code to the `test` branch of this repository or create a Pull Request to see how the process works. +To provide feedback (requesting a feature or reporting a bug) please post to [issues](https://github.com/cpp-linter/cpp-linter-action/issues). -For example, this test PR [#6](https://github.com/shenxianpeng/cpp-linter-action/pull/5) and github-actions bot comments [link](https://github.com/shenxianpeng/cpp-linter-action/pull/5#commitcomment-55252014). +## License -## Contribution +The scripts and documentation in this project are released under the [MIT License](https://github.com/cpp-linter/cpp-linter-action/blob/master/LICENSE) -If you have any suggestions or contributions, welcome to PR [here](https://github.com/shenxianpeng/cpp-linter-action). + diff --git a/action.yml b/action.yml index e4527c8f..62673c57 100644 --- a/action.yml +++ b/action.yml @@ -1,16 +1,24 @@ name: C/C++ Lint Action -description: Automatically checks push & pull request changes with clang-format & clang-tidy, then posts a comment with faulty results. +description: Automatically checks push & PR changes with clang-format & clang-tidy, then post annotations/comments with faulty results. author: shenxianpeng branding: - icon: 'check-circle' - color: 'green' + icon: "check-circle" + color: "green" inputs: + thread-comments: + description: Set this option to false to disable the use of thread comments as feedback. Defaults to false. + required: false + default: false + file-annotations: + description: Set this option to false to disable the use of file annotations as feedback. Defaults to true. + required: false + default: true style: description: > The style rules to use (defaults to 'llvm'). Set this to 'file' to have clang-format use the closest relative .clang-format file. required: false - default: 'llvm' + default: "llvm" extensions: description: > The file extensions to run the action against. @@ -22,25 +30,68 @@ inputs: A string of regex-like patterns specifying what checks clang-tidy will use. This defaults to 'boost-*,bugprone-*,performance-*,readability-*,portability-*,modernize-*,clang-analyzer-*,cppcoreguidelines-*'. See also clang-tidy docs for more info. required: false - default: 'boost-*,bugprone-*,performance-*,readability-*,portability-*,modernize-*,clang-analyzer-*,cppcoreguidelines-*' + default: "boost-*,bugprone-*,performance-*,readability-*,portability-*,modernize-*,clang-analyzer-*,cppcoreguidelines-*" repo-root: description: > The relative path to the repository root directory. The default value '.' is relative to the runner's GITHUB_WORKSPACE environment variable. required: false - default: '.' + default: "." version: - description: "The desired version of the clang tools to use. Accepted options are strings which can be 6.0, 7, 8, 9, 10, 11, 12. Defaults to 10." + description: "The desired version of the clang tools to use. Accepted options are strings which can be 14, 13, 12, 11, 10, 9, or 8. Defaults to 12." + required: false + default: "12" + verbosity: + description: A hidden option to control the action's log verbosity. This is the `logging` level (degaults to DEBUG) + required: false + default: "10" + lines-changed-only: + description: Set this option to 'true' to only analyse changes in the event's diff. Defaults to 'false'. + required: false + default: false + files-changed-only: + description: Set this option to 'false' to analyse any source files in the repo. Defaults to 'true'. + required: false + default: true + ignore: + description: > + Set this option with string of path(s) to ignore. + + - In the case of multiple paths, you can use a pipe character ('|') + to separate the multiple paths. Multiple lines are forbidden as input to this option. + - This can also have files, but the file's relative path has to be specified + as well. + - There is no need to use './' for each entry; a blank string ('') represents + the repo-root path (specified by the `repo-root` input option). + - Path(s) containing a space should be inside single quotes. + - Submodules are automatically ignored. + - Prefix a path with a bang (`!`) to make it explicitly not ignored - order of + multiple paths does take precedence. The `!` prefix can be applied to + submodules if desired. + - Glob patterns are not supported here. All asterisk characters ('*') are literal. + required: false + default: ".github" + database: + description: The directory containing compile_commands.json file. required: false - default: '10' + default: "" outputs: checks-failed: description: An integer that can be used as a boolean value to indicate if all checks failed. runs: - using: 'docker' - image: 'Dockerfile' + using: "docker" + image: "Dockerfile" + env: + USING_CLANG_TOOLS_DOCKER: 'true' args: - - ${{ inputs.style }} - - ${{ inputs.extensions }} - - ${{ inputs.tidy-checks }} - - ${{ inputs.repo-root }} - - ${{ inputs.version }} + - --style=${{ inputs.style }} + - --extensions=${{ inputs.extensions }} + - --tidy-checks=${{ inputs.tidy-checks }} + - --repo-root=${{ inputs.repo-root }} + - --version=${{ inputs.version }} + - --verbosity=${{ inputs.verbosity }} + - --lines-changed-only=${{ inputs.lines-changed-only }} + - --files-changed-only=${{ inputs.files-changed-only }} + - --thread-comments=${{ inputs.thread-comments }} + - --ignore=${{ inputs.ignore }} + - --database=${{ inputs.database }} + - --file-annotations=${{ inputs.file-annotations }} diff --git a/demo/compile_commands.json b/demo/compile_commands.json deleted file mode 100644 index d782c455..00000000 --- a/demo/compile_commands.json +++ /dev/null @@ -1,12 +0,0 @@ -[ - { - "directory": ".", - "command": "/usr/bin/g++ -Wall -Werror demo.cpp", - "file": "/demo.cpp" - }, - { - "directory": ".", - "command": "/usr/bin/g++ -Wall -Werror demo.cpp", - "file": "/demo.hpp" - } -] diff --git a/demo/compile_flags.txt b/demo/compile_flags.txt new file mode 100644 index 00000000..03e4446c --- /dev/null +++ b/demo/compile_flags.txt @@ -0,0 +1,2 @@ +-Wall +-Werror diff --git a/demo/demo.cpp b/demo/demo.cpp index 46cfb299..1bf553e0 100644 --- a/demo/demo.cpp +++ b/demo/demo.cpp @@ -13,4 +13,6 @@ int main(){ printf("Hello world!\n"); + + return 0;} diff --git a/demo/demo.hpp b/demo/demo.hpp index 0bf1104a..f93d0122 100644 --- a/demo/demo.hpp +++ b/demo/demo.hpp @@ -8,7 +8,7 @@ class Dummy { Dummy() :numb(0), useless("\0"){} public: - void *not_usefull(char *str){useless = str;} + void *not_useful(char *str){useless = str;} }; @@ -26,14 +26,11 @@ class Dummy { - - - - struct LongDiff { + long diff; }; diff --git a/demo/result.png b/demo/result.png deleted file mode 100644 index cf702cfe..00000000 Binary files a/demo/result.png and /dev/null differ diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..b067466d --- /dev/null +++ b/docs/README.md @@ -0,0 +1,21 @@ +## How to build the docs + +From the root directory of the repository, do the following to steps + +1. Install docs' dependencies + + ``` + pip install -r docs/requirements.txt + ``` + On Linux, you may need to use `pip3` instead. + +2. Build the docs + + ``` + mkdocs build + ``` + + or use the following command to see changes rendered in realtime. + ``` + mkdocs serve + ``` diff --git a/docs/images/annotations-clang-format.png b/docs/images/annotations-clang-format.png new file mode 100644 index 00000000..2175c94b Binary files /dev/null and b/docs/images/annotations-clang-format.png differ diff --git a/docs/images/annotations-clang-tidy.png b/docs/images/annotations-clang-tidy.png new file mode 100644 index 00000000..1ac02e6c Binary files /dev/null and b/docs/images/annotations-clang-tidy.png differ diff --git a/docs/images/comment.png b/docs/images/comment.png new file mode 100644 index 00000000..3bb32536 Binary files /dev/null and b/docs/images/comment.png differ diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..7322557a --- /dev/null +++ b/docs/index.md @@ -0,0 +1,21 @@ +{% + include-markdown "../README.md" + start="" + end="" +%} + +### Annotations + +![clang-format annotations](images/annotations-clang-format.png) + +![clang-tidy annotations](images/annotations-clang-tidy.png) + +### Thread Comment + +![sample comment](images/comment.png) + +{% + include-markdown "../README.md" + start="" + end="" +%} diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 00000000..69de84c1 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,3 @@ +mkdocs +mkdocs-include-markdown-plugin +mkdocs-material diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css new file mode 100644 index 00000000..cd2c4f82 --- /dev/null +++ b/docs/stylesheets/extra.css @@ -0,0 +1,8 @@ +.md-typeset table:not([class]) tbody tr:hover, +tr:hover { + background-color: var(--md-accent-fg-color--transparent); +} + +th { + background-color: var(--md-default-fg-color--lightest); +} diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 00000000..2cfbc97c --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,46 @@ +site_name: C/C++ Linter Action +site_description: "Developer documentation from sources." +site_url: "https://cpp-linter.github.io/cpp-linter-action" +repo_url: "https://github.com/cpp-linter/cpp-linter-action" +edit_uri: "" +repo_name: "cpp-linter/cpp-linter-action" +nav: + - index.md + +theme: + name: material + features: + - navigation.top + palette: + - media: "(prefers-color-scheme: light)" + scheme: default + primary: indigo + accent: light blue + toggle: + icon: material/lightbulb-outline + name: Switch to dark mode + - media: "(prefers-color-scheme: light)" + scheme: slate + primary: indigo + accent: cyan + toggle: + icon: material/lightbulb + name: Switch to light mode + +extra_css: + - stylesheets/extra.css + +plugins: + - search + - include-markdown + +markdown_extensions: + - admonition + - pymdownx.emoji + - pymdownx.superfences + - pymdownx.tasklist + - toc: + permalink: true + - pymdownx.highlight: + linenums_style: pymdownx-inline + - pymdownx.inlinehilite diff --git a/runchecks.sh b/runchecks.sh deleted file mode 100644 index 44eaf625..00000000 --- a/runchecks.sh +++ /dev/null @@ -1,268 +0,0 @@ -#!/bin/bash - -# global varibales -EXIT_CODE="0" -PAYLOAD_TIDY="" -FENCES=$'\n```\n' -OUTPUT="" -URLS="" -PATHNAMES="" -declare -a JSON_INDEX - -# alias CLI args -args=("$@") -FMT_STYLE=${args[0]} -IFS=',' read -r -a FILE_EXT_LIST <<< "${args[1]}" -TIDY_CHECKS="${args[2]}" -cd "${args[3]}" || exit "1" -CLANG_VERSION="${args[4]}" - - -################################################### -# Set the exit code (for expected exit calls). -# Optional parameter overides action-specific logic -################################################### -set_exit_code () { - if [[ $# -gt 0 ]] - then - EXIT_CODE="$1" - else - if [[ "$PAYLOAD_FORMAT" != "" || "$PAYLOAD_TIDY" != "" ]] - then - EXIT_CODE="1" - fi - fi - echo "::set-output name=checks-failed::$EXIT_CODE" -} - -################################################### -# Fetch JSON of event's changed files -################################################### -get_list_of_changed_files() { - echo "GH_EVENT_PATH = $GITHUB_EVENT_PATH" - echo "processing $GITHUB_EVENT_NAME event" - # cat "$GITHUB_EVENT_PATH" | jq '.' - - # Use git REST API payload - if [[ "$GITHUB_EVENT_NAME" == "push" ]] - then - FILES_LINK="$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/commits/$GITHUB_SHA" - elif [[ "$GITHUB_EVENT_NAME" == "pull_request" ]] - then - # FILES_LINK="$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/pulls//files" - # Get PR ID number from the event's JSON located in the runner's GITHUB_EVENT_PATH - FILES_LINK="$(jq -r '.pull_request._links.self.href' "$GITHUB_EVENT_PATH")/files" - fi - - # Download files list (another JSON containing files' names, URLS, statuses, & diffs/patches) - echo "Fetching files list from $FILES_LINK" - curl "$FILES_LINK" > .cpp_linter_action_changed_files.json -} - -################################################### -# extract info from downloaded JSON file -################################################### -extract_changed_files_info() { - # pull_request events have a slightly different JSON format than push events - JSON_FILES=".[" - if [[ "$GITHUB_EVENT_NAME" == "push" ]] - then - JSON_FILES=".files[" - fi - FILES_URLS_STRING=$(jq -r "$JSON_FILES].raw_url" .cpp_linter_action_changed_files.json) - FILES_NAMES_STRING=$(jq -r "$JSON_FILES].filename" .cpp_linter_action_changed_files.json) - - # convert json info to arrays - readarray -t URLS <<<"$FILES_URLS_STRING" - readarray -t PATHNAMES <<<"$FILES_NAMES_STRING" - - # Initialize the `JSON_INDEX` array. This helps us keep track of the - # source files' index in the JSON after calling `filter_out_source_files()` function. - for index in "${!URLS[@]}" - do - # this will only be used when parsing diffs from the JSON - JSON_INDEX[$index]=$index - done -} - -################################################### -# exclude undesired files (specified by the user) -################################################### -filter_out_non_source_files() { - for index in "${!URLS[@]}" - do - is_supported=0 - for i in "${FILE_EXT_LIST[@]}" - do - if [[ ${URLS[index]} == *".$i" ]] - then - is_supported=1 - break - fi - done - - if [ $is_supported == 0 ] - then - unset -v "URLS[index]" - unset -v "PATHNAMES[index]" - unset -v "JSON_INDEX[index]" - fi - done - - # exit early if nothing to do - if [ ${#URLS[@]} == 0 ] - then - set_exit_code "0" - echo "No source files need checking!" - exit $EXIT_CODE - else - echo "File names: ${PATHNAMES[*]}" - fi -} - -################################################### -# Download the files if not present. -# This function assumes that the working directory is the root of the invoking repo. -# Note that all github actions are run in path specified by the environment variable GITHUB_WORKSPACE. -################################################### -verify_files_are_present() { - # URLS, PATHNAMES, & PATCHES are parallel arrays - for index in "${!PATHNAMES[@]}" - do - if [[ ! -f "${PATHNAMES[index]}" ]] - then - echo "Downloading ${URLS[index]}" - curl --location --insecure --remote-name "${URLS[index]}" - fi - done -} - -################################################### -# get the patch info from the JSON. -# required parameter is the index in the JSON_INDEX array -################################################### -get_patch_info() { - # patches are multiline strings. Thus, they need special attention because of the '\n' used within. - # - # a git diff (aka "patch" in the REST API) can have multiple "hunks" for a single file. - # hunks start with `@@ -, +, @@` - # A positive sign indicates the incoming changes, while a negative sign indicates existing code that was changed - # Any changed lines will also have a prefixed `-` or `+`. - - file_status=$(jq -r "$JSON_FILES${JSON_INDEX[$1]}].status" .cpp_linter_action_changed_files.json) - - # we only need the first line stating the line numbers changed (ie "@@ -1,5 +1,5 @@"") - patched_lines=$(jq -r -c "$JSON_FILES${JSON_INDEX[$1]}].patch" .cpp_linter_action_changed_files.json) - patches=$(echo "$patched_lines" | grep -o "@@ \\-[1-9]*,[1-9]* +[1-9]*,[1-9]* @@" | grep -o " +[1-9]*,[1-9]*" | tr -d "\\n" | sed 's; +;;; s;+;;g') - - # if there is no patch field, we need to handle 'renamed' as an edgde case - if [[ "$patches" == "" ]] - then - echo "${PATHNAMES[$1]} was $file_status" - # don't bother checking renamed files with no changes to file's content - patches="0,0" - fi - echo "$patches" -} - -################################################### -# execute clang-tidy/format & assemble a unified OUTPUT -################################################### -capture_clang_tools_output() { - clang-tidy --version - - for index in "${!URLS[@]}" - do - filename=$(basename ${URLS[index]}) - if [[ -f "${PATHNAMES[index]}" ]] - then - filename="${PATHNAMES[index]}" - fi - - true > clang_format_report.txt - true > clang_tidy_report.txt - - echo "Performing checkup on $filename" - # echo "incoming changed lines: $(get_patch_info $index)" - - if [ "$TIDY_CHECKS" == "" ] - then - clang-tidy-"$CLANG_VERSION" "$filename" >> clang_tidy_report.txt - else - clang-tidy-"$CLANG_VERSION" -checks="$TIDY_CHECKS" "$filename" >> clang_tidy_report.txt - fi - clang-format-"$CLANG_VERSION" -style="$FMT_STYLE" --dry-run "$filename" 2> clang_format_report.txt - - if [[ $(wc -l < clang_tidy_report.txt) -gt 0 ]] - then - PAYLOAD_TIDY+=$"### ${PATHNAMES[index]}" - PAYLOAD_TIDY+="$FENCES" - sed -i "s|$GITHUB_WORKSPACE/||g" clang_tidy_report.txt - # cat clang_tidy_report.txt - PAYLOAD_TIDY+=$(cat clang_tidy_report.txt) - PAYLOAD_TIDY+="$FENCES" - fi - - if [[ $(wc -l < clang_format_report.txt) -gt 0 ]] - then - if [ "$OUTPUT" == "" ] - then - OUTPUT=$'## Run `clang-format` on the following files\n' - fi - OUTPUT+="- [ ] ${PATHNAMES[index]}"$'\n' - fi - done - - if [ "$PAYLOAD_TIDY" != "" ]; then - OUTPUT+=$'\n---\n## Output from `clang-tidy`\n' - OUTPUT+="$PAYLOAD_TIDY" - fi - - echo "OUTPUT is:" - echo "$OUTPUT" -} - -################################################### -# POST action's results using REST API -################################################### -post_results() { - # check for access token (ENV VAR needed for git API calls) - if [[ -z "$GITHUB_TOKEN" ]] - then - set_exit_code "1" - echo "The GITHUB_TOKEN is required." - exit "$EXIT_CODE" - fi - - COMMENTS_URL=$(jq -r .pull_request.comments_url "$GITHUB_EVENT_PATH") - if [[ "$GITHUB_EVENT_NAME" == "push" ]] - then - COMMENTS_URL="$FILES_LINK/comments" - fi - - echo "COMMENTS_URL = $COMMENTS_URL" - - PAYLOAD=$(echo '{}' | jq --arg body "$OUTPUT" '.body = $body') - - # creating PR comments is the same API as creating issue. Creating commit comments have more optional parameters (but same required API) - curl -s -S -H "Authorization: token $GITHUB_TOKEN" --header "Content-Type: application/vnd.github.VERSION.text+json" "$COMMENTS_URL" --data "$PAYLOAD" -} - -################################################### -# The main body of this script (all function calls) -################################################### -# for local testing (without docker): -# 1. Set the env var GITHUB_EVENT_NAME to "push" or "pull_request" -# 2. Download and save the event's payload (in JSON) to a file named ".cpp_linter_action_changed_files.json". -# See the FILES_LINK variable in the get_list_of_changed_files() function for the event's payload. -# 3. Comment out the following calls to `get_list_of_changed_files` & `post_results` functions -# 4. Run this script using `./run_checks.sh