Quick tools.go invocation

Sick of long `go run` commands when using tools.go to vendor CLI utilities? 03 July 2023
Posted in: Go Shell

Many places (including the wiki) recommend managing your CLI tool dependencies with a tools.go file. I’ve found this helpful as well. My contribution to this is a bash/zsh function (gt) that allows a shorthand invocation of a vendored tool.

function gt {
  shift 2> /dev/null
  [[ $? == 1 ]] && echo "Usage: gt tool" && return 1
  root=$(dirname "$(go env GOMOD)")
  cd "$root" || return 1
  cmd=$(go list -f '{{ join .Imports "\n" }}' -tags tools tools.go | grep -E "\/$arg\$" )
  if [[ $cmd == "" ]]; then
    echo cmd \""$arg"\" not in tools.go
    go run "$cmd" "$@"
  cd - 1> /dev/null || return 1

So now instead of invoking go run github.com/bufbuild/buf/cmd/buf lint, you can run gt buf lint. This will only work within a Go project where a tools.go file exists.

If you’re using zsh, here’s tab completion:

function _gt {
  if ((CURRENT == 2)); then
    root=$(dirname "$(go env GOMOD)")
    cd "$root" || return 1
    compadd $(go list -f '{{ join .Imports "\n" }}' -tags tools tools.go | sed -e "s#.*/##" )
    cd - 1> /dev/null || return 1
  elif ((CURRENT > 2)); then
    shift words
    _normal -p mycmd
compdef _gt gt

If you haven’t seen tools.go before, the TL;DR is:

  1. Invoke cli utilities – specifically Go commands (package main) in other modules – via go run. For example go run github.com/bufbuild/buf/cmd/buf lint.
  2. Reference the cli utilities in a stub tools.go file in the root of your projects.
//go:build tools
// +build tools

package foobar

import (
	_ "github.com/bufbuild/buf/cmd/buf"
	_ "github.com/golangci/golangci-lint/cmd/golangci-lint"
	_ "github.com/mfridman/tparse"
	_ "github.com/twitchtv/twirp/protoc-gen-twirp"
	_ "golang.org/x/vuln/cmd/govulncheck"
	_ "google.golang.org/protobuf/cmd/protoc-gen-go"

Your command-line utilities will be versioned like everything else in your project & will benefit from the ecosystem around that (Dependabot, govulncheck, etc.).