bazel_rules_detekt
v0.8.2.2
published 1 month ago
31 stars
10 forks
3 watchers
Apache License 2.0
public
1 assets
2,042,190 downloads
45 KB
v0.8.2.2
August 25, 2026
[expand for release notes]

bazel_rules_detekt

The Detekt (a Kotlin static analysis tool) integration for the Bazel build system.

Features

  • configuration and baseline files;
  • HTML, text, XML, Markdown, and SARIF reports;
  • plugins;
  • customizable Detekt version;
  • persistent workers support;
  • baseline generation via detekt_create_baseline;
  • configuration options via attributes.

Compatibility

bazel_rules_detekt Default Detekt Kotlin compiler Max language_version Min JDK Max tested JDK Bazel
v0.8.1.9 – v0.8.2.3 1.23.8 2.0.21 2.0 8 21 8.x – 9.x
v0.8.1.3 – v0.8.1.8 1.23.5 1.9.22 1.9 8 17 7.x – 9.x
v0.8.1 – v0.8.1.2 1.23.1 1.9.0 1.9 8 17 6.x
v0.7.0 1.22.0 — — 8 — 5.x
v0.6.0 – v0.6.1 1.19.0 – 1.21.0 — — 8 — 5.x
v0.4.0 – v0.5.0 1.15.0 — — — — 4.x
v0.3.0 1.7.4 — — — — 3.x
v0.1.0 – v0.2.0 1.2.0 — — — — 1.x

For detailed per-Detekt-version Kotlin and JDK compatibility, see the Detekt compatibility table.

Note: The Kotlin compiler bundled with Detekt determines which language_version values are valid — setting it higher than what the bundled compiler supports will cause Detekt to fail. The default Detekt version can always be overridden — see Detekt Version.

Note: Detekt 2.x is currently in alpha. The default is 2.0.0-alpha.6; the compatibility suite also validates a Detekt 1.23.8 override.

Note: JDK 25 and above are not supported with Detekt 1.23.x. The bundled Kotlin compiler performs a hard version check that fails on JDK 25+. This is resolved in the Detekt 2.x series.

Note: The wrapper reflects over the Detekt 1.x and 2.x CLI entry points during this alpha migration. Detekt 1.x compatibility will be deprecated after Detekt 2.x stabilizes and removed in a subsequent breaking release.

The project is developed and tested against Bazel 8 and 9 with Bzlmod.

Usage

Refer to GitHub releases for the version and the SHA-256 hashsum.

MODULE.bazel Configuration

bazel_dep(name = "rules_detekt", version = "...")

BUILD Configuration

Once declared in the MODULE.bazel file, the rules can be loaded in the BUILD file.

Rules

detekt

detekt is a regular Bazel build rule. When Detekt finds violations, the build action itself fails, stopping bazel build immediately with an error. This makes it behave like a compiler error — violations block the build.

load("@rules_detekt//detekt:defs.bzl", "detekt")

detekt(
    name = "my_detekt",
    srcs = glob(["src/main/kotlin/**/*.kt"]),
)
$ bazel build //mypackage:my_detekt

detekt_test

detekt_test is a Bazel test rule. The build action always succeeds (even when violations are found), and Bazel then runs a test script that reads the real Detekt exit code and prints findings to the test output. Violations cause the test to fail rather than the build action.

load("@rules_detekt//detekt:defs.bzl", "detekt_test")

detekt_test(
    name = "my_detekt",
    srcs = glob(["src/main/kotlin/**/*.kt"]),
)
$ bazel test //mypackage:my_detekt

Because it is a test target, it is included in bazel test //... alongside your unit tests, and it supports standard Bazel test flags such as --test_output=all.

detekt vs detekt_test

detekt detekt_test
Bazel rule type build rule test rule
Run with bazel build bazel test
Included in bazel build //... bazel test //...
Violation behaviour build action fails test fails; build action always passes
Text report printed when action fails printed to test output when test fails
Result caching yes yes
Bazel test flags n/a yes (--test_output, etc.)

Use detekt when you want violations to block builds the same way a compiler error does. Use detekt_test when you want Detekt to run alongside your test suite and report results through the test framework.

detekt_create_baseline

