Setup#
Go’s toolchain is famously self-contained, one binary
(go) installs, builds, tests, formats, and cross-compiles.
The operator’s setup problem is which Go to install and where
modules and binaries land.
Install#
1. Pick an installer.
Official installer from https://go.dev/dl/. The reference; installs to
/usr/local/go.Distro packages (
apt install golang-go,dnf install golang). Lags behind upstream; usable.gvm or g for per-user multiple-version management.
mise / asdf for cross-language version managers.
# Official tarball (Linux x86_64)
$ wget https://go.dev/dl/go1.23.linux-amd64.tar.gz
$ sudo rm -rf /usr/local/go
$ sudo tar -C /usr/local -xzf go1.23.linux-amd64.tar.gz
2. Set up the environment.
# ~/.profile or ~/.bashrc
$ export PATH=$PATH:/usr/local/go/bin
$ export GOPATH=$HOME/go
$ export PATH=$PATH:$GOPATH/bin
3. Verify.
$ go version
$ go env GOROOT GOPATH
Setup project#
1. Initialise a module.
$ mkdir my-tool && cd my-tool
$ go mod init github.com/operator/my-tool
$ git init && echo /my-tool > .gitignore
2. Add dependencies.
$ go get github.com/spf13/cobra@latest
$ go get -u ./... # update all
$ go mod tidy # prune unused
3. Lay out the source.
my-tool/
├── go.mod
├── go.sum
├── main.go
├── cmd/
│ └── my-tool/
│ └── main.go # entrypoint (multi-binary repos)
├── internal/ # private packages
│ ├── scan/
│ │ └── scan.go
│ └── report/
│ └── report.go
└── pkg/ # public packages (optional)
internal/ is special, packages there can only be imported by
the same module. The operator puts everything not designed for
reuse under internal/.
4. Build, run, test.
$ go run . # run main package in cwd
$ go build -o my-tool . # produce binary
$ go test ./... # run tests
$ go test -race ./... # race detector
Cross-compile#
Go’s headline trick. One command, every target.
$ GOOS=linux GOARCH=amd64 go build -o my-tool-linux-amd64
$ GOOS=linux GOARCH=arm64 go build -o my-tool-linux-arm64
$ GOOS=darwin GOARCH=arm64 go build -o my-tool-darwin-arm64
$ GOOS=windows GOARCH=amd64 go build -o my-tool.exe
For static binaries with no glibc dependency, add
CGO_ENABLED=0.
$ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -ldflags="-s -w" -o my-tool-static .
Common Tasks#
Run without writing a binary.
$ go run .
$ go run ./cmd/my-tool
Install a Go-built tool to ``$GOPATH/bin``.
$ go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
Vendor dependencies into the repo.
$ go mod vendor
Verify the module graph is clean.
$ go mod verify
$ go mod tidy -v
Strip a binary down further.
$ go build -ldflags="-s -w" .
$ upx --best --lzma my-tool # optional, controversial
References#
Tooling for
go test,go vet,gofmt, plus third-party (golangci-lint,delve).