Using "go get" to download binaries without adding them to go.mod
Asked Answered
Z

3

22

I'm using Go modules in my project and in my build system (e.g. Travis CI) I'm downloading a command-line utility (written in Go) with go get to assist with my build process, e.g.:

go get github.com/mitchellh/gox

However, this go get causes the file to be added to my go.mod file. This is contaminating the build environment, causing it to be "dirty" (since there are changes to some files tracked in git, in this case go.mod and go.sum), and I use git describe --always --dirty --tag to describe my build, which shows up as "dirty".

Is there a way to "go get" a binary just to download it, without adding it to the go.mod/go.sum?

I've tried setting GOPATH to somewhere else, even then, go get updates the go.mod/go.sum to add this as an // indirect dependency.

dir="$(mktemp -d)"; \
  env GOPATH="$dir" go get github.com/mitchellh/gox && \
  mv "$dir/bin/gox" "$(go env GOPATH)"/bin/gox
Zillion answered 1/7, 2019 at 20:57 Comment(4)
Have you tried disabling modules when installing tools? For example, GO111MODULE=off GO111MODULE=off go get -v golang.org/x/textMarvellamarvellous
This could work, but I’m assuming it’s not a long-term fix. Isn’t GO111MODULE will be ignored at some point in a newer go version when Go Modules are widely used?Zillion
I think that's far away in the horizon until everyone adapts modules. One other solution would be to change directories before installing tools. go.mod won't be in the current directory and modules will be disabled.Marvellamarvellous
Just to clarify, it is not true that modules will be disabled if you change directories to somewhere without a go.mod. More details in answer below.Between
B
6

Hopefully in Go 1.14 there will be a new flag for go get that does exactly what you are asking. This is tracked in issue #30515 "cmd/go: offer a consistent global install command".

Prior to that, you have a few different options.

Go 1.12 and 1.13: change directory

If you are using Go 1.12 or later, the simplest solution is probably to move outside your current module to a directory without a go.mod prior to doing the go get, such as:

$ cd /tmp                              
$ go get github.com/foo/[email protected]
$ cd -                                # return to prior directory

Go 1.11, 1.12, 1.13+: gobin

gobin is a module-aware command to install or run binaries that provides additional flexibility, including the ability to install without altering your current module's go.mod. See the gobin README and FAQ for more details.

Go 1.11: temporary module

If you are using Go 1.11 with modules, the first step is probably to upgrade to Go 1.12 or 1.13 given there are many improvements in modules. If you are required to use Go 1.11 and want to use the @version syntax without updating your current module's go.mod, then one approach is to create a temporary module:

cd $(mktemp -d) && go mod init tempmod && go get github.com/foo/[email protected]

This is because in Go 1.11, you can't use the @version syntax unless you are in a module, which was relaxed in Go 1.12. This approach has been automated by a simple shell script by @rogpeppe.

Additional Details

In general, the go command in module-module always determines what module it is "in", which is based on the current working directory when you invoke the go command. (You could make an analogy to how make without any args will look for a makefile in the current working directory, or how historically go build without any args will build the current working directory, etc.).

With modules, go get looks for a go.mod file in the current working directory or any of its parents, and go get will use the constraints listed in any go.mod as part of solving for versions, as well as update the go.mod if needed based on doing the go get. That is why your go.mod file is updated if you run go get from within an existing module.

On the other hand, starting with Go 1.12, if you are in a directory that is not part of any module (that is, the directory does not have a go.mod, nor do any of its parents), then there is no go.mod to update, but the go command is still able to operate in module mode and use the @version syntax.

From the Go 1.12 release notes:

When GO111MODULE is set to on, the go command now supports module-aware operations outside of a module directory, provided that those operations do not need to resolve import paths relative to the current directory or explicitly edit the go.mod file. Commands such as go get, go list, and go mod download behave as if in a module with initially-empty requirements. In this mode, go env GOMOD reports the system's null device (/dev/null or NUL).

Between answered 1/8, 2019 at 16:26 Comment(1)
The suggested edit queue is full, so I'm adding a comment instead. github.com/golang/go/issues/40276 tracks the proposal, which is implemented in CL go-review.googlesource.com/c/go/+/254365. This will most likely be included in Go 1.16, and you can run go install github.com/foo/bar@latest to install a command without changing go.mod.Grube
G
28