detekt_create_baseline is an executable rule that generates or updates a Detekt baseline file. The baseline suppresses existing findings so that only new violations fail the build going forward.

load("@rules_detekt//detekt:defs.bzl", "detekt_create_baseline")

detekt_create_baseline(
    name = "my_detekt_baseline",
    srcs = glob(["src/main/kotlin/**/*.kt"]),
    baseline = "detekt/baseline.xml",  # path where the baseline will be written
)
$ bazel run //mypackage:my_detekt_baseline

Running this target writes the baseline XML file into your source tree under $BUILD_WORKING_DIRECTORY. If baseline is not specified, the output path defaults to {package}/default_baseline.xml.

Once the baseline file exists, reference it in your analysis target:

detekt_test(
    name = "my_detekt",
    srcs = glob(["src/main/kotlin/**/*.kt"]),
    baseline = "detekt-baseline.xml",
)

Configuration Options

All three rules share the same configuration options. In addition to srcs, most attributes correspond directly to Detekt CLI flags and pass them through when explicitly set.

cfgs, plugins, build_upon_default_config, disable_default_rulesets, jvm_target, language_version, max_issues, fail_on_severity, and parallel are configured on reusable detekt_config targets. The executable wrapper and a default_config reference are configured on detekt_toolchain. srcs, deps, baseline, excludes, includes, all_rules, auto_correct, base_path, config_resource, is_android, and report options remain rule-level settings.

max_issues is the failure policy for Detekt 1.x. Detekt 2.x uses fail_on_severity (Never, Info, Warning, or Error). Both policies cannot be active on the same config; inactive values such as max_issues = -1 may coexist with the other policy. Choose the active option that matches the selected Detekt major version.

See the rule attributes, config attributes, and toolchain attributes.

Reports

A plain-text report ({name}_detekt_report.txt) is always generated. Detekt 1.x writes its native text report; Detekt 2.x writes captured console output to the same artifact. xml_report keeps the same output name and maps to Detekt 2.x's Checkstyle report ID. Other formats are opt-in.

Advanced Configuration

Detekt Version

The default bundled version is 2.0.0-alpha.6. To override it (including the supported Detekt 1.23.8 compatibility runtime):

MODULE.bazel Configuration

detekt = use_extension("@rules_detekt//detekt:extensions.bzl", "detekt")
detekt.detekt_version(
    version = "...",
    sha256 = "...",
)

use_repo(detekt, "detekt_cli_all")

To download Detekt from a custom location (e.g. an internal mirror), use the url_templates parameter:

detekt = use_extension("@rules_detekt//detekt:extensions.bzl", "detekt")
detekt.detekt_version(
    version = "...",
    sha256 = "...",
    url_templates = [
        "https://my-mirror.example.com/detekt/detekt-cli-{version}-all.jar",
    ],
)

use_repo(detekt, "detekt_cli_all")

To download Detekt from a custom location (e.g., an internal mirror), use the url_templates parameter:

detekt = use_extension("@rules_detekt//detekt:extensions.bzl", "detekt")
detekt.detekt_version(
    version = "...",
    sha256 = "...",
    url_templates = [
        "https://my-mirror.example.com/detekt/detekt-cli-{version}-all.jar",
    ],
)

use_repo(detekt, "detekt_cli_all")

Each template may contain {version} which will be replaced with the version string.

Analysis Configuration and Toolchains

detekt_config defines a reusable analysis profile. Multiple profiles can coexist and share the same executable toolchain; they do not need to be registered. Select a profile with the rule's config attribute:

load("@rules_detekt//detekt:config.bzl", "detekt_config")
load("@rules_detekt//detekt:defs.bzl", "detekt_test")

detekt_config(
    name = "strict",
    cfgs = ["detekt.yml"],
    build_upon_default_config = True,
    fail_on_severity = "Error",
    jvm_target = "11",
)

detekt_config(
    name = "lenient",
    fail_on_severity = "Never",
)

detekt_test(
    name = "strict_analysis",
    srcs = glob(["src/main/kotlin/**/*.kt"]),
    config = ":strict",
)

detekt_test(
    name = "legacy_analysis",
    srcs = glob(["legacy/**/*.kt"]),
    config = ":lenient",
)

