Skip to content

fix: venv usage #314

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Aug 18, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
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