Create Go Monorepo with Go-modules and Bazel
Hi, everyone!
In this article, I wanna show how we can create monorepo with Golang and Bazel.
Requirements:
- Go Modules — we wanna use
go mod
for managing dependencies - Shared codebase — we wanna reuse our code in different application
- Test — we wanna run tests on single/all applications
- Build — we wanna build every application separately
These requirements go beyond the definition of monorepo. Okay, let's start.
1. Init go modules
We need to create go mod in our folder:
go mod init monorepo
After this command, we can get go dependence on our project.
2. Create for example two application
Let’s create a folder packages
that folder will have all our applications.
mkdir packages
cd packages
mkdir main_app
mkdir second_app
cd main_app && touch main.go && cd ../
cd second_app && touch main.go && cd ../
mkdir shared && touch shared.go - that file for shared code
For example, our application will be use gin like HTTP server:
go get github.com/gin-gonic/gin
3. Create a workspace for our monorepo
In the root folder, we need to create WORKSPACE file with content:
And BUILD file with content:
After we need setup Bazel to use dependence from go.mod
with command:
bazel run //:gazelle -- update-repos -from_file=go.mod
That command will be updating our WORKSPACE file with dependencies from go.mod
We finish setup our workspace after that we need to set up our applications.
4. Create a build for applications
Every application in the application folder should have BUILD like an entry point for Bazel
Here we have two sections:
- go_binary
- go_test
Section go_binary
This section has some field full list you can find in the link. I will just describe some of them:
“name” — the name for Bazel, for deps and Bazel resolver
“srcs” — entry points for modules, will be like artifacts
“importpath” — how we should import this module in our applications
“deps” — an array of dependencies
Section go_test
“name” — for the run test
“srcs” — entry points for tests
Let's add some shared content
For creating an application like real life I add a router in both applications with similar content
If you see that package using some dependencies from a shared folder.
And lets create BUILD file for that package:
That file has a section ‘go_library’
Section go_library
“name” — the name for Bazel, for use in dep
“srcs” — the entry point for lib
“importpath” — path for import outside(Bazel will be resolved that alias like webpack in nodejs)
“visibility” — visibility of the module in Bazel
“deps” — dependencies, in this example we have dep from “@com_github_gin_gonic_gin” — it is the internal name of gin, you can get from WORKSPACE file and from our shared handler.
5. Create a shared folder and build
In the previous step, we have a reference on a shared folder.
I create a shared handler for gin like that:
And build for package:
As we see gin in dep too.
I will skip the part in creating code for the second application because it is totally the same. Full code you can find here
6. Let's build and test our applications
For build main application we can use the command:
bazel build //packages/main_app:main_app
That command will be build application and show the path to bin file
For build second application you can use:
bazel build //packages/second_app:second_app
Test
Single application test:
bazel test //packages/second_app/...:all
All applications test:
bazel test //packages/...:all
Dep graph
Bazel can create dep graph with the command:
bazel query 'allpaths(packages/...,//packages/shared/handlers/health:health)' --output graph | dot -Tpng > dep.png
That command will be shown all packages who dependent on health module.
7. Enjoy!
In this example, I showed the basis for creating a monorepo in Go, if you are interested, I will continue the article and tell how to use for example protobuf or general static files in monorepo.