An explicit config replaces the entire default profile, including empty lists, false booleans, and ordinary attribute defaults. No individual options are inherited or merged. If config is omitted or resolves to None in a select(), the selected toolchain's default_config is used. The bundled default config preserves the existing defaults, including JVM target 1.8.

Configuration labels, YAML inputs, and plugin JARs use the target configuration, so their select() expressions describe the code being analyzed. The wrapper is built in the execution configuration. Plugin JARs are supplied artifacts; callers are responsible for making them compatible with the runtime and execution platform.

detekt_toolchain selects the executable wrapper and can reference a repository-wide default profile:

load("@rules_detekt//detekt:toolchain.bzl", "detekt_toolchain")

detekt_toolchain(
    name = "detekt_default_impl",
    default_config = ":strict",
)

toolchain(
    name = "detekt_registered",
    toolchain = ":detekt_default_impl",
    toolchain_type = "@rules_detekt//detekt:toolchain_type",
)

Register it in MODULE.bazel:

register_toolchains("//:detekt_registered")

Targets without an explicit detekt_toolchain use Bazel's registered toolchain resolution. Register one intended default; if multiple compatible toolchains are registered, Bazel's registration priority/order decides which wins, not their names. A target can still select an implementation directly with detekt_toolchain = ":detekt_default_impl"; this is the implementation label, not the native toolchain() registration wrapper. An explicit config takes precedence over either selected toolchain's default.

The wrapper is an exec-configured java_binary. Configure its JVM options through Bazel, for example in .bazelrc:

build --host_jvmopt=-Xmx512m

Profiles configure the selected runtime; they do not select a Detekt version. To use the supported Detekt 1.23.8 override, configure that version separately and use its matching 1.x plugins and max_issues policy.

Migrating analysis options

cfgs, plugins, build_upon_default_config, disable_default_rulesets, jvm_target, language_version, max_issues, fail_on_severity, and parallel belong on detekt_config, not on detekt_toolchain, detekt, detekt_test, or detekt_create_baseline.

Move these options into a detekt_config target and pass its label using the rule's config attribute. If migrating a custom toolchain, leave detekt_wrapper on the toolchain and point its default_config at the new profile. Define separate profiles when targets need different settings; there is no per-option override.

The selected runtime determines which failure policy is valid: use max_issues with Detekt 1.23.8 and fail_on_severity with Detekt 2.0.0-alpha.6. JVM and Kotlin language versions are also validated by the selected runtime, and plugins must be built for the same Detekt major version.

Plugins

The config's plugins attribute accepts .jar file labels, including generated outputs, and targets or filegroups that contain only JAR files. JavaInfo is not required, and transitive dependencies are not automatically collected. Non-JAR files are rejected.

Supply ready-to-load plugin JARs containing their required dependencies and META-INF/services entries. A deploy JAR is useful for plugins with additional dependencies; exclude dependencies already supplied by Detekt, including its API. Callers must ensure the JAR's bytecode, Detekt API version, and any native contents are compatible with the selected runtime and execution platform. Generated JARs are built in the target configuration, with no execution transition applied by detekt_config.

Maven artifact (e.g., the formatting rule set):

maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
maven.install(
    artifacts = [
        "dev.detekt:detekt-rules-ktlint-wrapper:2.0.0-alpha.6",
    ],
)
use_repo(maven, "maven")
load("@rules_detekt//detekt:config.bzl", "detekt_config")

load("@rules_detekt//detekt:defs.bzl", "detekt_test")

detekt_config(
    name = "my_detekt_config",
    plugins = ["@maven//:dev_detekt_detekt_rules_ktlint_wrapper"],
)

detekt_test(
    name = "my_detekt",
    srcs = glob(["src/main/kotlin/**/*.kt"]),
    config = ":my_detekt_config",
)

For a Detekt 1.x override, use the matching legacy coordinate instead: io.gitlab.arturbosch.detekt:detekt-formatting:1.23.8. Plugin binaries are not interchangeable between majors because their public API packages differ (io.gitlab.arturbosch.detekt.api versus dev.detekt.api).

Custom local plugin built with rules_kotlin:

load("@rules_detekt//detekt:config.bzl", "detekt_config")

