Skip to content
Open
Show file tree
Hide file tree
Changes from 9 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
30 changes: 30 additions & 0 deletions .github/workflows/examples/auto-fix.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: cpp-linter (auto-fix)
on:
pull_request:
branches: [main, master, develop]
paths: ['**.c', '**.cpp', '**.h', '**.hpp', '**.cxx', '**.hxx', '**.cc', '**.hh', '**CMakeLists.txt', 'meson.build', '**.cmake']

jobs:
cpp-linter:
runs-on: ubuntu-latest
permissions:
contents: write # needed for auto-fix commits
pull-requests: write
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
steps:
- uses: actions/checkout@v7
# For auto-fix commits to trigger new CI runs, use a PAT instead:
# with:
# token: ${{ secrets.MY_PAT }}

- uses: cpp-linter/cpp-linter-action@v2
id: linter
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
style: 'file' # Use .clang-format config file
tidy-checks: '-*' # disable clang-tidy
auto-fix: 'true' # auto-apply clang-format fixes

- name: Fail fast?!
if: steps.linter.outputs.clang-format-checks-failed > 0
run: exit 1
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,37 @@ For all explanations of our available input parameters and output variables, see

See also our [example recipes][recipes-doc].

### Auto-fix clang-format issues

You can enable automatic fixing of clang-format issues by setting `auto-fix: 'true'`.
When enabled, the action will:

1. Run clang-format detection as usual
2. Apply `clang-format -i` to fix any files with style issues
3. Commit and push the formatted changes back to the PR branch

```yaml
steps:
- uses: actions/checkout@v7
- uses: cpp-linter/cpp-linter-action@v2
id: linter
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
style: 'file'
auto-fix: 'true' # automatically fix format issues
```

> [!TIP]
> To prevent the auto-fix commit from triggering another CI run, include a
> `[skip ci]` (or `[ci skip]`, `[no ci]`, etc.) tag in your custom commit message:
>
> ```yaml
> with:
> auto-fix: 'true'
> auto-fix-commit-msg: 'style: apply styling format fix [skip ci]'
Comment thread
shenxianpeng marked this conversation as resolved.
Outdated
> ```

## Used By

<p align="center">
Expand Down
92 changes: 91 additions & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,38 @@ inputs:
Set this option to `true` to prevent Pull Request reviews from approving or requesting changes.
default: 'false'
required: false
auto-fix:
description: |
Set this option to `true` to automatically apply clang-format fixes
and commit them back to the PR branch.

When enabled, cpp-linter runs with ``--fix``, which applies
``clang-format -i`` on files with style issues. After that,
a new commit is pushed to the PR branch with the formatted changes.

This option has no effect on clang-tidy issues.
default: 'false'
required: false
auto-fix-commit-msg:
description: |
Custom commit message for the auto-fix commit.
Only used when ``auto-fix`` is ``true``.
default: 'style: apply styling format fix'
Comment thread
shenxianpeng marked this conversation as resolved.
Outdated
required: false
auto-fix-git-user:
description: |-
Git username for the auto-fix commit.
Defaults to the value of ``$GITHUB_ACTOR``.
Only used when ``auto-fix`` is ``true``.
default: ''
required: false
auto-fix-git-email:
description: |-
Git email for the auto-fix commit.
Defaults to the value of ``$GITHUB_ACTOR_ID+$GITHUB_ACTOR@users.noreply.github.com``.
Only used when ``auto-fix`` is ``true``.
default: ''
required: false
jobs:
description: |
The number of jobs to run in parallel.
Expand Down Expand Up @@ -425,6 +457,18 @@ runs:
^$'($env.UV_INSTALL_DIR)/uv' ...$uv_args ...$cmd
}

- name: Checkout PR branch for auto-fix push capability
if: (inputs.auto-fix == 'true' || inputs.auto-fix == true) && github.event_name == 'pull_request'
shell: nu {0}
run: |
let head_ref = $env.GITHUB_HEAD_REF
if ($head_ref | is-not-empty) {
print $"(ansi purple)Fetching PR branch \"($head_ref)\" for auto-fix(ansi reset)"
^git fetch origin --depth=1 $"($head_ref):refs/remotes/origin/($head_ref)"
^git checkout --force -B $head_ref $"refs/remotes/origin/($head_ref)"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
print $"(ansi green)Switched to PR branch \"($head_ref)\"(ansi reset)"
}

- name: Run cpp-linter
id: cpp-linter
shell: nu {0}
Expand All @@ -433,7 +477,7 @@ runs:
$env.UV_INSTALL_DIR = $action_path | path join 'bin'
$env.UV_CACHE_DIR = $env.RUNNER_TEMP | path join 'cpp-linter-action-cache'

let args = [
mut args = [
'--style=${{ inputs.style }}'
'--extensions=${{ inputs.extensions }}'
'--tidy-checks=${{ inputs.tidy-checks }}'
Expand All @@ -457,6 +501,9 @@ runs:
'--jobs=${{ inputs.jobs }}'
'--summary-output-file=${{ inputs.summary-output-file }}'
]
if '${{ inputs.auto-fix }}' == 'true' {
$args = ($args | append ['--fix'])
Comment thread
shenxianpeng marked this conversation as resolved.
}
mut uv_args = [run --no-sync --project $action_path --directory (pwd)]

let gh_action_debug = $env | get --optional 'ACTIONS_STEP_DEBUG'
Expand All @@ -482,3 +529,46 @@ runs:

print $"\n(ansi purple)Running cpp-linter(ansi reset)"
^$'($env.UV_INSTALL_DIR)/uv' ...$uv_args cpp-linter ...$args

- name: Auto-commit clang-format fixes
if: inputs.auto-fix == 'true' || inputs.auto-fix == true
shell: nu {0}
run: |
# Refresh index first so stat-only differences don't create false positives
^git update-index -q --refresh
let has_changes = (^git diff-index --name-status --exit-code HEAD -- | complete | $in.exit_code == 1)
if $has_changes {
let git_user_name = if ('${{ inputs.auto-fix-git-user }}' | is-empty) {
$env.GITHUB_ACTOR
} else {
'${{ inputs.auto-fix-git-user }}'
}
let git_user_email = if ('${{ inputs.auto-fix-git-email }}' | is-empty) {
$"($env.GITHUB_ACTOR_ID)+($env.GITHUB_ACTOR)@users.noreply.github.com"
} else {
'${{ inputs.auto-fix-git-email }}'
}
let git_user = $"user.name=($git_user_name)"
let git_email = $"user.email=($git_user_email)"
^git add -A
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
let commit_msg = if ('${{ inputs.auto-fix-commit-msg }}' | is-empty) {
'style: apply styling format fix'
Comment thread
shenxianpeng marked this conversation as resolved.
Outdated
} else {
'${{ inputs.auto-fix-commit-msg }}'
}
^git -c $git_user -c $git_email commit -m $"($commit_msg)"
let branch = $env.GITHUB_HEAD_REF | default $env.GITHUB_REF_NAME
let push_result = (^git push origin $"HEAD:refs/heads/($branch)") | complete
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
if $push_result.exit_code != 0 {
let stderr_lower = ($push_result.stderr | str downcase)
if ($stderr_lower | str contains "403") or ($stderr_lower | str contains "refused") or ($stderr_lower | str contains "not have permission") {
print $"::warning title=Auto-fix push failed::This action does not have permission to push to this branch. When using auto-fix on pull_request events from a third-party fork, the GITHUB_TOKEN cannot write to the fork repository. See docs/permissions.md for details.(ansi reset)"
} else {
print $"::warning title=Auto-fix push failed::(ansi yellow)($push_result.stderr)(ansi reset)"
}
} else {
print $"(ansi green)Auto-fix commit pushed successfully(ansi reset)"
}
} else {
print $"(ansi green)No formatting changes to commit(ansi reset)"
}
9 changes: 9 additions & 0 deletions docs/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ inputs:
passive-reviews:
minimum-version: '2.12.0'
required-permission: 'pull-requests: write #pull-request-reviews'
auto-fix:
minimum-version: '2.19.0'
required-permission: 'contents: write #auto-fix'
auto-fix-commit-msg:
minimum-version: '2.19.0'
auto-fix-git-user:
minimum-version: '2.19.0'
auto-fix-git-email:
minimum-version: '2.19.0'
jobs:
minimum-version: '2.11.0'
cache-enable:
Expand Down
11 changes: 11 additions & 0 deletions docs/examples/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,22 @@
[style]: ../inputs-outputs.md#style
[tidy-checks]: ../inputs-outputs.md#tidy-checks
[thread-comments]: ../inputs-outputs.md#thread-comments
[auto-fix]: ../inputs-outputs.md#auto-fix

# Recipes

Here are some example workflows to get started quickly.

=== "auto-fix clang-format"

``` yaml
--8<-- ".github/workflows/examples/auto-fix.yml"
```

1. See also [`auto-fix`][auto-fix]
2. See also [`style`][style]
3. See also [`tidy-checks`][tidy-checks]

=== "only clang-tidy"

``` yaml
Expand Down
37 changes: 37 additions & 0 deletions docs/permissions.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,40 @@ The [`tidy-review`](inputs-outputs.md#tidy-review), [`format-review`](inputs-out
permissions:
pull-requests: write
```

## Auto-fix

The [`auto-fix`](inputs-outputs.md#auto-fix) feature requires `contents: write` permission
in addition to any other permissions needed for other features:

```yaml
permissions:
contents: write # (1)!
```
Comment thread
coderabbitai[bot] marked this conversation as resolved.

1. Needed to commit and push the formatted changes back to the PR branch.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We should also note that this permission is only needed by the token used in actions/checkout (which defaults to github.token).

However, the github.token is NOT able to trigger new CI runs when used to push a commit. If users want the auto-fix commit to trigger a new CI run, then they need to provide it with a PAT that has contents: write permission.

- uses: actions/checkout@v6
  with:
    # MY_TOKEN must have `contents: write` permission.
    # using default token does not trigger CI runs on `git push`
    token: ${{ secrets.MY_TOKEN }}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Another problem that might arise here is when auto-fixing PR changes from a third-party fork. My previous comment was made with experience about pushing changes to the same remote. If the changes need to be pushed to a different remote, then that is a different set of permissions.


!!! warning "CI re-triggering with auto-fix"

The default `GITHUB_TOKEN` **cannot** trigger new CI runs when pushing
a commit. If you need the auto-fix commit to trigger CI checks
(e.g. to verify the fix builds clean), use a personal access token
(PAT) with `contents: write` scope:

```yaml
- uses: actions/checkout@v7
with:
token: ${{ secrets.MY_PAT }}
```

When using the default `GITHUB_TOKEN`, you can include `[skip ci]` in
the auto-fix commit message to avoid unnecessary CI runs on the
fix commit itself. See the [`auto-fix-commit-msg`](./inputs-outputs.md#auto-fix-commit-msg) input.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

!!! warning "Pull requests from third-party forks"

Auto-fix does not work on pull requests from third-party forks. The
`GITHUB_TOKEN` lacks write permission to the fork repository, and
`git push` to the fork's branch is not possible. Consider
restricting `auto-fix` to `push` events or pull requests from the
same repository.
Loading