Notes: go_module() is deprecated in Core3. This codelab teaches a practical workflow that uses standard Go tooling (go get / go mod) together with Puku to generate and maintain third-party go targets (go_repo).
go getgo.mod changes into Please with plz puku syncplz puku fmt (and use plz puku watch for live updates)go mod editgo install github.com/please-build/puku/cmd/puku@latest orgo get github.com/please-build/puku/cmd/pukuThe final result of running through this codelab can be found here for reference. If you really get stuck you can find us on gitter!
The easiest way to get started is from an existing Go module:
mkdir puku_sync && cd puku_sync
go mod init example_module
plz init --no_prompt
plz init plugin go
Define a valid Puku version number as a build configuration string in .plzconfig:
[BuildConfig]
puku-version = "1.17.0"
Uncomment and edit the following lines in your .plzconfig to set up please version:
[please]
version = 17.22.0
Configure a Please alias for Puku (optional but convenient):
[Alias "puku"]
Cmd = run //third_party/binary:puku --
PositionalLabels = true
Desc = A tool to update BUILD files in Go packages
With the alias, you can use plz puku instead of plz run //third_party/binary:puku.
Then download that version of Puku in third_party/binary/BUILD:
remote_file(
name = "puku",
url = f"https://github.com/please-build/puku/releases/download/v{CONFIG.PUKU_VERSION}/puku-{CONFIG.PUKU_VERSION}-{CONFIG.OS}_{CONFIG.ARCH}",
binary = True,
)
Configure the Go plugin to point at your go.mod (recommended). Create a repo-root BUILD with a filegroup for go.mod:
BUILD in repo root:filegroup(
name = "gomod",
srcs = ["go.mod"],
visibility = ["PUBLIC"],
)
.plzconfig:[Plugin "go"]
Target = //plugins:go
ModFile = //:gomod
This lets Puku use standard go get to resolve modules, then sync them into third_party/go/BUILD.
By default, Please looks for Go in the following locations:
/usr/local/bin:/usr/bin:/bin
If you installed Go elsewhere (e.g., via Homebrew on macOS, or a custom location), you must configure the path in .plzconfig.
First, find where your Go binary is located:
which go
Then add the path to .plzconfig. For example, if Go is at /opt/homebrew/bin/go:
[Build]
Path = /opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin
Or if it's at /usr/local/go/bin/go:
[Build]
Path = /usr/local/go/bin:/usr/local/bin:/usr/bin:/bin
Note: On Windows, use where.exe go to find the Go installation path.
From Go version 1.20 onwards, the standard library is no longer included by default with the Go distribution. You must install it manually:
GODEBUG="installgoroot=all" go install std
Let's add a new third-party dependency using go get and sync it with Puku.
First, let's create a simple Go program that uses a third-party library. Create a file src/hello/hello.go:
package main
import (
"fmt"
"github.com/google/uuid"
)
func main() {
id := uuid.New()
fmt.Printf("Generated UUID: %s\n", id.String())
}
Now add the dependency with go get:
go get github.com/google/uuid
Sync the changes to third_party/go/BUILD:
plz puku sync -w
This creates a go_repo() rule in third_party/go/BUILD for the uuid module. You may need to create the third_party/go/BUILD file if it doesn't exist.
Create src/hello/BUILD:
go_binary(
name = "hello",
srcs = ["hello.go"],
)
Now let Puku automatically add the dependency:
plz puku fmt //src/hello
Puku will update your BUILD file to include the dependency on //third_party/go:google-uuid (or the subrepo format).
Build and run your program:
plz run //src/hello
To update a module to a specific version:
GOTOOLCHAIN=local go get github.com/google/uuid@v1.6.0
plz puku sync -w
To update to the latest version:
GOTOOLCHAIN=local go get -u github.com/google/uuid
plz puku sync -w
After syncing, rebuild your targets to use the updated version.
Missing import error? If you see could not import ... (open : no such file or directory), the module providing that package is missing. Add it with:
go get <module-name>
plz puku sync -w
Missing subrepo error? If you see Subrepo ... is not defined, you need to add or migrate the module:
go get <module-name>
plz puku sync -w
Sometimes you need to prevent a module from being updated due to breaking changes or compatibility issues.
Use the exclude directive to prevent a specific version from being used:
go mod edit -exclude github.com/example/module@v2.0.0
plz puku sync -w
This prevents version v2.0.0 from being selected. Go will use the next highest non-excluded version.
To remove an exclusion:
go mod edit -dropexclude github.com/example/module@v2.0.0
plz puku sync -w
Use the replace directive to pin a module to a specific version:
go mod edit -replace github.com/example/module=github.com/example/module@v1.5.0
plz puku sync -w
This pins the module to v1.5.0 regardless of what other dependencies require.
To unpin (and upgrade at the same time):
go mod edit -dropreplace github.com/example/module
go get -u github.com/example/module
plz puku sync -w
Warning: Pinning modules can cause compatibility issues with other dependencies. Use sparingly and resolve as soon as possible.
Let's say a new version of uuid has introduced a breaking change. Pin it to a working version:
go mod edit -replace github.com/google/uuid=github.com/google/uuid@v1.3.0
plz puku sync -w
plz build //src/hello
Before removing a module, ensure it's not used anywhere in your codebase.
plz query revdeps //third_party/go:module_name --level=-1 | grep -v //third_party/go
If this returns no results, the module is safe to remove.
go_repo() target from third_party/go/BUILD:Open third_party/go/BUILD and delete the corresponding go_repo() rule.
go.mod and go.sum:go mod edit -droprequire github.com/example/module
go mod tidy
plz puku sync -w
Once you've added a module with go get and plz puku sync, you can use it in your code.
The easiest way is to let Puku handle dependencies automatically:
.go fileplz puku fmt //your/packagePuku will parse your imports and add the necessary dependencies to your BUILD file.
There are two ways to specify dependencies on third-party packages:
1. Subrepo convention (recommended):
go_library(
name = "mylib",
srcs = ["mylib.go"],
deps = [
"///third_party/go/github.com_google_uuid//",
],
)
The subrepo format is: ///third_party/go/
2. Install list:
Add packages to the install list on the go_repo() target:
go_repo(
name = "google-uuid",
module = "github.com/google/uuid",
version = "v1.6.0",
install = ["."], # Installs the root package
)
Then depend on it like:
go_library(
name = "mylib",
srcs = ["mylib.go"],
deps = ["//third_party/go:google-uuid"],
)
For active development, use watch mode to automatically update BUILD files as you code:
plz puku watch //src/...
This watches for changes to .go files and updates dependencies automatically.
plz puku fmt to keep dependencies up to dateplz test after adding/updating dependencies to catch issues earlyCongratulations! You now know how to manage Go third-party dependencies using go get and Puku.
plz help, and explore this rich set of commands!Otherwise, why not try one of the other codelabs!