Development Guide

See structure for the full directory map.

Installation

Install bazelisk as your bazel binary:

wget -O ~/tools/bazel https://github.com/bazelbuild/bazelisk/releases/download/v1.18.0/bazelisk-linux-amd64
chmod +x ~/tools/bazel
# Add ~/tools to PATH

Build and Test

bazel build //...                          # build everything
bazel test //...                           # run all tests
bazel test //path/to:target                # run a single test
bazel test //... --test_output=all         # show test output
bazel coverage //... --combined_report=lcov  # generate coverage

Linting and Formatting

./lint.sh --mode check   # check formatting (CI runs this)
./lint.sh --mode format  # auto-format code

CI

  • GitHub Actions: .github/workflows/

  • Bazel remote caching via BuildBuddy (requires API key in user.bazelrc)

  • Disk caching via GitHub’s cache action

  • Remote caching also available via nativelink

Language FAQs

Each language answers: how to lint/format, build, test, run, add dependencies, package, and release.

Bazel

# Regenerate BUILD files
bazel run //:gazelle

# Completely clean
bazel clean --expunge

# Avoid networked backends / remote caches (helpful on slow networks)
bazel build //... --config local

References: Bazel query language, bzlmod migration

Python

Style: 79-char line length (black + isort via pyproject.toml)

# Create virtualenv with all dependencies
bazel run //:create_venv

Adding / modifying external dependencies:

  1. Update requirements.in

  2. bazel run //:requirements.update — updates requirements_lock.txt

  3. bazel run //:gazelle_python_manifest.update — updates gazelle_python.yaml

Tests enforce that both update commands have been run after any change to requirements.in.

Excluding lines from coverage: # pragma: no cover (docs)

Rust

# Update cargo dependencies
CARGO_BAZEL_REPIN=1 bazel sync --only=crate_index

C++

Style: C++23, clang-format (.clang-format)

Use cc_test (from //bzl:cc.bzl) for all tests — see cpp_testing for details.

# Run buildozer / buildifier manually
bazel run //tools/buildozer -- <args>
bazel run //tools/buildifier -- <args>

ThreadSanitizer on Ubuntu 24.04:

sudo sysctl vm.mmap_rnd_bits=30

(reference)

JavaScript / Node

bazel run -- @pnpm//:pnpm --dir $PWD install --lockfile-only

Git LFS

Large binary files (firmware images, photos, archives) are tracked via Git LFS. The patterns are configured in .gitattributes.

The git-lfs binary is managed via rules_multitool and can be run directly from the repo root:

tools/git-lfs <args>
# or via bazel
bazel run @multitool//tools/git-lfs -- <args>

First-time setup

tools/git-lfs install   # installs LFS hooks into .git/

Common operations

tools/git-lfs track "*.bin"   # start tracking a new pattern (updates .gitattributes)
tools/git-lfs ls-files        # list LFS-tracked files in the current commit
tools/git-lfs status          # show LFS file status
tools/git-lfs pull            # download LFS files after a clone/fetch

Adding a new file type

  1. Add a pattern to .gitattributes (or use tools/git-lfs track "*.ext")

  2. Commit the updated .gitattributes

  3. Migrate any existing files: tools/git-lfs migrate import --include="*.ext"

Inspiration