go get
like, simple CLI that allows automated versioning of Go package level binaries (e.g required as dev tools by your project!)
built on top of Go Modules, allowing reproducible dev environments.
From our experience all repositories and projects require some tools and binaries to be present on the machine to be able to perform various development operations like building, formatting, releasing or static analysis. For smooth development all such tools should be pinned to a certain version and bounded to the code commits there were meant to be used against.
Go modules does not aim to solve this problem, and even if they will do at some point it will not be on the package level, which makes it impossible to e.g
pin minor version X.Y.0
of package module1/cmd/abc
and version X.Z.0
of module1/cmd/def
.
At the end bingo
, has following features:
- It allows maintaining separate, hidden, nested Go modules for Go buildable packages you need without obfuscating your own module or worrying with tool's cross dependencies!
- Package level versioning, which allows versioning different (or the same!) package multiple times from a single module in different versions.
- Works also for non-Go projects. It only requires the tools to be written in Go.
- No need to install
bingo
in order to use pinned tools. This avoids thechicken & egg
problem. Onlygo build
required. - Easy upgrade, downgrade, addition, or removal of the needed binary's version, with no risk of dependency conflicts.
- NOTE: Tools are often not following semantic versioning, so
bingo
allows to pin by the commit.
- NOTE: Tools are often not following semantic versioning, so
- Immutable binary names, which gives a reliable way for users and CIs to use the expected version of the binaries, with reinstall on-demand only if needed.
- Optional, automatic integration with Makefiles.
You can read full a story behind bingo
in this blog post.
- Go 1.14+
- Linux or MacOS.
- All tools that you wish to "pin" have to be build in Go and use [Go Modules].
go get github.com/bwplotka/bingo && go mod tidy
or if you already installed bingo and want to pin it (inception!):
bingo get -u github.com/bwplotka/bingo
The key idea is that you can manage your tools similar to your Go dependencies via go get
:
bingo get [<package or binary>[@version1 or none,version2,version3...]]
Once pinned, anyone can reliably install correct version of the tool either doing:
go build -modfile .bingo/<tool>.mod -o=<where you want to build> <tool package>
or
bingo get <tool>
bingo
allows to easily maintain a separate, nested Go Module for each binary. By default, it will keep it .bingo/<tool>.mod
This allows to correctly pin the binary without polluting the main go module or other's tool module.
Also, make sure to check out the generated .bingo/Variables.mk
if your project uses Makefile
. It has useful helper variables 💖 that makes it super easy to install pinned
binaries without even installing bingo
(it will use just go build
!). For shell
users, you can invoke source .bingo/variables.env
to source those variables.
See an extensive and up-to-date description of the bingo
usage below:
bingo: 'go get' like, simple CLI that allows automated versioning of Go package level binaries (e.g required as dev tools by your project!)
built on top of Go Modules, allowing reproducible dev environments.
The key idea is that 'bingo' allows to easily maintain a separate, nested Go Module for each binary. By default, it will keep it '.bingo/<tool>.mod'
This allows to correctly pin the tool without polluting the main go module or other's tool module.
For detailed examples see: https://github.com/bwplotka/bingo
'bingo' supports following commands:
Commands:
get <flags> [<package or binary>[@version1 or none,version2,version3...]]
Similar to 'go get' you can pull, install and pin required 'main' (buildable Go) package as your tool in your project.
'bingo get <repo/org/tool>' will resolve given main package path, download it using 'go get -d', then will produce directory (controlled by -moddir flag) and put
separate, specially commented module called <tool>.mod. After that, it installs given package as '$GOBIN/<tool>-<Version>'.
Once installed at least once, 'get' allows to reference the tool via it's name (without Version) to install, downgrade, upgrade or remove.
Similar to 'go get' you can get binary with given Version: a git commit, git tag or Go Modules pseudo Version after @:
'bingo get <repo/org/tool>@<Version>' or 'bingo get <tool>@<Version>'
'get' without any argument will download and get ALL the tools in the moddir directory.
'get' also allows bulk pinning and install. Just specify multiple versions after '@':
'bingo get <tool>@<version1,version2,tag3>'
Similar to 'go get' you can use -u and -u=patch to control update logic and '@none' to remove binary.
Once pinned apart of 'bingo get', you can also use 'go build -modfile .bingo/<tool>.mod -o=<where you want to build> <tool package>' to install
correct Version of a tool.
Note that 'bingo' creates additional useful files inside -moddir:
* '<moddir>/Variables.mk': When included in your Makefile ('include <moddir>/Variables.mk'), you can refer to each binary
using '$(TOOL)' variable. It will also install correct Version if missing.
* '<moddir>/variables.env': When sourced ('source <moddir>/variables.env') you can refer to each binary using '$(TOOL)' variable.
It will NOT install correct Version if missing.
-go string
Path to the go command. (default "go")
-insecure
Use -insecure flag when using 'go get'
-moddir string
Directory where separate modules for each binary will be maintained. Feel free to commit this directory to your VCS to bond binary versions to your project code. If the directory does not exist bingo logs and assumes a fresh project. (default ".bingo")
-n string
The -n flag instructs to get binary and name it with given name instead of default, so the last element of package directory. Allowed characters [A-z0-9._-]. If -n is used and no package/binary is specified, bingo get will return error. If -n is used with existing binary name, rename will be done.
-u The -u flag instructs get to update modules providing dependencies of packages named on the command line to use newer minor or patch releases when available.
-upatch
The -upatch flag (not -u patch) also instructs get to update dependencies, but changes the default to select patch releases.
-v Print more'
list <flags> [<package or binary>]
List enumerates all or one binary that are/is currently pinned in this project. It will print exact path, Version and immutable output.
-moddir string
Directory where separate modules for each binary is maintained. If does not exists, bingo list will fail. (default ".bingo")
-v Print more'
Version
Prints bingo Version.
Let's show a few examples on popular goimports
tool (which formats Go code including imports):
-
Pinning latest
goimports
:bingo -u get golang.org/x/tools/cmd/goimports
This will install (at the time of writing) binary:
${GOBIN}/goimports-v0.0.0-20200601175630-2caf76543d99
-
After running above, pinning (or downgrading/upgrading) version:
bingo get goimports@e64124511800702a4d8d79e04cf6f1af32e7bef2
This will pin to that commit and install
${GOBIN}/goimports-v0.0.0-20200519204825-e64124511800
-
Installing (and pinning) multiple versions:
bingo get goimports@e64124511800702a4d8d79e04cf6f1af32e7bef2,v0.0.0-20200601175630-2caf76543d99,af9456bb636557bdc2b14301a9d48500fdecc053
This will pin and install three versions of goimports. Very useful to compatibility testing.
-
Unpinning
goimports
totally from the project:bingo get goimports@none
PS:
go get
allows that, did you know? I didn't (: -
Editing
.mod
file manually. You can totally go to.bingo/goimports.mod
and edit the version manually. Just make sure tobingo get goimports
to install that version! -
Installing all tools:
bingo get
-
Bonus: Makefile mode! If you use
Makefile
,bingo
generates a very simple helper with nice variables. After running anybingo get
command, you will notice.bingo/Variables.mk
file. Feel free to include this in your Makefile (include .bingo/Variables.mk
on the top of your Makefile).From now in your Makefile you can use, e.g.
$(GOIMPORTS)
variable which reliably ensures a correct version is used and installed. -
Bonus number 2! Using immutable names might be hard to maintain for your other scripts so
bingo
also produces environment variables you can source to you shell. It's as easy as:source .bingo/variables.env
From now on you can use, e.g.
$(GOIMPORTS)
variable which holds currently pinned binary name of the goimports tool.
To see production example see:
Any contributions are welcome! Just use GitHub Issues and Pull Requests as usual. We follow Thanos Go coding style guide.
@bwplotka inspired by Paul's research and with a bit of help from Duco (: