Skip to content

cmd/go: offer a consistent "global install" command #30515

Closed

Description

@mvdan

As of 1.12, one can run the following from outside a Go module:

GO111MODULE=on go get foo.com/cmd/bar

The same mechanism can be used to download a specific version instead of @latest, such as @v1.2.3.

We can even emulate very similar behavior in Go 1.11, with a one-liner like:

cd $(mktemp -d); go mod init tmp; go get foo.com/cmd/bar

1.13 will likely make GO111MODULE=on a default, so it's likely that project READMEs will be able to justs tell users to run go get foo.com/cmd/bar on Go 1.13 and later.

However, this has a problem - if the user runs the command from within a Go module, the command will add clutter to the module's go.mod and go.sum files. The binary will also be installed as usual, so the user might not even notice the unintended consequences until much later.

This is a fairly common point of confusion among Go developers, particularly those new to modules. Now that most Go projects are modules, the chances of one running cmd/go commands within a module are quite high.

What we need is a "global install" command, which will ignore the current module. For example, imagine a go get -global, to ensure backwards compatibility. Or perhaps even repurposing go install to always mean global installs, since go get can be used for non-global ones.

I think we should make a decision and implement it before the 1.13 release, so that READMEs can finally drop the problematic go get -u foo.com/cmd/bar line. We can almost do it now, minus this confusing edge case with $PWD.

/cc @bcmills @rsc @myitcv @rogpeppe @thepudds

Activity

  1. added
    NeedsDecisionFeedback is required from experts, contributors, and/or the community before a change can be made.
    on Mar 1, 2019
  2. bcmills commented on Mar 1, 2019

    @bcmills
    Contributor
  3. myitcv commented on Mar 1, 2019

    @myitcv
    Member

    cc @ianthehat given the recent golang-tools related conversations too.

  4. bcmills commented on Mar 1, 2019

    @bcmills
    Contributor

    See previously #27643, #28400, #28156.

  5. added this to the Go1.13 milestone on Mar 1, 2019
  6. jayconrod commented on Mar 1, 2019

    @jayconrod
    Contributor

    I think we should repurpose go install for this when GO111MODULE=on. Since the build cache is required now, it doesn't seem like there's much use for go install anymore, especially when the target is a main package not in the build list.

  7. mvdan commented on Mar 1, 2019

    @mvdan
    MemberAuthor

    I agree that repurposing go install for this would be ideal in the long run, as right now go get and go install seem to overlap a bit too much. But we'd need to document and announce the new behavior of go install well, as this would be a breaking change for some users.

  8. ianthehat commented on Mar 1, 2019

    @ianthehat

    I think it is fairly easy to make a case for
    go install module/binary@version
    doing exactly what the user would expect (not modifying your local go.mod, and reliably building that specific version no matter what the local directory/state is)
    I don't think it would even be a breaking change if that is all we did (at the moment specifying a version would not be valid)

    The harder things are at the edges, things like are a way of doing that work and then running the resulting binary (had to do will with go run, and I am not sure we should even try), or wether the behaviour of go install when not given a version should be changed to avoid any confusion.

  9. thepudds commented on Mar 1, 2019

    @thepudds
    Member

    From what I have observed as well as for my personal usage, this would be very useful regardless of how it is spelled (go install, go get -global, ...).

    If it is implemented, I think it would be important to use the remote go.mod if one exists, including respecting any replace and exclude directives in that remote go.mod. In practice, authors will need to use replace or exclude in some cases, even though hopefully it will not be the common case. One of the main goals of modules is 100% reproducible builds, and that seems especially important when publishing a binary.

  10. thepudds commented on Mar 1, 2019

    @thepudds
    Member

    At this point, personally I would vote for go install.

    Regarding this:

    I think it is fairly easy to make a case for
    go install module/binary@version
    ...
    The harder things are at the edges, things like ... whether the behaviour of go install when not given a version should be changed to avoid any confusion.

    If go install module/binary@version takes on the behavior suggested by this proposal, then I think go install module/binary would need to be equivalent to go install module/binary@latest (including for consistency with go get foo vs. go get foo@latest behavior).

    A related question -- should go get some/cmd and go get some/cmd@v1.2.3 stop installing binaries? That would be a more radical change and would reduce overlapping behavior. One could argue that might be desirable if starting from scratch, but given the number of install instructions out there that say go get some/cmd or go get -u some/cmd, I suspect it would be preferable to let go get keep installing binaries.

  11. bufdev commented on Mar 1, 2019

    @bufdev

    Just passing by. +1 to having this. My current workaround as of 1.12:

    $(STATICCHECK):
    $(eval STATICCHECK_TMP := $(shell mktemp -d))
    @cd $(STATICCHECK_TMP); GOBIN=path/to/bin go get honnef.co/go/tools/cmd/staticcheck@$(STATICCHECK_VERSION)
    @rm -rf $(STATICCHECK_TMP)

  12. peebs commented on Mar 1, 2019

    @peebs

    I also support go install being for global binary installs to gopath and go get only for use inside a module path.

    Modules brings some breaking tooling changes and it seems like making some more here is the time to do it and worth the cost since this makes the tooling much easier to understand.

  13. rogpeppe commented on Mar 2, 2019

    @rogpeppe
    Contributor

    I support this suggestion.

    One question: would this re-purposed go install command respect replace and exclude directives in the installed module? I've argued before that it should, and I still think that's the case.

    To take one example, it wouldn't be possible to build a correctly working executable for github.com/juju/juju/cmd/juju without respecting replace directives.

  14. ianthehat commented on Mar 2, 2019

    @ianthehat

    I think the results should be identical to as if you had checked out the module at the supplied version to a temporary directory, changed to that directory and done go install ., so yes, fully obeying the go.mod with replace and excludes applied, not treating it as a dependancy.

  15. 123 remaining items

  16. gopherbot commented on Jul 16, 2020

    @gopherbot
    Contributor

    Change https://golang.org/cl/243077 mentions this issue: design: add 30515-go-get-b.md

  17. jayconrod commented on Jul 17, 2020

    @jayconrod
    Contributor

    Hey everyone, I've opened a new proposal issue, #40276, with a design doc at CL 243077.

    It'd be best if we could continue the discussion on that CL: Gerrit's threading and versioning will be more readable than a long GitHub issue.

    To concentrate discussion, I'd like to close and lock this issue. Before I do that, if you feel the proposal doesn't address an important use case and would like to continue this discussion separate from the proposal, please comment here.

    Also note, this will be on the agenda for the next golang-tools discussion on 2020-07-22 at 15:30 UTC, so feel free to join in then as well.

  18. mvdan commented on Jul 22, 2020

    @mvdan
    MemberAuthor

    I helped Jay with the new proposal above, so I agree that we should close this issue in favor of that newer one. This isue was more of a problem statement than a proposal for a specific solution that can be implemented.

  19. removed this from the Go1.16 milestone on Jul 22, 2020
  20. added a commit that references this issue on May 17, 2021
  21. locked and limited conversation to collaborators on Jul 22, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    FrozenDueToAgeNeedsFixThe path to resolution is known, but the work has not been done.modules

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions