In the 101 course, we created an empty Bazel repository using the init
command. In this course we will use the same repository to illustrate the concepts. If you didn’t select Go when you created your repository, then run aspect init
again.
Application code
Let’s start by creating a program we can run. In Go, there is a convention of placing these in a cmd
folder. Let’s create cmd/hello/main.go
with this basic content:
package main
import (
"net/http"
"nmyk.io/cowsay"
)
func main() {
http.HandleFunc("/", HelloServer)
http.ListenAndServe(":8080", nil)
}
func HelloServer(w http.ResponseWriter, r *http.Request) {
cowsay.Cow{}.Write(w, []byte("Hello, world!"), false)
}
Running the go
tool
To ensure we’re using the Bazel-managed version of the Go SDK rather than something you might happen to have installed on your computer, we replace go
commands with the executable @rules_go//go
target that comes from rules_go. To make this even easier to discover, our repository has this aliased at ./tools/go
. Under the covers, you can see that script is just running the rules_go target, and then ensuring the correct working directory.
If you prefer, you could alias the go
command in your terminal to run this script instead, so you don’t need to remember to run the correct one.
External dependencies
As you can see, our application uses the cowsay
third-party library. We’ll need to add this to our go.mod
file like we would in any Go project. We could use the go get
command, but this installs the library to a system location which differs from how Bazel manages dependencies. So instead we’ll use the mod tidy
command:
% ./tools/go mod tidy
This results in updating the go.mod
file, which is how Go provides for reproducible builds. If you open that file, you should see this line was added for you. (The version may differ of course, since our Go source file didn’t specify any constraint).
require nmyk.io/cowsay v1.0.2
For supply-chain security, we might be suspicious that the maintainers of this library could accidentally allow an attacker to re-tag this v1.0.2
and point it to malicious code. The go.sum
file provides the cryptographic hash of the library at the time we ran go mod tidy
, following the “Trust on First Use” principle.
Additionally, rules_go exposes an external repository where this library is built from sources. Thanks to the strict visibility feature of bzlmod, this dependency must be declared in MODULE.bazel
. Looking in that file, you’ll see this line was also added for you:
use_repo(go_deps, "io_nmyk_cowsay")
That’s it! We’ve got this library ready to use as a dependency of our app.
If you used earlier versions of Go with Bazel, you will notice this is significantly simpler to setup! Now that the go.mod
file is read directly, you no longer need go_repository
rules.
Generate BUILD files
Next we need a go_binary
target to make our application runnable. As we learned in the 101 course, we can use the bazel configure
command:
% bazel configure
Now look inside the cmd/hello/BUILD.bazel
file. You’ll see something like the following has been automatically added for you:
go_library(
name = "cmd_lib",
srcs = ["server.go"],
importpath = "example.com/bazel105/cmd",
visibility = ["//visibility:private"],
deps = ["@io_nmyk_cowsay//:go_default_library"],
)
go_binary(
name = "cmd",
embed = [":cmd_lib"],
visibility = ["//visibility:public"],
)
Aspect CLI provides the configure
command. If you are in a project using vanilla Bazel, you’ll have to follow the gazelle setup instructions, and build the Gazelle binary from source on each developer’s machine.
Run the application
The bazel run
command can be used with a label as a shorthand for “build the application and then spawn the resulting binary”. So we can now run:
% bazel run cmd/hello
The server starts up, and we can navigate to http://localhost:8080/ to see the website it serves.