Using Bazel on Gitlab CI

Brian Michalski
3 min readOct 7, 2019

--

When I work on personal projects it feels like I spend 30% of my time writing code and the other 70% figuring out various “DevOps” tasks like automated build & test pipelines and various configuration options. This DevOps stuff takes so much time because I’m often trying to glue together things people don’t often do — like today’s adventure using Bazel and GitLab CI.

“Now kiss” meme with bazel and gitlab.
Bazel and Gitlab sitting in a tree…

For the uninitialized, Bazel is a very robust build system helping you manage dependencies and produce consistent/reliable builds. With Bazel, developers write BUILD rules which tell the system how your libraries and dependencies should all be wired together and tested. Instead of running a command like go build you’d use bazel build to build your Go code.

To get Gitlab CI playing nicely with Bazel, we need to plug a few things into our .gitlab-ci.yml file.

Bazel provides a Docker image that can be used to build code without needing to download Java, Bazel, etc. By default, Gitlab runner execute script commands with the /bin/sh entry point which won’t work, so we need to override it:

default:
image:
name: l.gcr.io/google/bazel:latest
entrypoint: [""]

From here, it’s pretty easy to write build and test stages into your config like you normally would:

stages:
- build
- test

build:
stage: build
script:
- bazel version
- bazel build ...

test:
stage: test
script:
- bazel test ...

If you’re Docker Bazel rules to build a container_image and associated container_push, you can also use Gitlab to push your image:

push_listener:
stage: push
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- bazel run //directory:push

Running the docker login command first will write the login credentials for Gitlab’s registry into a config.json file that Bazel is smart enough to keep using.

Since each of these three jobs runs independently, there will be some duplication of work as each step re-builds a bunch of the same files. Luckily, Bazel has a caching feature that can be made to play nicely with Gitlab’s CI caching too.

First, we’ll need to tell Bazel to use a different directory for its disk cache. We can do this by dropping a line in our .bazelrc file. Since I only want to do this when running on Gitlab, we can slip this into a before_script like so:

default:
# image stuff here
before_script:
- echo "build --disk_cache=$CI_PROJECT_DIR/.cache" >> .bazelrc
before_script:
- echo "build --disk_cache=$CI_PROJECT_DIR/.cache" >> .bazelrc

Now, we just need to tell Gitlab to cache everything in that directory. I’m choosing to use a global cache shared by all the workers/jobs/etc. My gut says Bazel will be smart about any need to expire the cache.

cache:
key: bazel_cache
paths:
- .cache/

With that in place, your initial build step will still bit slower, but subsequent test or run (like used to push to a docker registry) should be much faster.

Here’s my final .gitlab-ci.yml:

default:
image:
name: l.gcr.io/google/bazel:latest
entrypoint: [""]
before_script:
- echo "build --disk_cache=$CI_PROJECT_DIR/.cache" >> .bazelrc
cache:
key: bazel_cache
paths:
- .cache/
stages:
- build
- test
- push
build:
stage: build
script:
- bazel version
- bazel build ...
test:
stage: test
script:
- bazel test ...
push_listener:
stage: push
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- bazel run //directory:push

Troubleshooting

Command ‘sh’ not found. Try ‘bazel help’. check that you set the entrypoint correctly for the image.

Error publishing registry.gitlab.com/bamnet/hedgehog:latest: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:590) has been fixed in more recent versions of io_bazel_rules_docker, until released consider swapping to a git_repository like:

git_repository(
name = "io_bazel_rules_docker",
commit = "d9c571c1f29d78ea9ceee3f7c52c14e9661fea55",
remote = "https://github.com/bazelbuild/rules_docker",
)

--

--

Brian Michalski

Engineer @ Google working on Maps + Cloud (@GMapsPlatform). Open Source [Digital Signage, Vehicle Tracking] Developer.