Trunk YAML (.trunk/trunk.yaml)

trunk.yaml specification

Trunk is controlled by the .trunk/trunk.yaml file in your repository.

/your_repo
├── .trunk
│   └── trunk.yaml
└── src
    ├── bar
    └── foo

This is initially generated by trunk init and is the central source of truth for how Trunk operates inside your repository. As we build new services and features, we'll extend trunk.yaml to include configuration sections for them. We believe strongly in configuration as code and being able to guarantee that trunk can be run reproducibly.

The Trunk configuration file is written in YAML and is meant to be self-descriptive. We've included below a sample config file in its entirety to help you understand how the pieces all come together. Alternatively, you can also refer to the trunk.yaml in our GitHub Action as an example or trunk-yaml-schema.json.

version: 0.1 # the version of this config file.
cli:
  version: 0.15.1 # the version of trunk you will run in your repository
runtimes:
  enabled:
    - ruby@>=2.7.1
    - python@3.9.1
lint:
  definitions:
    - name: my_custom_linter
      files: [ALL]
      commands:
        output: sarif
        run: ${workspace}/bin/foo --file ${target}
        read_output_from: stdout
        run_linter_from: workspace
        success_codes: [0, 1]
  enabled:
    - ansible-lint@5.3.2
    - bandit@1.7.0
    - black@21.6b0
    - buf-lint@1.0.0-rc3
    - buildifier@5.1.0
    - cfnlint@0.51.0
    - eslint@7.30.0
    - gitleaks@7.6.1
    - gofmt@1.16.7
    - golangci-lint@1.41.1
    - hadolint@2.6.0
    - isort@5.8.0
    - markdownlint@0.28.1
    - mypy@0.910
    - prettier@2.3.2
    - pylint@2.8.1
    - rustfmt@1.55.0
    - semgrep@0.104.0
    - shellcheck@0.7.2
    - shfmt@3.3.1
  disabled:
    - rufo
    - tflint
  ignore:
    - linters: [ALL]
      paths:
        # Generated files
        - a/proto/code_pb*
        # Test data
        - b/test_data/**
    - linters: [eslint]
      paths:
        - c/run.ts
  triggers:
    - linters:
        - ansible-lint
      paths:
        - ansible # A directory
      targets:
        - ansible # A directory

The first two sections of this file are self-explanatory: version is the schema version of trunk.yaml and cli.version is the version of the trunk CLI that we have pinned.

cli

cli:
  version: 0.15.1 # the version of trunk you will run in your repository
  options:
    - commands: [ALL] # apply to all `trunk` commands
      args: --monitor=true
    - commands: [check, fmt] # apply only to `trunk check` and `trunk fmt` commands
      args: -y

In addition to specifying version, cli allows you to specify default command line arguments using the options field. Specified args will be appended to strictly matched commands during trunk invocations. Specifying ALL as a commands element applies its options to all trunk subcommands. Any command line options will take precedence over these args.

Some examples using the configuration above:

  • trunk check resolves to trunk check -y --monitor=true

  • trunk check -n resolves to trunk check -n --monitor=true

  • trunk fmt resolves to trunk fmt -y --monitor=true

repo

repo:
  # main is the branch that everyone's work is merged into
  # (this is usually inferred and not required to be set)
  trunk_branch: main

Some Trunk features require Trunk to be aware of the canonical repository your organization uses, i.e. the repository which everyone pulls from and makes pull requests into. trunk can infer this from your origin remote, but if you don't want your origin to be used for this purpose, you can explicitly specify your canonical repository.

Other features - namely trunk check - need to be aware of the primary upstream branch that everyone branches from. If you use main or master, trunk can infer this; however, if you use some other primary branch, then you may want to consider setting this.

The above configuration is how you would specify that https://github.com/github/gitignore is your canonical repository and that main is the branch which trunk should always think of as your upstream branch.

api

api:
  # name of your trunk organization on app.trunk.io
  org: { your-org-name }

Some Trunk features, like the CI Debugger, require knowledge of the Trunk organization your repository is using. This information can be provided on the command-line or hard-coded in the trunk.yaml file.

Stacked PR support

repo:
  use_branch_upstream: true

By default, trunk will auto-detect all changed files relative to your main branch. If you would instead like it to compare against the upstream of your current git branch, you can enable this feature by setting use_branch_upstream to true.

runtimes

Trunk manages the hermetic installation of all required runtimes. You can also specifically pin a version of a runtime you'd like Trunk to use, or tell Trunk to re-use an already-installed runtime on the system.

By default, Trunk will use its own hermetic runtime installations. To specify that Trunk is allowed to use the version of a runtime available in your PATH, you can specify a range-based version constraint, e.g. >=0.1.2.

In the example config below, if there is a version of ruby which is at least 2.7.1 already installed on the system, Trunk will use that system version of ruby; similarly, if there is a version of python which is exactly 3.9.1, trunk will use that system version of python. If there is no pre-installed runtime available that satisfies the specified constraint, then Trunk will instead install its own instance of said runtime to use.

runtimes:
  enabled:
    - ruby@>=2.7.1 # Use system installed ruby if it matches version 2.7.1 or greater
    - python@3.9.1 # Use system python if it matches 3.9.1 exactly

lint

See the check configuration and custom linter documentation.

Disable upgrade notifications

Trunk will periodically tell you to upgrade to a newer version if one is available. If you prefer not to see these notifications, edit (or add) the section of your .trunk/trunk.yaml to include the following lines:

actions:
  disabled:
    - trunk-upgrade-available

Overriding defaults

Trunk ships with a default configuration which trunk.yaml is merged into to produce the actual configuration that Trunk runs with. You can view this merged configuration using trunk print-config.

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.

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"]
  ...

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
  ...

Some linters have multiple commands, such as trivy, which can run in different ways. 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 wanted 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"]

Notes:

  1. Custom linter/download/runtime configs are subject to validation - they must be fully defined - but configs that have a matching name to a built-in linter/download/runtime can have as few or many values specified as desired.

  2. Scalar values are overridden in a straightforward manner - the value specified in the override takes the place of the default, and otherwise, default values are retained.

  3. To override a sequence value in the default (ex. environment in the node runtime), it is necessary to fully specify the new sequence. This is why the environment override above also defines HOME. If you just wanted to add a new value, you would have to copy in the existing sequence to your overriding config, and add your new value to the end of the list.

  4. It is not possible to set sequences of non-zero length to zero length. For example, if the default config has success_codes: [0], you may override this to success_codes: [0, 1], but you cannot clear its value.

  5. Merged configurations are subject to the same validation that custom linters are - they must all have a name, type, command, and either success_codes or error_codes set.

Last updated