mediocregopher / goat

A simple, yaml-based go dependency manager
Apache License 2.0
68 stars 11 forks source link

Proposal: allow ref key for goget types #24

Open tummychow opened 10 years ago

tummychow commented 10 years ago

Consider this case:

You depend on package X and you have fixed it at version A. Package X depends on package Y, and you don't care about the version of Y.

You have a deps that looks like this:


---
path: github.com/foo/bar
deps:
  - path: X
    loc: X
    type: git
    ref: A
  - path: Y
    type: goget

Now you bump X to version B. As a side effect of this version bump, X introduces a new dependency on Z. You don't care about the version of Z either. However, the version bump broke your build because you didn't have Z in your .go.yaml.

Okay, let's imagine that my version of goat gen (described on #19) was implemented already. You see that there's a missing dependency in your build, so you immediately know your dep graph is incomplete. You run goat gen again and it fills in Z for you. Now you can carry on with your build.


Counterpoint: what if you could set the ref on a goget package instead? What this means is "whatever deps this thing has, just get them, but I want the thing itself to be at this version". You end up with this deps instead:


---
path: github.com/foo/bar
deps:
  - path: X
    type: goget
    ref: A

Shorter than the original, and no risk of breakage from a new sub-dependency. I don't know who would really care about the difference between the first option and this second hypothetical one, but I know that the second one is clearly more intuitive than the first and requires less thinking. People who are suspicious of introducing any new subtrees in their depgraph without realizing it can always fall back to the first option to enforce their pedantic-ness.

@mediocregopher what are your thoughts? I'd be willing to take a shot at implementing this. It's something that many other dep managers already implement in some form (ie instead of clone, use go get + checkout). We could always borrow some of their code for this purpose ;)

mediocregopher commented 10 years ago

Hey @tummychow, thanks for taking the time to put your thoughts down. I'm currently out of town, but I'll be back in on sunday so I'll go through and really read your posts then, so I can give them as much time as you did :P

tummychow commented 10 years ago

No problem, @mediocregopher.

While I'm here, let me also point out another use case for the ref key on goget types. This is a real use case that I just crashed into while experimenting with another dependency manager.

My current project depends on the official client for InfluxDB, a time-series database written in Go. The client lives in a subfolder of the influxdb repository itself. To incorporate the client as a dependency, I have two basic options:

  1. I can add an entry for path: github.com/influxdb/influxdb/client with type: goget.
  2. I can add an entry for path: github.com/influxdb/influxdb with type: git. I can't specify the client itself because the client is not its own repository.

Now at the moment, either option works. But if I were to invoke the imaginary command goat gen, it would only behave correctly in the first case.

In the second case, goat gen thinks that my dependency is on all of influxdb. Influx happens to be a big project with a nontrivial dependency tree, so goat gen would add all those entries to my .go.yaml as well, which is incorrect because I only need the client.

On the other hand, in the first case, goat gen checks the code for the client and sees there are zero dependencies (client only needs stdlib, fortunately) so it doesn't have to add anything.

The problem with the first case is that it's not version-locked. If the client underwent a breaking API change, my code would break because the client is using type: goget. The fix would be to provide a ref on that type: goget entry so that I can check out a specific version of the influx repo and use that version's client code.

pib commented 10 years ago

Another option would be to add a rel option to the type: git dependencies, which would allow you to specify just the path within the repo that you care about. This is done in Berkshelf (specified in the Git Location section of their docs), and it works well for picking just a subdirectory within a git package.

mediocregopher commented 10 years ago

@tummychow So what would the ref actually be? Would this be like a git ref, and goat would have to just figure out that this is actually a git repository and handle it as such?

Also, to the first scenario, upon realizing that a dependency is missing you're going to have to run some goat command. Either this is already implemented and you can run deps, or if gen (or whatever we call it) is implemented you can call that, and it implicitly calls deps. Or we bail on everything and you edit .go.yaml and call deps. So adding this feature doesn't save the user any more hassle than if we implement gen (which I'd much prefer).

For your second scenario, I agree with @pib that this could be solved other ways. Instead of a field in .go.yaml I'd rather just have a flag we can pass in for gen to specify a directory. The fewer changes to .go.yaml we make the better; I'd much rather make a breaking change to flags than that in the future.

tummychow commented 10 years ago

Yes, ref on goget would vary depending on the underlying VCS at play.

I'll have to surrender the other two points because I can see a use case for git+rel that would not be satisfied by goget+ref. It starts to look pretty ugly if you have multiple subpackages of a single repository (have to reference the same path and ref several times, with a different loc and rel each time), but there's no way around that.

I'll accept @pib's suggestion with two counterpoints of my own:

And possibly a third point for future implementation: being able to list multiple sub folders on a single dep, to avoid duplicating the path and ref. But that's a convenience that can be left for another discussion.