Go 1.16 onwards

Go 1.16 (released February 2021) includes a change that makes it possible to install a binary without affecting go.mod.

Issue 40276 tracks the proposal:

cmd/go: 'go install' should install executables in module mode outside a module

This was implemented in CL 254365. As part of this change, you can run e.g.:

go install golang.org/x/tools/cmd/goimports@latest

to install a binary without affecting go.mod.

To install a specific version, replace @latest with e.g. @v0.1.5.

Grube answered 15/1, 2021 at 10:29 Comment(3)
Note, @latest or similar is required for this to workKubis
Note that @latest will not select the latest major version, it will only select the latest minor version for the major version given in the import path. For example, if you need v2 of a tool, you need /v2@latest.Lyrism
Note also that go install ...@latest does not work with modules that have replace directives in their go.mod files. For those modules you still need to download the repository (or clone it) locally, then use go install inside the repository without any parameters.Lyrism
B
6

Hopefully in Go 1.14 there will be a new flag for go get that does exactly what you are asking. This is tracked in issue #30515 "cmd/go: offer a consistent global install command".

Prior to that, you have a few different options.

Go 1.12 and 1.13: change directory

If you are using Go 1.12 or later, the simplest solution is probably to move outside your current module to a directory without a go.mod prior to doing the go get, such as:

$ cd /tmp                              
$ go get github.com/foo/[email protected]
$ cd -                                # return to prior directory

Go 1.11, 1.12, 1.13+: gobin

gobin is a module-aware command to install or run binaries that provides additional flexibility, including the ability to install without altering your current module's go.mod. See the gobin README and FAQ for more details.

Go 1.11: temporary module

If you are using Go 1.11 with modules, the first step is probably to upgrade to Go 1.12 or 1.13 given there are many improvements in modules. If you are required to use Go 1.11 and want to use the @version syntax without updating your current module's go.mod, then one approach is to create a temporary module:

cd $(mktemp -d) && go mod init tempmod && go get github.com/foo/[email protected]

This is because in Go 1.11, you can't use the @version syntax unless you are in a module, which was relaxed in Go 1.12. This approach has been automated by a simple shell script by @rogpeppe.

Additional Details

In general, the go command in module-module always determines what module it is "in", which is based on the current working directory when you invoke the go command. (You could make an analogy to how make without any args will look for a makefile in the current working directory, or how historically go build without any args will build the current working directory, etc.).

With modules, go get looks for a go.mod file in the current working directory or any of its parents, and go get will use the constraints listed in any go.mod as part of solving for versions, as well as update the go.mod if needed based on doing the go get. That is why your go.mod file is updated if you run go get from within an existing module.

On the other hand, starting with Go 1.12, if you are in a directory that is not part of any module (that is, the directory does not have a go.mod, nor do any of its parents), then there is no go.mod to update, but the go command is still able to operate in module mode and use the @version syntax.

From the Go 1.12 release notes:

When GO111MODULE is set to on, the go command now supports module-aware operations outside of a module directory, provided that those operations do not need to resolve import paths relative to the current directory or explicitly edit the go.mod file. Commands such as go get, go list, and go mod download behave as if in a module with initially-empty requirements. In this mode, go env GOMOD reports the system's null device (/dev/null or NUL).

Between answered 1/8, 2019 at 16:26 Comment(1)
The suggested edit queue is full, so I'm adding a comment instead. github.com/golang/go/issues/40276 tracks the proposal, which is implemented in CL go-review.googlesource.com/c/go/+/254365. This will most likely be included in Go 1.16, and you can run go install github.com/foo/bar@latest to install a command without changing go.mod.Grube
S
0

Per go help build:

The -mod build flag provides additional control over updating and use of go.mod.

If invoked with -mod=readonly, the go command is disallowed from the implicit automatic updating of go.mod

Southwesterly answered 1/7, 2019 at 21:50 Comment(3)
1. Sadly go get -mod=readonly github.com/mitchellh/gox still updates go.mod. This could be a legit golang issue, however. 2. rest of the sentence you quoted goes like ...Instead, it fails when any changes to go.mod are needed.Zillion
go get -mod=readonly will be fixed in 1.13 github.com/golang/go/issues/30667Marvellamarvellous
@Marvellamarvellous that issue is unrelated.Southwesterly

© 2022 - 2024 — McMap. All rights reserved.