One Tool To Rule Them All
The release of Go 1.24 introduces the tool directive for Go modules 1. This command simplifies the process of managing
tools like liners, generators or task runners. The usual approach up to this point was to install all related tools
outside the project directory. Usually using operating system package manager like beloved pacman 2. This approach
by its own is not that bad but requires managing the dependencies from outside the project itself. The go tool allowes
us to manage these tools’ dependencies from within the project directory.
As an example we wil install a task tool that runs commands from a Taskfile.yml 3.
$ go get -tool github.com/go-task/task/v3/cmd/task@latest
What exactly happens when we do this?
- Go reads the
-toolflag and the module path with the@latestversion query. it signals that this dependency should be tracked as a tool dependency, not a regular library import. - Go contacts the module proxy (controlled by
GOPROXYenviorement variable, default isproxy.golang.org) and asks what is the latest tagged version ofgithub.com/go-task/task/v3? The proxy responds with the most recent semver tag likev3.50.0. - Go downloads the module’s source code and stores it in the local module cache (
GOPATH/pkg/mod/). - The
go.modfile is updated with a linetool github.com/go-task/task/v3/cmd/taskand a resolved version is added torequireblock. - Go computes and records cryptographic checksums for the downloaded module in
go.sum, ensuring reproducible builds 4. - Go does not place a binary in
$GOPATH/bin, instead it will be compiled the first time You invoke the command and placed somewhere inside theGOCACHEdirectory. For me, it is :GOCACHE/db/[hash]/task.
We can confirm that indeed the tool is a compiled go binary:
$ go version -m /home/mp/.cache/go-build/db/dbaf4dccb08d0a7e2fb38ec6b15f8cae110b7c39119c9382854dfc378b56752d-d/task
/home/mp/.cache/go-build/db/dbaf4dccb08d0a7e2fb38ec6b15f8cae110b7c39119c9382854dfc378b56752d-d/task: go1.26.2-X:nodwarf5
path github.com/go-task/task/v3/cmd/task
(...)
Now we can run the actual tool with go tool task. To see what will be called (instead of actually running the command)
add -n like this go tool -n task.
One imporant aspect to remember is that the adding a tool to project does not automatically update the $PATH. If this
is required for some reason, we have to update it ourselves.
There is a slightly negative aspect to using go tool. The go.mod file can grow siginificantly with indirect dependencies.
The go tool command allows us to manage project tools from within the project directory by using the same go.mod
that we use for other module dependencies. This simplifies tracking of all the dependencies for the project.
To summarize:
- The
-toolapproach is the idiomatic way to manage development tools (linters, generators, task runners) - Tools are clearly separated from the code dependencies in the
go.modfile - Tools can be locked to specific versions that guarantee their consistent behavior
- We have one tool for managing tools, which simplifies installing, updating and removing of tools