A tool to compare Bazel action query outputs with an interactive HTML report. This is a re-imagination of the Bazel aquery_differ.py in Go with enhanced visualization features.
- Compare Bazel action graphs between builds or git commits
- Interactive HTML reports with GitHub-style diff visualization
- Syntax-highlighted diffs (unified diff and go-cmp formats)
- Built-in web server with auto-open browser support
- Native Bazel rules for integration into your build
Add to your MODULE.bazel
:
bazel_dep(name = "bazel_difftools", version = "0.0.0")
Note: This module is not yet published to the Bazel Central Registry. For now, use an
archive_override
orgit_override
pointing to this repository.
Download a release artifact, or build from source:
git clone https://github.com/stackb/bazel_difftools.git
cd bazel_difftools
bazel build //cmd/aquerydiff
Load the rules in your BUILD.bazel
file:
load("@bazel_difftools//rules:defs.bzl", "aquery_diff", "aquery_git_diff")
Compare two aquery output files:
aquery_diff(
name = "compare_actions",
before = "before.pb",
after = "after.pb",
)
Attributes:
Attribute | Type | Default | Description |
---|---|---|---|
before |
label |
required | Baseline aquery file (.pb , .proto , .textproto , .json , .jsonproto ) |
after |
label |
required | Comparison aquery file (same format options) |
match |
string |
"output_files" |
Strategy to match before and after actions: "output_files" or "mnemonic" |
serve |
bool |
True |
Start web server to view report |
open |
bool |
True |
Automatically open browser to report |
unidiff |
bool |
False |
Generate unified diffs (can be slow for large actions) |
cmpdiff |
bool |
True |
Generate go-cmp diffs (fast, structural comparison) |
Run the comparison:
bazel run //path/to:compare_actions
Performance Note: The
unidiff
attribute defaults toFalse
because generating unified diffs can be prohibitively slow for large actions with many inputs/outputs. Thecmpdiff
format (enabled by default) is much faster and provides good structural comparison for most use cases. Only enableunidiff
if you need the traditional unified diff format and are willing to wait for the additional processing time.
Choosing a Match Strategy:
The match
attribute determines how actions are paired between the before and
after builds:
-
output_files
(default): Actions are matched by their output file paths. Use this when comparing the same target across different commits or configurations. This is the most common use case and ensures you're comparing the exact same action that produces the same outputs. -
mnemonic
: Actions are matched by their mnemonic (action type, e.g., "GoCompile", "CppCompile"). Use this when comparing different targets that use similar build rules. For example, comparing//old/pkg:binary
vs//new/pkg:binary
where both arego_binary
targets but produce different output paths. This helps identify how the same type of action differs between targets.
Example using mnemonic matching:
aquery_diff(
name = "compare_go_binaries",
before = "old_binary.pb",
after = "new_binary.pb",
match = "mnemonic", # Compare by action type instead of output path
)
Compare aquery outputs between git commits:
aquery_git_diff(
name = "git_compare",
before = "main",
after = "feature-branch",
target = "//my/package:target",
)
Attributes:
Same as aquery_diff
, plus:
Attribute | Type | Default | Description |
---|---|---|---|
target |
string |
required | Bazel target to aquery (e.g., //pkg:binary , deps(...) ) |
bazel |
string |
"bazel" |
Path to bazel executable |
before |
string |
required | Git commit/branch/tag for baseline |
after |
string |
required | Git commit/branch/tag for comparison |
This rule will:
- Check for uncommitted changes (fails if found)
- Checkout
before
commit and runbazel aquery
- Checkout
after
commit and runbazel aquery
- Restore original commit
- Generate comparison report
Generate aquery files using Bazel:
# Binary proto format (recommended for large graphs)
bazel aquery //pkg:target --output=proto > before.pb
# Text proto format (human-readable)
bazel aquery //pkg:target --output=textproto > before.textproto
# JSON proto format
bazel aquery //pkg:target --output=jsonproto > before.json
Supported formats: The tool automatically detects format based on file extension:
- Binary:
.pb
,.proto
- Text:
.textproto
- JSON:
.json
,.jsonproto
Run the comparison:
aquerydiff \
--before before.pb \
--after after.pb \
--report_dir ./output \
--serve \
--open
CLI Flags:
--before
- Path to baseline aquery file--after
- Path to comparison aquery file--report_dir
- Directory to write HTML report--match
- Matching strategy:output_files
(default) ormnemonic
--serve
- Start web server (default: true)--open
- Open browser automatically (default: true)--unidiff
- Generate unified diffs (default: false)--cmpdiff
- Generate go-cmp diffs (default: true)
Note: The report title is automatically derived from the most common target in the action graph.
The HTML report shows:
- Actions only in before - Removed actions
- Actions only in after - New actions
- Non-equal actions - Actions with changes
- Equal actions - Unchanged actions
Each action displays:
- Mnemonic (action type)
- Output files
- Links to before/after JSON/textproto representations
- Colorized diffs (unified and/or go-cmp format)
See the examples/simple directory for working examples using both rules.
Contributions welcome! Please open an issue or pull request.
Apache 2.0