Using Bazel on Gitlab CI

Brian Michalski
Oct 7 · 3 min read

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.
“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

Written by

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

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade