Trunk Code Quality's linter integrations are fully configurable. This means that you can easily tune existing linters or leverage our caching and hold-the-line solution with your own custom linters.
Here's an overview of the ways you can configure linters.
Linters can be configured at different places:
- The source plugin repo usually
https://github.com/trunk-io/plugins
. - The repo-wide Trunk config file overrides the definitions in the plugin repos,
.trunk/trunk.yaml
- Local, per-user configuration in
.trunk/user.yaml
which is used for local overrides of.trunk/trunk.yaml
and doesn't - Per linter configuration in linter config files such as
eslint.config.js
or.prettierrc
.
Trunk defines linter configuration in a plugin system. By default, it'll point to the Trunk plugin repo on GitHub. You can check if other custom plugin sources are specified in your trunk.yaml
file for shared-configs.md.
version: 0.1
cli:
version: 1.22.2
# Trunk provides extensibility via plugins. (https://docs.trunk.io/cli/configuration/plugins)
plugins:
sources:
- id: trunk
ref: v1.6.1
uri: https://github.com/trunk-io/plugins
Each linter implemented in the Plugin Repo has its own linter definition. Let's take clang-tidy as an example, which ships with the following default configuration:
definitions:
...
- name: clang-tidy
files: [c/c++-source]
type: llvm
commands:
- output: llvm
run: clang-tidy --export-fixes=- ${target}
success_codes: [0]
download: clang-tidy
direct_configs: [.clang-tidy]
disable_upstream: true
include_scanner_type: compile_command
environment:
- name: PATH
list: ["${linter}/bin"]
...
You can find the default definitions for linters in the Plugin Repo and find references for these fields on the Linter Definitions page.
You may find while using Trunk that you want to modify one of these defaults: perhaps you want clang-tidy
to not run on the upstream, or maybe you want the node
runtime to include another environment variable. In these cases, you can specify the field in your trunk.yaml
to override the default value.
If you wanted to flip the value of disable_upstream
to false
, you could, in your own trunk.yaml
, specify:
definitions:
...
- name: clang-tidy
disable_upstream: false
...
{% hint style="info" %}
Overriding definitions in your trunk.yaml
file doesn't require you to specify the entire definition again. You only need to specify what's being overridden.
{% endhint %}
Some linters have multiple commands, such as Ruff, which can run in different ways. By default, Ruff is configured to only run as a linter:
lint:
enabled:
- ruff@<version>:
commands: [lint]
You can configure ruff to also run the format command by adding it to the commands tuple:
lint:
enabled:
- ruff@<version>:
commands: [lint, format]
Similarly, some linters are configured to run differently on different platforms or at different versions. When overriding a command definition, overrides are applied on the tuple [name, version, platforms]
.
For example, if you wanted to disable batching when running ktlint on Windows, you could consider its default configuration:
definitions:
...
- name: ktlint
...
commands:
- name: format
platforms: [windows]
run: java -jar ${linter}/ktlint.exe -F "${target}"
output: rewrite
cache_results: true
formatter: true
in_place: true
batch: true
success_codes: [0, 1]
- name: format
run: ktlint -F "${target}"
output: rewrite
cache_results: true
formatter: true
in_place: true
batch: true
success_codes: [0, 1]
...
And override it as such:
definitions:
...
- name: ktlint
...
commands:
- name: format
platforms: [windows]
batch: false
...
When executing linters, Trunk will execute the first matching command based on its compatible platforms and linter version. Note when overriding that new commands that don't match an existing tuple are prepended to the resulting commands list.
Alternatively, consider the default node
runtime:
runtimes:
definitions:
- type: node
download: node
runtime_environment:
- name: HOME
value: ${home}
- name: PATH
list: ["${runtime}/bin"]
linter_environment:
- name: PATH
list: ["${linter}/node_modules/.bin"]
version: 16.14.2
version_commands:
- run: "node --version"
parse_regex: ${semver}
If you want to add ${home}/my/special/node/path
to PATH
, you could specify the following:
runtimes:
- type: node
runtime_environment:
- name: HOME
value: ${home}
- name: PATH
list: ["${home}/my/special/node/path", "${runtime}/bin"]
All issue severities low-high are considered blocking by default. In cases where you might want to slowly try out a new linter, we provide a mechanism to set specific thresholds for each linter.
lint:
threshold:
- linters: [clang-tidy]
level: high
Every entry in threshold
defines a set of linters and the severity threshold that is considered blocking. In this example, we're saying that only high
lint issues should be considered blocking for clang-tidy
.
Key | Value |
---|---|
linters | List of linters (e.g. [black, eslint] ) or the special [ALL] tag |
level | Default low . Threshold at which issues are considered blocking. One of: note , low , medium , high , or none (this last option will result in issues never blocking) |
Some linters do not operate on individual files. Instead, you must lint your entire repo at once. The way this is handled in Trunk is to set up a trigger rule. Most linters will not require the use of a trigger rule.
Trigger rules work on 3 principles:
- Input(s) that trigger the linters. These can be files, directories, or extended globs.
- Linter(s) to run when a triggered file is modified.
- Targets(s) to pass to the linters (can be files or directories).
An example for ansible-lint:
lint:
enabled:
- [email protected]
triggers:
- linters:
- ansible-lint
paths:
- ansible # A directory
targets:
- ansible # A directory
Triggered linters will also be run when executing trunk check with --all
so long as a file exists that matches one of the listed paths.
You may use .
as a target to run on the entire repo instead of an isolated directory.
By default, Trunk only lints files up to 4 MiB in size. To override this globally, specify a default_max_file_size
in lint
:
lint:
default_max_file_size: 1048576 # Bytes
To override this for a specific linter, specify a max_file_size
in its definition:
lint:
definitions:
- name: prettier
max_file_size: 2097152 # Bytes
Each linter has a default timeout of 10 minutes. If its execution takes longer than this amount of time, Trunk Code Quality will terminate the process and return an error to the user.
To override the timeout for a specific linter, specify a run_timeout
in its definition:
lint:
definitions:
- name: clang-tidy
run_timeout: 5m
The run_timeout
value can be specified in seconds (s
), minutes (m
), or hours (h
).
Trunk can also be managed by the .trunk/user.yaml
file in your repository. This file is optional, but it allows individual developers to customize how they want trunk
to run on their machines.
Simply configure .trunk/user.yaml
as you would for .trunk/trunk.yaml
. Be mindful that .trunk/user.yaml
takes precedence over .trunk/trunk.yaml
, so substantial modifications could violate hermeticity.
Trunk allows you to keep using your existing linter configs, and new linters recommended by Trunk will have their configs added in the .trunk/configs
folder. These config files will be symlinked in during any trunk check
run.
{% hint style="info" %} If you're using an IDE Extension like clangd with an LSP that relies on those configs being in the root, you will need to create an additional symlink from the hidden config to the workspace root. {% endhint %}
You can move existing linter config files into the .trunk/config
folder. You can check which files are automatically symlinked by looking for the direct_configs
of each plugin's definition. If there are config files not listed, you can add them by overriding the definition like this:
lint:
definitions:
- name: some_linter_name
direct_configs:
- .custom_config.file