Try Live
Add Docs
Rankings
Pricing
Enterprise
Docs
Install
Theme
Install
Docs
Pricing
Enterprise
More...
More...
Try Live
Rankings
Create API Key
Add Docs
Aspect Build Rules Lint
https://github.com/aspect-build/rules_lint
Admin
A Bazel ruleset that integrates linting and formatting as first-class concepts, supporting 30+
...
Tokens:
14,947
Snippets:
249
Trust Score:
9.5
Update:
2 weeks ago
Context
Skills
Chat
Benchmark
69.7
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# aspect_rules_lint Aspect Rules Lint is a Bazel ruleset that integrates linting and formatting as first-class concepts under Bazel. It provides a unified way to run various language-specific linters and formatters on your codebase without requiring changes to existing BUILD files or rulesets. The project supports over 30 languages and tools including ESLint, Prettier, Ruff, Flake8, Shellcheck, Buildifier, and many more. The ruleset offers several key features: incremental linting via Bazel's caching and remote execution capabilities, multiple presentation modes for lint results (terminal warnings, test failures, or CI code review comments), the ability to lint only changed files following the "water leak principle," and support for formatting files not tracked by Bazel. Configuration uses standard tool config files (like `.eslintrc`, `.flake8`, `pyproject.toml`) ensuring consistency between Bazel and editor plugins. ## format_multirun Creates a multi-formatter that runs multiple language formatters in parallel. This macro produces both a fix target (for in-place formatting) and a check target (for CI validation). Formatters are registered by language attribute, using either built-in tools from rules_multitool or custom binaries. ```starlark # tools/format/BUILD.bazel load("@aspect_rules_lint//format:defs.bzl", "format_multirun") # Basic usage with built-in formatters format_multirun( name = "format", python = "@aspect_rules_lint//format:ruff", javascript = ":prettier", go = "@aspect_rules_lint//format:gofmt", ) # With custom formatter arguments format_multirun( name = "format", kotlin = ":ktfmt", kotlin_fix_args = ["--google-style"], kotlin_check_args = ["--google-style", "--set-exit-if-changed", "--dry-run"], java = ":java-format", java_fix_args = ["--aosp", "--replace"], python = ":ruff", python_check_args = ["format", "--check", "--diff"], ) # Run formatting: # bazel run //:format # Format all files # bazel run //:format some/file.py # Format specific files # bazel run //:format.check # Check without modifying (CI mode) ``` ## format_test Creates a test target that verifies files are correctly formatted. Supports two modes: hermetic mode with explicit `srcs` list (cacheable) or non-hermetic sandbox mode that checks all workspace files (always runs). ```starlark # tools/format/BUILD.bazel load("@aspect_rules_lint//format:defs.bzl", "format_test") # Hermetic mode - test specific files (cacheable) format_test( name = "format_test", srcs = ["my_code.go", "helper.py"], python = "@aspect_rules_lint//format:ruff", go = "@aspect_rules_lint//format:gofmt", ) # Non-hermetic mode - test all workspace files format_test( name = "format_test", workspace = "//:MODULE.bazel", no_sandbox = True, python = "@aspect_rules_lint//format:ruff", javascript = ":prettier", ) # Run the test: # bazel test //tools/format:format_test ``` ## lint_eslint_aspect Factory function to create an ESLint linter aspect for JavaScript/TypeScript projects. The aspect visits `js_library` and `ts_project` targets by default and produces both human-readable and SARIF machine-readable reports. ```starlark # tools/lint/linters.bzl load("@aspect_rules_lint//lint:eslint.bzl", "lint_eslint_aspect") load("@aspect_rules_lint//lint:lint_test.bzl", "lint_test") # First, declare the ESLint binary in tools/lint/BUILD.bazel: # load("@npm//:eslint/package_json.bzl", eslint_bin = "bin") # eslint_bin.eslint_binary(name = "eslint") eslint = lint_eslint_aspect( binary = Label("//tools/lint:eslint"), configs = [ Label("//:eslintrc"), Label("//src:tsconfig.json"), ], rule_kinds = ["js_library", "ts_project", "ts_project_rule"], ) # Create test rule for BUILD file usage eslint_test = lint_test(aspect = eslint) # In BUILD files: # eslint_test( # name = "eslint", # srcs = [":my_js_library"], # ) # Run linting via Aspect CLI: # bazel lint //src:all # Or configure in .bazelrc for build-time linting: # common:lint --aspects=//tools/lint:linters.bzl%eslint # common:lint --@aspect_rules_lint//lint:fail_on_violation ``` ## lint_ruff_aspect Factory function to create a Ruff linter aspect for Python projects. Ruff is a fast Python linter written in Rust that can replace Flake8, isort, and many other tools. The aspect visits `py_binary`, `py_library`, and `py_test` targets. ```starlark # tools/lint/linters.bzl load("@aspect_rules_lint//lint:ruff.bzl", "lint_ruff_aspect") load("@aspect_rules_lint//lint:lint_test.bzl", "lint_test") # Use the built-in ruff binary ruff = lint_ruff_aspect( binary = Label("@aspect_rules_lint//lint:ruff_bin"), configs = [ Label("//:pyproject.toml"), Label("//src:ruff.toml"), ], rule_kinds = ["py_binary", "py_library", "py_test"], filegroup_tags = ["python", "lint-with-ruff"], ) ruff_test = lint_test(aspect = ruff) # pyproject.toml configuration example: # [tool.ruff] # line-length = 120 # select = ["E", "F", "W", "I"] # ignore = ["E501"] # # [tool.ruff.isort] # known-first-party = ["myproject"] # Run linting: # bazel lint //src:all --fix # Apply auto-fixes # bazel lint //src:all # Report violations ``` ## lint_flake8_aspect Factory function to create a Flake8 linter aspect for Python projects. Flake8 is a wrapper around PyFlakes, pycodestyle, and McCabe complexity checker. ```starlark # tools/lint/linters.bzl load("@aspect_rules_lint//lint:flake8.bzl", "lint_flake8_aspect") load("@aspect_rules_lint//lint:lint_test.bzl", "lint_test") # First, declare flake8 binary in tools/lint/BUILD.bazel: # load("@rules_python//python/entry_points:py_console_script_binary.bzl", "py_console_script_binary") # py_console_script_binary( # name = "flake8", # pkg = "@pip//flake8:pkg", # ) flake8 = lint_flake8_aspect( binary = Label("//tools/lint:flake8"), config = Label("//:.flake8"), rule_kinds = ["py_binary", "py_library"], ) flake8_test = lint_test(aspect = flake8) # .flake8 configuration example: # [flake8] # max-line-length = 120 # extend-ignore = E203, W503 # exclude = .git,__pycache__,build # In BUILD files: # flake8_test( # name = "flake8", # srcs = [":my_py_lib"], # ) ``` ## lint_shellcheck_aspect Factory function to create a ShellCheck linter aspect for shell scripts. ShellCheck is a static analysis tool for shell scripts that finds bugs and suggests improvements. ```starlark # tools/lint/linters.bzl load("@aspect_rules_lint//lint:shellcheck.bzl", "lint_shellcheck_aspect") load("@aspect_rules_lint//lint:lint_test.bzl", "lint_test") shellcheck = lint_shellcheck_aspect( binary = Label("@aspect_rules_lint//lint:shellcheck_bin"), config = Label("//:.shellcheckrc"), rule_kinds = ["sh_binary", "sh_library", "sh_test"], ) shellcheck_test = lint_test(aspect = shellcheck) # .shellcheckrc configuration example: # disable=SC2034,SC2086 # shell=bash # Run with fix mode (generates diff patches): # bazel lint //scripts:all --fix ``` ## lint_buildifier_aspect Factory function to create a Buildifier linter aspect for Starlark/Bazel files. Buildifier formats and lints BUILD, WORKSPACE, and .bzl files. ```starlark # tools/lint/linters.bzl load("@aspect_rules_lint//lint:buildifier.bzl", "lint_buildifier_aspect") load("@aspect_rules_lint//lint:lint_test.bzl", "lint_test") # Requires buildifier_prebuilt in MODULE.bazel: # bazel_dep(name = "buildifier_prebuilt", version = "8.5.1") buildifier = lint_buildifier_aspect( binary = Label("@buildifier_prebuilt//:buildifier"), warnings = "all", rule_kinds = ["bzl_library", "bzl_library_rule"], filegroup_tags = ["starlark", "lint-with-buildifier"], ) buildifier_test = lint_test(aspect = buildifier) # To lint BUILD.bazel and MODULE.bazel files, create a filegroup: # filegroup( # name = "starlark_files", # srcs = ["BUILD.bazel", "MODULE.bazel", "defs.bzl"], # tags = ["starlark"], # ) ``` ## lint_stylelint_aspect Factory function to create a Stylelint linter aspect for CSS, SCSS, Less, and other style files. ```starlark # tools/lint/linters.bzl load("@aspect_rules_lint//lint:stylelint.bzl", "lint_stylelint_aspect") load("@aspect_rules_lint//lint:lint_test.bzl", "lint_test") # First, declare stylelint binary in tools/lint/BUILD.bazel: # load("@npm//:stylelint/package_json.bzl", stylelint_bin = "bin") # stylelint_bin.stylelint_binary(name = "stylelint") stylelint = lint_stylelint_aspect( binary = Label("//tools/lint:stylelint"), config = Label("//:.stylelintrc.json"), ) stylelint_test = lint_test(aspect = stylelint) # .stylelintrc.json configuration example: # { # "extends": "stylelint-config-standard", # "rules": { # "indentation": 2, # "color-hex-case": "lower" # } # } ``` ## lint_test Factory function to create a lint test rule from a linter aspect. When the linter exits non-zero, the test fails and prints the linter output. This enables running linters via `bazel test`. ```starlark # tools/lint/linters.bzl load("@aspect_rules_lint//lint:lint_test.bzl", "lint_test") load("@aspect_rules_lint//lint:flake8.bzl", "lint_flake8_aspect") flake8 = lint_flake8_aspect( binary = Label("//tools/lint:flake8"), config = Label("//:.flake8"), ) # Create a test rule flake8_test = lint_test(aspect = flake8) # In BUILD files: # src/BUILD.bazel load("//tools/lint:linters.bzl", "flake8_test") py_library( name = "my_lib", srcs = ["my_lib.py"], ) flake8_test( name = "my_lib_flake8", srcs = [":my_lib"], expected_exit_code = 0, # Default: fail if linter reports issues ) # For testing that a linter correctly detects issues: flake8_test( name = "bad_code_flake8", srcs = [":bad_code"], expected_exit_code = 1, # Expect failure ) # Run tests: # bazel test //src:my_lib_flake8 # bazel test //... # Run all tests including lint ``` ## Configuring .bazelrc for Linting Configure Bazel to run linters during builds or via the `bazel lint` command (requires Aspect CLI). ```bash # .bazelrc # Option 1: Use with Aspect CLI's `bazel lint` command common:lint --aspects=//tools/lint:linters.bzl%eslint,//tools/lint:linters.bzl%ruff # Option 2: Fail build on lint violations build:strict --aspects=//tools/lint:linters.bzl%eslint build:strict --@aspect_rules_lint//lint:fail_on_violation # Enable colored output common --@aspect_rules_lint//lint:color # Enable debug mode for troubleshooting common --@aspect_rules_lint//lint:debug # Run: bazel build --config=strict //src:all # Or: bazel lint //src:all (with Aspect CLI) ``` ## Pre-commit Hook Integration Integrate formatting with git pre-commit hooks using either the pre-commit framework or a custom git hook. ```yaml # .pre-commit-config.yaml (using pre-commit.com) repos: - repo: local hooks: - id: aspect_rules_lint name: Format language: system entry: bazel run //:format files: .* ``` ```bash #!/usr/bin/env bash # githooks/pre-commit (custom git hook) # Run: git config core.hooksPath githooks git diff --cached --diff-filter=AM --name-only -z | xargs --null --no-run-if-empty bash -c ' if [ $# -gt 0 ]; then bazel run //:format -- "$@" if ! git diff --quiet -- "$@"; then echo "Some staged files were modified by the pre-commit hook." echo "Please stage the changes and try committing again:" git diff --stat -- "$@" exit 1 fi fi ' _ ``` ## Complete Multi-Language Setup Example A comprehensive example showing how to set up linting and formatting for a multi-language project. ```starlark # MODULE.bazel module(name = "my_project") bazel_dep(name = "aspect_rules_lint", version = "1.0.0") bazel_dep(name = "aspect_rules_js", version = "1.40.0") bazel_dep(name = "rules_python", version = "0.26.0") bazel_dep(name = "buildifier_prebuilt", version = "8.5.1") # tools/lint/linters.bzl load("@aspect_rules_lint//lint:eslint.bzl", "lint_eslint_aspect") load("@aspect_rules_lint//lint:ruff.bzl", "lint_ruff_aspect") load("@aspect_rules_lint//lint:shellcheck.bzl", "lint_shellcheck_aspect") load("@aspect_rules_lint//lint:buildifier.bzl", "lint_buildifier_aspect") load("@aspect_rules_lint//lint:lint_test.bzl", "lint_test") eslint = lint_eslint_aspect( binary = Label("//tools/lint:eslint"), configs = [Label("//:eslintrc")], ) eslint_test = lint_test(aspect = eslint) ruff = lint_ruff_aspect( binary = Label("@aspect_rules_lint//lint:ruff_bin"), configs = [Label("//:pyproject.toml")], ) ruff_test = lint_test(aspect = ruff) shellcheck = lint_shellcheck_aspect( binary = Label("@aspect_rules_lint//lint:shellcheck_bin"), config = Label("//:.shellcheckrc"), ) shellcheck_test = lint_test(aspect = shellcheck) buildifier = lint_buildifier_aspect( binary = Label("@buildifier_prebuilt//:buildifier"), ) buildifier_test = lint_test(aspect = buildifier) # tools/format/BUILD.bazel load("@aspect_rules_lint//format:defs.bzl", "format_multirun") format_multirun( name = "format", javascript = ":prettier", python = "@aspect_rules_lint//format:ruff", go = "@aspect_rules_lint//format:gofmt", starlark = "@aspect_rules_lint//format:buildifier", ) # BUILD.bazel (root) alias(name = "format", actual = "//tools/format") # .bazelrc common:lint --aspects=//tools/lint:linters.bzl%eslint,//tools/lint:linters.bzl%ruff,//tools/lint:linters.bzl%shellcheck,//tools/lint:linters.bzl%buildifier # Usage: # bazel run //:format # Format all files # bazel lint //... # Lint all targets (Aspect CLI) # bazel test //... # Run all tests including lint tests ``` ## Summary Aspect Rules Lint provides a comprehensive solution for integrating code quality tools into Bazel builds. The primary use cases include: running formatters on changed files via `bazel run //:format`, validating code style in CI with `bazel run //:format.check` or `bazel test`, presenting lint violations as terminal warnings with `bazel lint`, failing builds on violations using `--@aspect_rules_lint//lint:fail_on_violation`, and integrating with code review systems through SARIF output for automated review comments. The integration pattern follows a consistent structure: install tool binaries via your language's package manager (npm, pip, etc.) or use built-in multitool binaries, create aspect factory functions in `tools/lint/linters.bzl`, create format targets in `tools/format/BUILD.bazel`, configure `.bazelrc` with aspect flags, and optionally set up pre-commit hooks for local development. This approach allows teams to gradually adopt linting without fixing all existing violations, lint only new code changes, and leverage Bazel's incremental builds and remote caching for efficient CI pipelines.