Skip to content

Latest commit

 

History

History
176 lines (122 loc) · 5.71 KB

formatting.md

File metadata and controls

176 lines (122 loc) · 5.71 KB

Formatting

Installation

Create a BUILD file that declares the formatter binary, typically at tools/format/BUILD.bazel

This file contains a format_multirun rule. To use the tools supplied by default in rules_lint, just make a simple call to it like so:

load("@aspect_rules_lint//format:defs.bzl", "format_multirun")

format_multirun(name = "format")

For more details, see the format_multirun API documentation and the example/tools/format/BUILD.bazel file.

Finally, we recommend an alias in the root BUILD file, so that developers can just type bazel run format:

alias(
    name = "format",
    actual = "//tools/format",
)

Choosing formatter tools

Each formatter should be installed by Bazel. A formatter is just an executable target.

rules_lint provides some default tools at specific versions using rules_multitool. You may fetch alternate tools or versions instead.

To register the tools you fetch, supply them as values for that language attribute.

For example:

load("@aspect_rules_lint//format:defs.bzl", "format_multirun")

format_multirun(
    name = "format",
    python = ":ruff",
)

Usage

Configuring formatters

Since the format target is a bazel run command, it already runs in the working directory alongside the sources. Therefore the configuration instructions for the formatting tool should work as-is. Whatever configuration files the formatter normally discovers will be used under Bazel as well.

As an example, if you want to change the indent level for Shell formatting, you can follow the instructions for shfmt and create a .editorconfig file:

[[shell]]
indent_style = space
indent_size = 4

One-time re-format all files

Assuming you installed with the typical layout:

bazel run //:format

Note that mass-reformatting can be disruptive in an active repo. You may want to instruct developers with in-flight changes to reformat their branches as well, to avoid merge conflicts. Also consider adding your re-format commit to the .git-blame-ignore-revs file to avoid polluting the blame layer.

Re-format specific file(s)

bazel run //:format some/file.md other/file.json

Ignoring files explicitly

Commonly, the underlying formatters that rules_lint invokes provide their own methods of excluding files (.prettierignore for example). At times when that is not the case, rules_lint provides its own escape hatch to exclude files from linting using attributes specified via .gitattributes files.

If any of following attributes are set or have a value of true on a file it will be excluded:

  • rules-lint-ignored=true
  • gitlab-generated=true
  • linguist-generated=true

Install as a pre-commit hook

If you use pre-commit.com, add this in your .pre-commit-config.yaml:

- repo: local
  hooks:
    - id: aspect_rules_lint
      name: Format
      language: system
      entry: bazel run //:format
      files: .*

Note that pre-commit is silent while Bazel is fetching the tools, which can make it appear hung on the first run. There is no way to avoid this; see pre-commit/pre-commit#1003

If you don't use pre-commit, you can just wire directly into the git hook, however this option will always run the formatter over all files, not just changed files.

$ echo "bazel run //:format.check" >> .git/hooks/pre-commit
$ chmod u+x .git/hooks/pre-commit

Check that files are already formatted

We recommend using Aspect Workflows to hook up the CI check to notify developers of formatting changes, and supply a patch file that can be locally applied.

format on CI

To set this up manually, there are two supported methods:

1: run target

This will exit non-zero if formatting is needed. You would typically run the check mode on CI.

bazel run //tools/format:format.check

2: test target

Normally Bazel tests should be hermetic, declaring their inputs, and therefore have cacheable results.

This is possible with format_test and a list of srcs. Note that developers may not remember to add format_test for their new source files, so this is quite brittle, unless you also use a tool like Gazelle to automatically update BUILD files.

load("@aspect_rules_lint//format:defs.bzl", "format_test")

format_test(
    name = "format_test",
    # register languages, e.g.
    # python = "//:ruff",
    srcs = ["my_code.go"],
)

Alternatively, you can give up on Bazel's hermeticity, and follow a similar pattern as buildifier_test which creates an intentionally non-hermetic, and not cacheable target.

This will always run the formatters over all files under bazel test, so this technique is only appropriate when the formatters are fast enough, and/or the number of files in the repository are few enough. To acknowledge this fact, this mode requires an additional opt-in attribute, no_sandbox.

load("@aspect_rules_lint//format:defs.bzl", "format_test")

format_test(
    name = "format_test",
    # register languages, e.g.
    # python = "//:ruff",
    no_sandbox = True,
    workspace = "//:WORKSPACE.bazel",
)

Then run bazel test //tools/format/... to check that all files are formatted.