Bazel for client/*
See Bazel at Sourcegraph for general bazel info/setup.
Tools
The sourcegraph client projects are setup to compile, bundle and test with Bazel. The tools used within Bazel include:
- Babel for ts\[x\] transpilation
- Webpack for bundling
- Jest, Mocha for testing
- esbuild for experimental bundling as well as internal tooling
- Node tools such as graphql-codegen for generating graphql schema types
The Bazel rulesets used to support these include:
See Aspect rules docs for more information on the Bazel rulesets used.
Targets
The primary Bazel targets have been configured roughly aligning with the pnpm workspace projects, while often composed of many sub-targets. The primary targets for client/*
pnpm projects are generated using bazel configure
. The primary targets include:
:{name}_pkg
for the npm package representing the pnpm project:test
for the Jest unit tests of the project:{name}_lib
for compiling non-test*.ts\[x\]
files:{name}_tests
for compiling unit test*.ts\[x\]
files
Other targets may be configured per project such as sass compilation, graphql schema generation etc. See client/*/BUILD.bazel
files for more details and examples.
The srcs
and deps
attributes of the prmiary targets are generated by bazel configure
and any manual changes to those attributes will normally be overriden. If manual files/deps must be specified they must end with a # keep
comment to ensure bazel configure
does not remove them. Currently typescript type-only imports are not supported by bazel configure
and must be manually added to the deps
attribute, see https://github.com/aspect-build/aspect-cli/issues/404.
Additional BUILD.bazel
files may exist throughout subdirectories and is encouraged to create many smaller independently cacheable targets.
Testing
All client tests (of all types such as jest and mocha) can be invoked by bazel test //client/...
or individual tests can be specified such as bazel test //client/common:test
or bazel test //client/web/src/end-to-end:e2e
. Jest tests can be debugged using bazel run --config=debug //client/common:test
.
Notes
Currently, it's impossible to use features that Babel will transpile, creating helper methods inside Puppeteer driver.page.evaluate
calls. E.g., the for-of
syntax transpiled by Babel creates a helper in the module's top-level scope and uses it in the evaluate
call. But since the contents of the evaluate
call are passed to the eval
function inside Puppeteer, it doesn't have the reference to the created helper and fails in the runtime. This is caused by the fact that we uniformly transform all TS files to JS using Babel in Bazel. We will develop an approach that would allow skipping the Babel transpilation step for files executed only in the node environment.
Bundling
The primary client/web
bundle targets are:
//client/web:bundle
//client/web:bundle-dev
//client/web:bundle-enterprise
Seeclient/web/BUILD
.
The client/web
devserver can be run using bazel run //client/web:devserver
Rule configuration
Most rules used throughout client/*
are macros defined in tools/*.bzl
to provide a consistent configuration used throughout the repo.
FAQ
I encountered "bazel configure" failures on CI, and I'm not sure how to debug and fix the issue. What steps should I follow to resolve this?
- Examine the Bazel output logs: Scroll through and parse the Bazel output logs to find any reported errors, particularly missing modules or type declarations.
- Compare local and CI environments: Run the same failing test locally (e.g.,
bazel test //client/web:web_lib_typecheck_test
) and compare the results with those on CI. This can help identify discrepancies between the two environments. - Check for missing types: If you see errors related to missing types or modules in the Bazel logs, it's possible that some required dependencies are missing from your Bazel build file. To fix this, manually add the missing dependencies to the
deps
attribute in your primary target and include a# keep
comment to preventbazel configure
from removing them. Be aware thatbazel configure
currently does not support TypeScript type-only imports, and these dependencies need to be added manually (see https://github.com/aspect-build/aspect-cli/issues/404). - Update the Bazel configuration: Run
bazel configure
to update your Bazel build configuration. Note that thesrcs
anddeps
attributes of the primary targets are generated bybazel configure
, and any manual changes to those attributes without the # keep comment will be overridden. - Leverage additional BUILD.bazel files: You can create additional
BUILD.bazel
files in subdirectories to break down your project into smaller, independently cacheable targets. This can help improve build performance and simplify your build configuration.
I want to execute a js binary with bazel. How do I do that?
Lets say I want to execute a js binary called rawr
. rules_js
generates Starlark APIs for all dependencies defined in package
.json, so to import the generated api for rawr
we can do:
load("@npm//:rawr/package_json.bzl", my_alias = "bin")
With the above statement rules_js
generated the following targets for rawr
:
rawr
for use withbazel build
(js_binary + js_run_binary internally)rawr_binary
for use withbazel run
(js_binary internally)rawr_test
for use withbazel test
(js_test internally)
See the generated macro implementation here for more details.
Cool? I mean we technically have rawr
now available but HTF do we use this? To create a build target using rawr
we can now do:
my_alias.rawr ( # js_binary attributes name = "rawr_event" srcs = ["earth.js], args = ["-event", "asteroid"], )
To create a test target using rawr we can do (note the _test suffix):
my_alias.rawr_test ( # js_test attributes name = "rawr_event_test" srcs = ["earth.js], args = ["-event", "asteroid", "-test"], )
So now we have two targets that will execute the rawr
js binary with different arguments:
bazel build //:rawr_event bazel test //:rawr_event_test
An example of a js binary being executed as a bazel test target is graphql-schema-linter
, whose definition can be found in cmd/frontend/graphqlbackend/BUILD.bazel
.
How do I build/test Bazel targets of a specific type for all client packages?
Using Bazel, you can build or test specific targets across all client packages. This can be done by leveraging Bazel's query
and test
commands.The test
command is used to build and run all test targets. However, with the help of query
, we can filter out the specific targets we want to build and test.
For example, to check all Typescript types across all client packages locally, use:
bazel test `bazel query 'attr("name", ".*_typecheck_test", //...)'`
bazel test
: Commands Bazel to build and run specified test targets.bazel query 'attr("name", ".*_typecheck_test", //...)'
: Sub-command that specifies the targets for test. It queries for all targets whose name ends with_typecheck_test
in all packages (//...
).
If you want to run tests for a specific package, replace //...
with the package path like //client/vscode...
.
bazel test `bazel query 'attr("name", ".*_typecheck_test", //client/vscode...)'`
How do I run ESLint on a client package?
Similar to the above, we can use bazel query to find all target names ending with _eslint
and test them:
bazel test `bazel query 'attr("name", ".*_eslint$", //client/wildcard/...)'`