load("@rules_java//java:defs.bzl", "java_binary")
load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library")
load("@rules_detekt//detekt:defs.bzl", "detekt_test")

kt_jvm_library(
    name = "my_custom_rules",
    srcs = glob(["src/main/kotlin/**/*.kt"]),
    resources = glob(["src/main/resources/**"]),
    deps = ["@detekt_cli_all//jar"],
)

java_binary(
    name = "my_custom_rules_plugin",
    create_executable = False,
    deploy_env = ["@rules_detekt//detekt/wrapper:bin"],
    runtime_deps = [":my_custom_rules"],
)

detekt_config(
    name = "my_detekt_config",
    plugins = [":my_custom_rules_plugin_deploy.jar"],
)

detekt_test(
    name = "my_detekt",
    srcs = glob(["src/main/kotlin/**/*.kt"]),
    config = ":my_detekt_config",
)

The deploy_env excludes dependencies supplied by the bundled Detekt wrapper. If a plugin needs no additional runtime dependencies, its ordinary JAR (for example :my_custom_rules.jar) is sufficient. Existing JavaInfo targets can still be used if their default outputs contain only the required JARs.

Configuration File

Pass one or more Detekt YAML configuration files via the config's cfgs attribute. Files must use the .yml extension. You may pass raw file labels or filegroup targets:

load("@rules_detekt//detekt:config.bzl", "detekt_config")

detekt_config(
    name = "my_detekt_config",
    cfgs = [":detekt.yml"],
)

detekt_test(
    name = "my_detekt",
    srcs = glob(["src/main/kotlin/**/*.kt"]),
    config = ":my_detekt_config",
)

To extend Detekt's built-in defaults rather than replace them, set build_upon_default_config = True on the config:

load("@rules_detekt//detekt:config.bzl", "detekt_config")

detekt_config(
    ...
    build_upon_default_config = True,
    ...
)

JVM Target

Use jvm_target to set the JVM bytecode target version that matches what was used during compilation. Set it on the config (default: 1.8):

load("@rules_detekt//detekt:config.bzl", "detekt_config")

detekt_config(
    name = "my_detekt_config",
    jvm_target = "11",
)

detekt_test(
    name = "my_detekt",
    srcs = glob(["src/main/kotlin/**/*.kt"]),
    config = ":my_detekt_config",
)

Language Version

Detekt will report errors for any language features introduced after the specified version if language_version is specified on the config:

load("@rules_detekt//detekt:config.bzl", "detekt_config")

detekt_config(
    name = "my_detekt_config",
    language_version = "2.0",
)

detekt_test(
    name = "my_detekt",
    srcs = glob(["src/main/kotlin/**/*.kt"]),
    config = ":my_detekt_config",
)

Type Resolution

Type resolution enables more advanced static analysis by giving Detekt access to the full compilation classpath, including return types, nullability, and symbol information — capabilities that match those of the Kotlin compiler itself. Rules requiring it are annotated with @RequiresFullAnalysis in Detekt's source.

Provide the compile dependencies through rule-level deps and set config jvm_target and language_version to match the compilation settings of your project. With Detekt 2.x, a non-empty deps automatically selects full analysis; the Detekt 1.x runtime keeps its existing classpath behavior.

load("@rules_detekt//detekt:config.bzl", "detekt_config")

detekt_config(
    name = "my_detekt_config",
    jvm_target = "11",
    language_version = "2.0",
)

detekt_test(
    name = "my_detekt",
    srcs = glob(["src/main/kotlin/**/*.kt"]),
    deps = [":my_compiled_library"],
    config = ":my_detekt_config",
)

Reports

By default, Detekt generates a text report internally (used for console output). To export reports as build outputs, enable them explicitly:

detekt_test(
    name = "my_detekt",
    srcs = glob(["src/main/kotlin/**/*.kt"]),
    txt_report = True,   # {target_name}_detekt_report.txt
    html_report = True,  # {target_name}_detekt_report.html
    xml_report = True,   # {target_name}_detekt_report.xml  (Checkstyle format, compatible with SonarQube)
    md_report = True,    # {target_name}_detekt_report.md
    sarif_report = True, # {target_name}_detekt_report.sarif
)

Any combination of reports may be enabled.