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

x/vgo: transitive dependencies and transitive dependency updates #24500

Closed
mattfarina opened this issue Mar 23, 2018 · 11 comments
Closed

x/vgo: transitive dependencies and transitive dependency updates #24500

mattfarina opened this issue Mar 23, 2018 · 11 comments
Milestone

Comments

@mattfarina
Copy link

I have a question about intent with minimum version selection. Let's say I have an app with a dependency on A which has a dependency on B. The App does not have a direct dependency on B.

App --> A (v1.1.1) --> B (v1.2.3)

Let's say B has a security bug and an update is released of v1.2.4. Dependency A has not updated to the security release of B and the latest version still depends on B@v1.2.3.

How would the App developer go about getting the security release of B prior to A updating to require it as the minimum version? Using MVS, vgo is going to install 1.2.3, right?

@gopherbot gopherbot added this to the vgo milestone Mar 23, 2018
@kardianos
Copy link
Contributor

From https://research.swtch.com/vgo-mvs

Minimal version selection assumes that each module declares its own dependency requirements: a list of minimum versions of other modules. Modules are assumed to follow the import compatibility rule—packages in any newer version should work as well as older ones—so a dependency requirement gives only a minimum version, never a maximum version or a list of incompatible later versions

It either depA or App require the newer version, the newer version will be selected for App.

@mattfarina
Copy link
Author

@kardianos thanks for clarifying. That's what I thought but I wanted to double check.

I brought this up for three reasons:

  1. This is not how package managers for other languages (e.g., rust, python, javascript, php, etc) handle things and it's not how existing go package managers (e.g., dep, glide) do either. That means this is a change in expectations for users.
  2. There's extra work for app developers compared to today. I've worked on projects there the App needs to track versions of transitive dependencies for a time and it's extra work for developers. The sentiment I've seen is they don't like it which means there will likely be tools built on top of vgo to handle these issues.
  3. This doesn't match the desires of the community which can be seen in the Go Package Management survey of users. We surveyed end users (results online) and this is the opposite direction of their desires. The screenshot from the results below as an example of what I mean.

screen shot 2018-03-23 at 9 35 19 am

The first two are changes in expectations and experiences. It's good to be very open about them early so fewer people are tripped up by the change.

Unless there is something I'm missing, which I hope is the case.

@rasky
Copy link
Member

rasky commented Mar 24, 2018

My understanding is that, if you're App's developer, A's requirements are only taken into account the first time A is added to App (through MVS). Afterwards, App's developer will have to use vgo list -m -u to find out when updates are available in all the transitive closure of dependencies and decide whether to upgrade (through vgo get -u).

This is no different to what most package managers do, since lock files fix a specific version. You still need to be aware of updates in your dependencies (and possibly updates in their transitive closure), decide whether it is worth for you (security or not) and run a package manager command to get an updated version.

Your original statement is true for new apps (say App2). If you create App2 and adds A as dependency, MVS will include the B's version that A was last tested with by A's developer, which is the one not including the latest (security or not) fixes. But (if you think of it) this doesn't change the picture: App2's developer still has to continuously monitor fixes of its (transitive) dependencies for correct maintenance of their application; B's security fix might be released the day after A is added to App2, so you either monitor this or run the risk.

If you want to update all your dependencies to the latest version no matter what, you can still run a vgo get -u and cross fingers / run tests. If you feel strongly about isolating security fixes, you should start lobbying Go's community to begin using the patch version of semantic versioning as a security fix indicator. In that case, it would be very easy to spot them with vgo list -m -u and an ecosystem of tools (think, GitHub dependency alert system) could be build upon it.

@mattfarina
Copy link
Author

@rasky I'm not sure we're on the same page for how most package managers work. Let me explain where I'm coming from and how it works.

The data we collected suggest that Go developers are most familiar with npm, pip, gems, maven, bower, bundler, easy install, cpan, composer, and cargo (first 10 in the list anyway). When I look at most package managers I'll try and do so in terms of what Go users expect from their experiences.

Most of these have the concept of a manifest and a lock file. The lock file keeps the current revision, and often some more details, that are in use for the complete dependency tree (direct and transitive).

The manifest file holds details on direct dependencies of an application and a range is typically included rather than a specific version to use. The range allows the package managers resolver to look for the maximum compatible version based on ranges (as opposed to the vgo minimum compatible version).

Using a tools like npm (and many of the others on that list), an app developer can run to update (withing the realm of of compatible range). For example, npm update. This will resolve to the latest compatible release and pickup and new versions. It will do so for the complete dependency tree including transitive ones.

Because it looks at the maximum compatible version and ranges are often used, security updates like the one in the original issue, are picked up with no extra work from the app developer.

We did a pretty through analysis of the package managers people have the most experience with. The features in dep (and to a slightly lesser extent in glide), when it comes to handling updates, are the same patterns used in the popular package managers for other languages. You can find a roll-up of details here.

If you'd like to view the details of the survey, including that a majoring of go devs want to use ranges, you can find them here

@kardianos kardianos changed the title x/vgo: transitive dependencies and security updates x/vgo: MVS should not be used because it it is different then what other package managers use Mar 24, 2018
@rasky
Copy link
Member

rasky commented Mar 24, 2018

I tried to answer to the question you wrote in the first post, which is how to handle a specific single version upgrade to handle a security fix. I tried to depict different scenarios and compare them.

You're now making an example where you want to upgrade all dependencies to the latest compatible release (npm update). That can still be done with vgo, it's called vgo get -u.

If your point is just that vgo is different from the others, then yes it is. If you want to have a constructive discussion, you need to explain a specific scenario and a specific use case, and we can discuss about differences, pros and cons.

@kybin
Copy link
Contributor

kybin commented Mar 25, 2018

Also, vgo get -p will be added in the future to update your dependencies to latest patches.

@mattfarina
Copy link
Author

@kybin your direct dependencies or both direct and transitive ones?

@mattfarina mattfarina changed the title x/vgo: MVS should not be used because it it is different then what other package managers use x/vgo: transitive dependencies and security updates Mar 26, 2018
@mattfarina mattfarina changed the title x/vgo: transitive dependencies and security updates x/vgo: transitive dependencies and transitive dependency updates Mar 26, 2018
@mattfarina
Copy link
Author

@rasky With vgo get -u you're saying that it will upgrade transitive to newer versions than the ones specified by their parent packages in the specification tree? The question here is about transitive not direct dependencies.

For example, if I import Kubernetes client-go it has a bunch of dependencies of its own. And, as I update to newer version of client-go the dependencies it has changes. The managing of those and their versions.

@rsc
Copy link
Contributor

rsc commented Mar 30, 2018

I have a question about intent with minimum version selection. Let's say I have an app with a dependency on A which has a dependency on B. The App does not have a direct dependency on B.

App --> A (v1.1.1) --> B (v1.2.3)

Let's say B has a security bug and an update is released of v1.2.4. Dependency A has not updated to the security release of B and the latest version still depends on B@v1.2.3.

How would the App developer go about getting the security release of B prior to A updating to require it as the minimum version? Using MVS, vgo is going to install 1.2.3, right?

The app developer can run "go get -u" to update everything to latest minor version, or (eventually) "go get -p" to update everything to latest patch version. The requirement simplification algorithm (Algorithm R in the mvs post) means that in your situation the App's go.mod will show B v1.2.4 explicitly. If, later, A v1.1.2 adds a requirement on B v1.2.4, then when App updates to A v1.1.2 the mention of B v1.2.4 in go.mod will become redundant and will be dropped automatically.

I brought this up for three reasons:

  1. This is not how package managers for other languages (e.g., rust, python, javascript, php, etc) handle things and it's not how existing go package managers (e.g., dep, glide) do either. That means this is a change in expectations for users.

Yes and no.

In your example, at the point where the App picks up A for the very first time and picks v1.1.1, then yes Cargo/Dep/others will see that A v1.1.1 asks for B v1.2.3 and automatically pick up B v1.2.4 instead if it exists. So yes, vgo would pick up B v1.2.3 in this situation and that's a change in user expectation. This is absolutely by design. The minute before v1.2.4 comes out, vgo picks up B v1.2.3. The minute after v1.2.4 comes out, vgo still picks up B v1.2.3. No one has asked for v1.2.4. It may be a terrible release that's going to be yanked in an hour. Until someone says explicitly "I want to do an update operation", with the implied "I'm ready for things to break and to deal with that", I don't believe the build system has any business pulling in brand new releases.

Note that if even when Cargo/Dep picks v1.2.4, from that point on there's a lock file keeping the App at B v1.2.4, even if v1.2.5 exists, until the next version adjustment. Even in Cargo/Dep, once you do the initial pull, you still don't get future upgrades unless you ask for them. It seems like the Cargo/Dep developers believe the same thing I do, in at least some contexts. That belief is just not as consistently applied in Cargo/Dep as in vgo. (I'll also note that Sam Boyer has talked in the past about making Dep treat A's suggestion of B v1.2.3 as a "preferred version" to avoid pulling in v1.2.4 automatically.)

