Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/go: mod is too much of a grab bag #26581

Closed
robpike opened this issue Jul 25, 2018 · 7 comments

Comments

Projects
None yet
7 participants
@robpike
Copy link
Contributor

commented Jul 25, 2018

The UX model for go mod is to define what the operation is by setting a flag. This is an odd model that I suspect just fell out of the way the go command already works. But I find it odd, and it mixes mandatory and optional flags together in odd ways.

It will take some rearchitecting but the operations should be specified as subcommands, not flags, like this:

$ go mod init foo.bar/package

Besides being a better match for the usage, this would enable two important changes: First, a help enabler could be added; today we get

$ go mod -help -init
usage: mod [-v] [maintenance flags]
Run 'go help mod' for details.
$

which is no help at all, really, when we could have

$ go mod help init
The init subcommand does this and that and has these flags...

Similarly, by turning these actual commands into syntactic commands, we open up the possibility of having flags on them, with the usual advantages (and disadvantages) and clarity that results.

@rsc

This comment has been minimized.

Copy link
Contributor

commented Jul 26, 2018

I agree go mod is not right as it stands. It's too much of a grab bag.

We don't have any sub-sub-commands today, and I don't particularly want to go there. As the new 'go help mod' text (CL pending) points out:

Note that support for modules is built into all the go commands,
not just 'go mod'. For example, day-to-day adding, removing, upgrading,
and downgrading of dependencies should be done using 'go get'.
See 'go help modules' for an overview of module functionality.

Too many people think everything about modules is 'go mod', to the point of not including 'go get' in tutorials about how to download dependencies. Subcommands won't help avoid that misperception.

But mod should probably be split into a few different commands.

The bulk of the flags do combine nicely. In particular -init, -module, -require, -exclude, -replace, -droprequire, -dropexclude, -dropreplace all work really well together in a single invocation and would work less well if you had to run the go command repeatedly to effect the same list of those changes. I've been thinking there should be a -n that just shows you the resulting go.mod instead of writing it out. Similarly -json prints the go.mod in json form and maybe -n -json should print it without writing go.mod back. All this was meant to keep the promise in #23966 to make go.mod machine parseable (with -json) and editable (with the others). The -fmt and -fix flags fit into that category too.

The abuses begin with -sync, although even that is a natural thing to do after the edits and before committing the modified go.mod to disk as well. But it ends up being a useful operation by itself enough of the time that maybe it should be kept separate. It is unlike the others mentioned so far in that it looks at Go source files.

Then -vendor, which must do most of the work of -sync to decide what to vendor, and then vendor it.

Then -verify, which doesn't actually look at any packages but does consult go.sum and the download caches.

Then -packages, which can probably just be dropped so I won't say more.

Then -graph, which is quite possibly the most important thing here after -sync. It prints the module graph in a simple text form that allows further analysis, andthat data is hard to extract in any other way.

I also have a CL in progress to add a -pgraph to print the module+package import graph so that you can answer "why does go mod -sync keep such and such module around?". That data is possible to extract in other ways, so maybe it's not as important.

If we're going to split up the mod command, probably the editing operations should stick around as 'go mod'. Another possibility is to split out 'go init' and leave the others as 'go mod'. Then you would have go init my.module/name which I think you were going for.

But then maybe 'go graph' (package graph), 'go graph -m' (module graph), 'go sync', 'go vendor', 'go verify'?

Happy to brainstorm tomorrow.

@mvdan

This comment has been minimized.

Copy link
Member

commented Jul 26, 2018

We don't have any sub-sub-commands today

What about go tool <name> <args>? I realise it's a slightly different case, as that runs separate programs, but to the user it still seems like sub-sub-commands.

@rsc rsc changed the title cmd/go: the mod flags should be subcommands cmd/go: mod is too much of a grab bag Jul 27, 2018

@robpike

This comment has been minimized.

Copy link
Contributor Author

commented Jul 27, 2018

I don't think there's any design principle at work here that argues against sub-subcommands, and a compelling argument in favor of them is that they make it much easier to provided targeted, per-operation help from the command line. Another is a similar story for flags.

@gopherbot

This comment has been minimized.

Copy link

commented Jul 29, 2018

Change https://golang.org/cl/126655 mentions this issue: cmd/go: split go mod into multiple subcommands

@rajender

This comment has been minimized.

Copy link
Contributor

commented Jul 30, 2018

Cross posting here from the CL 126655.

This seems like less seamless integration to me.
Why all these need to go through mod command?
We don't have go package get. We have go get.
It also seems as it is there are too many commands.
Do we really need fix and tidy commands? Looks like
we can make their functionality part of other commands
like get/build.

Do we really need 'mod edit' part of go command?
May be it can be separate tool. I know you have plans
to make mod file reading a separate package. May be this can be
part of it.

go mod edit => remove, go mod edit -fmt can be part of go fmt
go mod fix => remove and make it's functionality implicit
go mod graph => remove and make it part of go list
go mod init => go init
go mod tidy => remove and make it's functionality implicit
go mod vendor => go vendor
go mod verify => go verify

@bcmills

This comment has been minimized.

Copy link
Member

commented Jul 30, 2018

go mod edit is extremely useful for unit-testing the go command itself. It could be factored out, but it needs to be a command-line tool: if it is a compiled-in library, then what happens if the go command is updated to support a directive that the library doesn't know about?

go mod init is similarly useful as a standalone tool: it makes use of contextual information to infer the module path and applies conversions to existing lock files, so we don't want things that use it to bake in one specific set of functionality.

go mod tidy needs to be an explicit and separate command: the operations it performs (chasing down transitive dependencies with exotic build tags) can be expensive.

However, go mod fix could perhaps be folded into go mod tidy (as go mod tidy -m).

I agree that go mod graph should probably be a go list flag.

I disagree that go mod vendor should be a top-level command, as it is already kind of a niche use-case. I think it would work reasonably well as a go get flag, such as go get -d=vendor: go get already has the job of downloading source code, which is also more-or-less what go mod vendor does.

I suspect that go mod verify would fit reasonably as a flag on go list or go get (perhaps go get -d=verify or just a normal part of go get -d).

@kostix

This comment has been minimized.

Copy link

commented Jul 31, 2018

@bcmills, let me disagree on your

I disagree that go mod vendor should be a top-level command, as it is already kind of a niche use-case.

point.

I'm pretty much confident that teams which already make heavy use of vendoring will unlikely be moving away to "the next thing" (borderline package proxies?) anytime soon: there will just not be enough justification to fix what is not broken.
Vendoring solves very real issues in a pretty effective way, and the simplicity of this approach—basically you do not need to deploy and maintain anything "extra" to make it work—is a reasonable selling point of vendoring.
So please do not discount it easily.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.