The Problem

Bazel projects benefit from consistent code formatting. While Buildifier handles formatting when run manually through text editors, this approach lacks reliability across teams and automated processes. Code from various sources—scripts, contributors without Buildifier installed—can bypass formatting standards.

Continuous integration typically catches formatting issues, but this feedback arrives late in the development cycle. Wouldn’t it be great to get this feedback when building and testing your code before submitting it to the CI system?

Solution: bzlformat

bzlformat integrates formatting into the normal build workflow by providing Bazel rules that:

  • Format Starlark files using Buildifier
  • Test that formatted files exist in the workspace
  • Copy formatted files to source directories

Implementation Steps

1. Configure Your Workspace

Add to your WORKSPACE file:

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
    name = "cgrindel_bazel_starlib",
    sha256 = "fc2ee0fce914e3aee1a6af460d4ba1eed9d82e8125294d14e7d3f236d4a10a5d",
    strip_prefix = "bazel-starlib-0.3.2",
    urls = ["http://github.com/cgrindel/bazel-starlib/archive/v0.3.2.tar.gz"],
)

load("@cgrindel_bazel_starlib//:deps.bzl", "bazel_starlib_dependencies")
bazel_starlib_dependencies()

load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")
bazel_skylib_workspace()

Include Buildifier:

load("@buildifier_prebuilt//:deps.bzl", "buildifier_prebuilt_deps")
buildifier_prebuilt_deps()

load("@buildifier_prebuilt//:defs.bzl", "buildifier_prebuilt_register_toolchains")
buildifier_prebuilt_register_toolchains()

2. Update Root BUILD File

load("@cgrindel_bazel_starlib//bzlformat:defs.bzl",
    "bzlformat_missing_pkgs", "bzlformat_pkg")
load("@cgrindel_bazel_starlib//updatesrc:defs.bzl",
    "updatesrc_update_all")

bzlformat_pkg(name = "bzlformat")

bzlformat_missing_pkgs(name = "bzlformat_missing_pkgs")

updatesrc_update_all(
    name = "update_all",
    targets_to_run = [":bzlformat_missing_pkgs_fix"],
)

3. Add bzlformat_pkg to Every Package

Run:

bazel run //:update_all

4. Format and Test

# Format and copy files back
bazel run //:update_all

# Test including formatting checks
bazel test //...

5. Optional CI Integration

bazel run //:bzlformat_missing_pkgs_test

If failures occur, run the fix command and re-test.

How It Works

bzlformat_pkg creates three types of targets per package:

  1. Format targets - Build-time actions that format Starlark files (.bzl, BUILD, BUILD.bazel)
  2. Test targets - Diff tests comparing source files with formatted output; fails if differences exist
  3. Update target - Executable that copies formatted files back to source

bzlformat_missing_pkgs provides three executable targets:

  • find - Reports packages missing bzlformat declarations
  • test - Same query but fails if packages found (for CI)
  • fix - Automatically adds missing declarations

Conclusion

By embedding format checks into the development build cycle rather than deferring to CI, developers receive immediate feedback. This approach maintains code consistency while improving the developer experience through earlier problem detection.