It is certainly true that some users may expect that adding A will get the latest version of A and then the latest version of all its dependencies automatically, so that A's author can be lax about updating A's own dependencies. I think I've been very up front that instead vgo pulls the latest version of A and then the versions of A's dependencies that A was actually built and presumably tested with, or as close as possible to that ideal. This is of course in direct conflict with the "I want the latest of everything" argument. But the people who want the latest of everything can use "go get -u A" instead of "go get A". In contrast, there's no way in Cargo/Dep to say "get me what A actually used".

  1. There's extra work for app developers compared to today. I've worked on projects there the App needs to track versions of transitive dependencies for a time and it's extra work for developers. The sentiment I've seen is they don't like it which means there will likely be tools built on top of vgo to handle these issues.

The only extra work is typing "go get -u" when you want to override the default and bring in the absolute newest of a set of dependencies (and are ready to test and debug the result). Or "go get -p" if you think that's meaningful (we'll see whether it is).

Yes there's extra work, but not much. A new tool that types -u for you doesn't seem like it will be worthwhile.

  1. This doesn't match the desires of the community which can be seen in the Go Package Management survey of users. We surveyed end users (results online) and this is the opposite direction of their desires. The screenshot from the results below as an example of what I mean.

screen shot 2018-03-23 at 9 35 19 am

From this, it looks to me like if you had a system that forced newest dependencies always, like Cargo/Dep, then you'd be alienating the 35% of respondents who only want that sometimes (implicitly, not all the time). Better to give developers the option instead.

I think the survey question is a bit misleading, for two reasons. First, I assume you didn't also ask "It's important to me that I control when new dependencies are pulled in." Second, the question says "latest compatible". Obviously everyone would agree with that in the abstract. The problem is that
despite everyone's best efforts to ensure compatibility, just pulling the latest update in the same major version does not guarantee it is actually compatible. One goal of minimum version selection is to delay pulling the latest version of something until it is explicitly requested, which presumably is at a time when the developer is ready to test to see if it works. For example, someone tweeted at me a few days ago "I can't even begin to count how many hours I've lost over the years to builds that just suddenly stopped working when newer versions of dependencies got pulled."

Minimal version selection attempts to strike a balance between these two competing concerns, so that updating to latest of everything is easy, but developers get more control over exactly when that happens.

The first two are changes in expectations and experiences. It's good to be very open about them early so fewer people are tripped up by the change.

Agreed. I've tried to be very up front about this distinction from other systems, and I will continue to be.

I'm going to close this issue since it seems like your initial question has been answered.

@rsc rsc closed this as completed Mar 30, 2018
@mattfarina
Copy link
Author

@rsc First, thanks for clarifying. I've gotten contradictory information which is why I asked the question in the first place.

Second, I get the impression we're doing a bit of reinventing the wheel going on. go get -u and go get -p assumes you're going to pass in the ones you want updated or do everything at this level. You can do this by hand or have a script do it. Existing tools do this for you. Files like the Gopkg.toml let you set the rules for your dependencies when performing operations while Gopkg.lock hold where you're at (similar to your go.mod). In the vgo setup we have a case where more tooling needs to be built on top of it to handle this nuance but the resolver isn't going to be as aware of how this is happening in dependencies. Other tools are already to that next level.

As someone who has worked on dependency managers I know that people want to store the rules, per dependency, in configuration. Requests for features quickly show up around that. Have you talked with people, other than those on dep, about their experiences building a package manager to know where they've gone and why?

Third, It's a little disheartening to know that effort was put into survey the Go users on dependency management to ascertain their needs and you've not read it. If you had you would have known what was asked and the results. Building a dependency manager without knowing what the community has stated as their needs should cause the community some caution because it gives the impression you're not listening to them. Even when someone on the Go team at Google was involved in conducting the survey.

@rsc
Copy link
Contributor

rsc commented Jul 23, 2018

One thing I don't often read is comments on closed GitHub issues - there are just too many mails from GitHub - so I hadn't seen this response. It was pointed out to me earlier today that some people are citing this response as evidence that I ignored all the prior work done by the dependency management group and committee, which is unfortunate.

Third, It's a little disheartening to know that effort was put into survey the Go users on dependency management to ascertain their needs and you've not read it. If you had you would have known what was asked and the results.

My only comment on this thread, above, critiques the survey question wording and the conclusions you drew from the response.

There is absolutely no support for your claim that I didn't read the survey.

To set the record straight, in fact I did read the survey and all the docs generated from the working group, and I talked through the company survey results with Steve Francia multiple times, all in the winter of 2016-2017.

@golang golang locked and limited conversation to collaborators Jul 23, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants