Skip to main content
rbs builds Go with a hermetic, downloaded toolchain — no Go installation on the host is required. Compilation always runs offline: third-party modules are fetched once into a shared workspace cache (verified against your committed go.sum) or read from a committed vendor/ tree, and the compiler itself can never reach the network. The Go rules live in two modules:

Declaring the Go toolchain

Declare the toolchain once in your workspace’s WORKSPACE.rbs. rbs downloads the matching Go SDK for your platform on first build and reuses it from a shared store afterwards.
1

Declare the toolchain in WORKSPACE.rbs

2

Build a Go target

The first build downloads the Go SDK; subsequent builds are fully offline.
go_toolchain accepts:
The Go rules never auto-download a different Go version mid-build. If a go.mod declares a go directive newer than the workspace toolchain, the build fails with the Go tool’s own version error — bump version in WORKSPACE.rbs instead.
If no Go toolchain is declared, the rules fall back to whatever go is on your PATH. That works for quick experiments but is not hermetic — declare the toolchain for anything you share or ship.

Rules

go_binary

Builds a Go main package into a runnable binary. Sources (plus go.mod/go.sum for module builds) are staged into an isolated build workspace; the build runs with the network disabled. A typical service target uses a standard Go module layout — a thin main under cmd/, everything else under internal/:

go_library

Packages Go sources under a name so other targets can depend on them. It does not compile a separate archive — the consuming go_binary or go_test compiles all sources together.
Within a single Go module, the simplest pattern is to glob the module’s sources directly into each go_binary/go_test target (as in the go_binary example above) — the Go compiler resolves package imports through the module path.

go_test

Runs go test hermetically over your module’s packages, with the same offline module resolution as go_binary. It is also the coverage entry point for rbs coverage.
Stage every file your tests read at runtime. If tests load fixtures such as SQL migrations, add them to srcs (e.g. + glob(["migrations/*.sql"])) so they exist in the isolated test workspace.

go_deps_consistency

An opt-in gate for workspaces with multiple Go modules (one go.mod per service). It fails the build when two modules require the same dependency at different versions, naming every module, version, and file. Skip it if your workspace uses a single shared go.mod — drift is then impossible by construction.
When a conflict is found, the build fails with a fix-it message: align the versions (go get <module>@<version> && go mod tidy in each service) or list the module in exempt with a reason.

go_proto_library

Generates Go protobuf/gRPC code from .proto sources using the hermetic protoc and pinned protoc-gen-go / protoc-gen-go-grpc plugins. Generated .go files are written into the source tree (stamped DO NOT EDIT) so they can be committed next to the code that imports them — go_binary/go_test build from committed sources, so generated code must live beside them. Requires proto_toolchain() in WORKSPACE.rbs.
Re-run the target (rbs build //proto/orders:go) after editing the .proto to refresh the committed stubs; catch drift in CI by regenerating and diffing.

Third-party dependencies

Go dependencies are declared exactly as Go expects — in your module’s go.mod and go.sum — and rbs resolves them hermetically. Compilation always runs with module fetching disabled, so dependencies must be on disk before the compiler starts. Which mode you are in follows from what you commit:
Commit go.mod + go.sum (and list both in srcs), with no vendor/ tree. In a single network-enabled staging step, rbs downloads modules from the Go module proxy into a shared per-workspace cache, verifying every download against your committed go.sum. The build and all future builds then run offline against that cache.If go.sum is missing entries, the build fails and tells you to run go mod tidy and commit the result — incomplete checksums are never silently self-healed.
Private modules: downloads come from the module proxy only — there is no fallback to git or VCS credentials, so builds stay reproducible on any machine. Point the RBS_GOPROXY environment variable at an internal module proxy to resolve private modules.

Testing and coverage

Run tests with rbs test:
Enforce coverage thresholds with rbs coverage:
rbs coverage runs the tests with coverage instrumentation across all staged packages — untested packages count as 0% instead of silently dropping out — converts the Go cover profile to lcov, and fails the target if line coverage falls below min_line_coverage.
rbs test does not enforce coverage thresholds — it prints a reminder when a target declares them. Gate coverage in CI with rbs coverage.
Go coverage measures statements, so go_test exposes a line coverage threshold only — there are no branch or function thresholds that could never be measured.
Use coverage_exclude sparingly and only for machine-generated files (for example "*.pb.go" from go_proto_library) — excluded files disappear from both sides of the ratio, and the coverage log names every active exclusion so the narrowed denominator is never silent.

Cross-compilation

go_binary cross-compiles per target via the goos and goarch attributes — the standard workflow for packaging Linux images from a macOS workspace:
Rules of the road:
  • Two targets that differ only in goos/goarch are cached and built independently — they can never reuse each other’s output.
  • cgo = True cannot be combined with goos/goarch: cross-compiling CGO would need a target C toolchain, which rbs does not provide.
  • A cross-compiled binary is for packaging and deployment, not for running locally. Invoking it on a mismatched host prints a clear explanation instead of the kernel’s cryptic exec format error.
  • Container images stamp their os/arch from the packaged binary itself, so packaging a host-platform binary into a Linux-stamped image fails loudly rather than producing a lying image.

Caching

Go builds share one module cache and one build cache per workspace, so each dependency version downloads once and packages compile incrementally across all targets; completed build actions are additionally cached in the shared rbs store, so unchanged targets are not rebuilt at all.