Skip to content
54 changes: 49 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,6 @@ to collect feedback provided in the form of
workflow [`step-summary`][step-summary], and Pull Request reviews (with
[`tidy-review`][tidy-review] or [`format-review`][format-review]).

> [!WARNING]
> We only support Linux runners using a Debian-based Linux OS (like Ubuntu and many others).
>
> MacOS and Windows runners are supported as well.

## Usage

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/main/.github/workflows/cpp-linter.yml)
Expand Down Expand Up @@ -158,8 +153,57 @@ Example

To provide feedback (requesting a feature or reporting a bug) please post to [issues](https://github.com/cpp-linter/cpp-linter-action/issues).

## Required tools installed

As of v2.16.0, this action uses

- [nushell] for cross-platform compatible scripting
- [uv] for driving a Python virtual environment

This action installs [nushell] and [uv] automatically.
Only [nushell] is added to the PATH environment variable.
[uv], and any standalone Python distribution it downloads, are not added to the PATH environment variable.

### On Linux runners

We only support Linux runners using a Debian-based Linux OS (like Ubuntu and many others).
This is because we first try to use the `apt` package manager to install clang tools.

Linux workflows that use a specific [`container`][gh-container-syntax] should ensure that
the following are installed:

- GLIBC (v2.32 or later)
- `wget` or `curl`
- `lsb-release` (required by LLVM-provided install script)
- `software-properties-common` (required by LLVM-provided install script)
- `gnupg` (required by LLVM-provided install script)

```shell
apt-get update
apt-get install -y libc6 wget lsb-release software-properties-common gnupg
```

Otherwise, [nushell] and/or the LLVM-provided bash script will fail to run.

### On macOS runners

The specified `version` of `clang-format` and `clang-tidy` is installed via Homebrew.
Failing that, we attempt to use static binaries that we built ourselves;
see [cpp-linter/clang-tools-pip] and [cpp-linter/clang-tools-static-binaries] projects for more detail.

### On Windows runners

For Windows runners, we only use clang tools built as static binaries.
See [cpp-linter/clang-tools-pip] and [cpp-linter/clang-tools-static-binaries] projects for more detail.

## License

The scripts and documentation in this project are released under the [MIT License](https://github.com/cpp-linter/cpp-linter-action/blob/main/LICENSE)

[nushell]: https://www.nushell.sh/
[uv]: https://docs.astral.sh/uv/
[cpp-linter/clang-tools-pip]: https://github.com/cpp-linter/clang-tools-pip
[cpp-linter/clang-tools-static-binaries]: https://github.com/cpp-linter/clang-tools-static-binaries
[gh-container-syntax]: https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#jobsjob_idcontainer

<!--README-end-->
194 changes: 141 additions & 53 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,13 @@ inputs:
required: false
default: 0
cache-enable:
description: enable caching of cpp-linter dependencies
description: |-
Controls the caching of cpp-linter dependencies.
The installed `clang-format` and `clang-tidy` tools are not cached.

By default, this is enabled.
Any cached assets are kept within the path to this action's source
(not in the runner's workspace or temp directory).
required: false
default: true
outputs:
Expand All @@ -233,21 +239,6 @@ outputs:
runs:
using: "composite"
steps:
- name: Install Linux clang dependencies
if: runner.os == 'Linux'
shell: bash
run: |
sudo apt-get update
# First try installing from default Ubuntu repositories before trying LLVM script
if ! sudo apt-get install -y clang-format-${{ inputs.version }} clang-tidy-${{ inputs.version }}; then
# This LLVM script will add the relevant LLVM PPA: https://apt.llvm.org/
wget https://apt.llvm.org/llvm.sh -O ${GITHUB_ACTION_PATH%/}/llvm_install.sh
chmod +x ${GITHUB_ACTION_PATH%/}/llvm_install.sh
if sudo ${GITHUB_ACTION_PATH%/}/llvm_install.sh ${{ inputs.version }}; then
sudo apt-get install -y clang-format-${{ inputs.version }} clang-tidy-${{ inputs.version }}
fi
fi

- name: Setup nu shell
# I'm done writing everything twice (in bash and powershell)
# With nu shell, we use the same shell/script for all platforms
Expand Down Expand Up @@ -279,75 +270,172 @@ runs:
path: ${{ runner.temp }}/cpp-linter-action-cache
key: cpp-linter-action_${{ runner.os }}_${{ steps.compute-cache-key.outputs.key }}

- name: Install Linux clang dependencies
if: runner.os == 'Linux'
shell: nu {0}
run: |
let action_path = $env.GITHUB_ACTION_PATH | path expand
let apt_install_args = [
install -y clang-format-${{ inputs.version }} clang-tidy-${{ inputs.version }}
]
let has_sudo = not ((which 'sudo') | is-empty)

# First try installing from default Ubuntu repositories before trying LLVM script
let are_tools_present = (
if $has_sudo {
^sudo apt-get update
^sudo apt-get ...$apt_install_args
} else {
^apt-get update
^apt-get ...$apt_install_args
}
) | complete | $in.exit_code == 0
if (not $are_tools_present) {
# This LLVM script will add the relevant LLVM PPA: https://apt.llvm.org/
(
http get --raw --redirect-mode follow https://apt.llvm.org/llvm.sh
| save $"($action_path)/llvm_install.sh"
)
^chmod +x $"($action_path)/llvm_install.sh"

let llvm_installer_result = (
if $has_sudo {
^sudo $"($action_path)/llvm_install.sh" ${{ inputs.version }}
} else {
^bash $"($action_path)/llvm_install.sh" ${{ inputs.version }}
}
) | complete
print $llvm_installer_result

if ($llvm_installer_result.exit_code == 0) {
let result = (
if $has_sudo {
^sudo apt-get ...$apt_install_args
} else {
^apt-get ...$apt_install_args
}
) | complete
print $result
}
}

- name: Install MacOS clang dependencies
if: runner.os == 'macOS'
shell: bash
continue-on-error: true
run: |
brew install llvm@${{ inputs.version }}
ln -s "$(brew --prefix llvm@${{ inputs.version }})/bin/clang-format" "/usr/local/bin/clang-format-${{ inputs.version }}"
ln -s "$(brew --prefix llvm@${{ inputs.version }})/bin/clang-tidy" "/usr/local/bin/clang-tidy-${{ inputs.version }}"
shell: nu {0}
run: |-
let brew_install_arg = 'llvm@${{ inputs.version }}'
let result = (^brew install $brew_install_arg) | complete
print $result
if ($result.exit_code == 0) {
let brew_prefix = ^brew --prefix $brew_install_arg
^ln -s $"($brew_prefix)/bin/clang-format" "/usr/local/bin/clang-format-${{ inputs.version }}"
^ln -s $"($brew_prefix)/bin/clang-tidy" "/usr/local/bin/clang-tidy-${{ inputs.version }}"
}

- name: Setup cpp-linter dependencies
shell: nu {0}
env:
UV_NO_MODIFY_PATH: 1
UV_VERSION: '0.8.9'
UV_VERSION: '0.8.11'
run: |-
let action_path = $env.GITHUB_ACTION_PATH | path expand
$env.UV_INSTALL_DIR = $action_path | path join 'bin'
$env.UV_PROJECT_ENVIRONMENT = $action_path | path join '.venv'

$env.UV_CACHE_DIR = $env.RUNNER_TEMP | path join 'cpp-linter-action-cache'
if (not ($env.UV_CACHE_DIR | path exists)) {
mkdir $env.UV_CACHE_DIR
mkdir $env.UV_CACHE_DIR
}

print $"\n(ansi purple)Installing uv version ($env.UV_VERSION)(ansi reset)"
if ((sys host | get 'name') == 'Windows') {
^powershell -ExecutionPolicy ByPass -c $"irm https://astral.sh/uv/($env.UV_VERSION)/install.ps1 | iex"
let is_windows = (sys host | get 'name') == 'Windows'
let uv_installer_url = if $is_windows {
$"https://astral.sh/uv/($env.UV_VERSION)/install.ps1"
} else {
$"https://astral.sh/uv/($env.UV_VERSION)/install.sh"
}
let installer = http get --raw --redirect-mode follow $uv_installer_url
if $is_windows {
^powershell -ExecutionPolicy ByPass $installer
} else {
^curl -LsSf $"https://astral.sh/uv/($env.UV_VERSION)/install.sh" | sh
$installer | ^sh
}

let gh_action_debug = $env | get --optional 'ACTIONS_STEP_DEBUG'
let action_verbosity = '${{ inputs.verbosity }}' == 'debug'
let verbosity = (
$action_verbosity
or ($gh_action_debug == true)
or ($gh_action_debug == 'true')
)

print $"\n(ansi purple)Installing workflow dependencies(ansi reset)"
^$'($env.UV_INSTALL_DIR)/uv' sync --directory $action_path --group action
mut uv_args = [sync --project $action_path --group action]
if $verbosity {
$uv_args = $uv_args | append '-v'
}
^$'($env.UV_INSTALL_DIR)/uv' ...$uv_args

print $"\n(ansi purple)Ensuring clang-format and clang-tidy ${{ inputs.version }} are present(ansi reset)"
^$'($env.UV_INSTALL_DIR)/uv' run clang-tools -i ${{ inputs.version }} -b
let cmd = [clang-tools -i ${{ inputs.version }} -b]
$uv_args = [run --no-sync --project $action_path --directory (pwd)]
if $verbosity {
$uv_args = $uv_args | append '-v'
}
^$'($env.UV_INSTALL_DIR)/uv' ...$uv_args ...$cmd

- name: Run cpp-linter
id: cpp-linter
shell: nu {0}
run: |-
let action_path = $env.GITHUB_ACTION_PATH | path expand
$env.UV_INSTALL_DIR = $action_path | path join 'bin'
$env.UV_PROJECT_ENVIRONMENT = $action_path | path join '.venv'
$env.UV_CACHE_DIR = $env.RUNNER_TEMP | path join 'cpp-linter-action-cache'

let args = [
--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 }}
--no-lgtm=${{ inputs.no-lgtm }}
--step-summary=${{ inputs.step-summary }}
--ignore="${{ inputs.ignore }}"
--ignore-tidy="${{ inputs.ignore-tidy }}"
--ignore-format="${{ inputs.ignore-format }}"
--database=${{ inputs.database }}
--file-annotations=${{ inputs.file-annotations }}
--extra-arg="${{ inputs.extra-args }}"
--tidy-review="${{ inputs.tidy-review }}"
--format-review="${{ inputs.format-review }}"
--passive-reviews="${{ inputs.passive-reviews }}"
--jobs=${{ inputs.jobs }}
--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 }}
--no-lgtm=${{ inputs.no-lgtm }}
--step-summary=${{ inputs.step-summary }}
--ignore="${{ inputs.ignore }}"
--ignore-tidy="${{ inputs.ignore-tidy }}"
--ignore-format="${{ inputs.ignore-format }}"
--database=${{ inputs.database }}
--file-annotations=${{ inputs.file-annotations }}
--extra-arg="${{ inputs.extra-args }}"
--tidy-review="${{ inputs.tidy-review }}"
--format-review="${{ inputs.format-review }}"
--passive-reviews="${{ inputs.passive-reviews }}"
--jobs=${{ inputs.jobs }}
]
mut uv_args = [run --no-sync --project $action_path --directory (pwd)]

let gh_action_debug = $env | get --optional 'ACTIONS_STEP_DEBUG'
let action_verbosity = '${{ inputs.verbosity }}' == 'debug'
let verbosity = (
$action_verbosity
or ($gh_action_debug == true)
or ($gh_action_debug == 'true')
)
if $verbosity {
$uv_args = $uv_args | append '-v'
}

let local_bin = '~/.local/bin' | path expand
if (
('${{ runner.os }}' == 'Linux')
and ($local_bin | path exists)
and (not ($env.PATH | any {$in == $local_bin}))
) {
# add ~/.local/bin to PATH (temporarily)
$env.PATH = $env.PATH | append $local_bin
}

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