whyrusleeping / gx

A package management tool
MIT License
1.88k stars 110 forks source link

How does gx deal with version constraints, if at all? #134

Open hdgarrood opened 7 years ago

hdgarrood commented 7 years ago

First of all, thanks for making this! I'm not a go user but it's really encouraging to me to see that it can be done. I just have a couple of questions that occurred to me from reading the readme.

Suppose I am creating a library which I expect other people to depend on - let's call it "foo" - and I also want to depend on other libraries. In particular, I want to depend on a library called "bar". Firstly, from my reading of the "dependencies" section of the readme, it seems to me that gx only lets me express the fact that "I need bar version 1.2.1", and not, say, "I need bar >= 1.2.1 < 2.0.0". Is this correct?

Now suppose that someone else is also making a library called "baz", and "baz" depends on "bar" at version 1.2.2. What happens if yet another person now creates their own project and wants to depend on both "foo" and "baz"? Do they get two copies of "bar", one at 1.2.1 and one at 1.2.2?

ghost commented 7 years ago

Version numbers don't really have any meaning when it comes to dependencies -- you always depend on a specific hash.

The only way that they currently have a meaning is semantic versioning -- i.e. they hint to the developer which level of changes to expect.

ghost commented 7 years ago

Do they get two copies of "bar", one at 1.2.1 and one at 1.2.2?

Yes

hdgarrood commented 7 years ago

Ok, great, thank you! Do you know if this is likely to change, i.e. are there any plans for gx to be able to deal with version ranges?

hdgarrood commented 7 years ago

To clarify - of course, I recognise that reproducibility is one of the core goals of gx and that it would be at odds with any approach which involves saying 'any out of these versions will do'. I also recognise that for many projects, you of course do want to completely nail down your dependencies. However I think for libraries which are intended to be reused by many different projects, it can be very useful to be able to say 'here's what I work with' so that the work required to find out what set of versions your library can work with only needs to be done once, and then that information can be communicated to downstream users.

I completely understand if this is out of scope of gx, though, I'm just trying to get a feel for the general direction of the project.

whyrusleeping commented 7 years ago

@hdgarrood This is a hard problem, And I have yet to figure out a nice way that makes this all work cleanly. I generally do not trust authors of external dependencies to follow semver, and even if they do follow semver, the possibility exists that a patch version update in a dep could cause very subtle issues in my code. I was thinking at one point of adding a way to use the highest version number of a given package throughout the dep tree (in your example above, this would cause everyone to use "bar" 1.2.2). But i would need to give a good deal of thought to this to make sure there arent any weird gotchas

hdgarrood commented 7 years ago

I agree that it's probably best for a package manager to avoid making any assumption that library authors will stick to semver, especially as (imo) people can reasonably disagree about what kind of version bump a given change warrants. However, if library authors say that their libraries depend on other libraries with a certain set of version ranges, I think it makes sense to trust them — it's usually going to be inaccurate, because authors cannot predict the future, but it's also usually pretty close to being accurate, and a lot better than hoping that semver will end up doing the right thing, or even having no information at all. And when ranges turn out to be inaccurate, authors can always publish a new version with updated ranges.

I agree that the issue of patch version updates causing subtle problems is not to be taken lightly: certainly being able to absolutely nail down all your dependencies (like gx currently does) is invaluable, and doing it this way is almost always the right choice for an application, in my view (as opposed to a library).

Incidentally, are you at all familiar with the Haskell ecosystem? There are two tools people use to install packages: cabal, which uses version ranges and dependency solving, and stack, which uses curated package snapshots and completely pins down all your dependencies so that no changes to your install plan can happen without you making a change to the file which determines the versions of the dependencies you're using. There might be some useful lessons which could be taken from there.

renatoathaydes commented 6 years ago

I generally do not trust authors of external dependencies to follow semver

It is possible to verify that the author adheres to semantic versioning automatically. For example, Elm enforces semantic versioning.

thanks to that, in Elm, depending on ranges of versions is the norm for libraries (but not for applications).

hdgarrood commented 6 years ago

It's not really true to say that Elm enforces semantic versioning, unfortunately. Elm only requires you to do a major bump if you change the type of something in a way that could require downstream users to modify their code to get it to compile again, but that's only one way that a change could be a breaking change. It's definitely possible to have breaking changes which aren't reflected in the types. Additionally, if gx aims to be a general-purpose package manager it doesn't help us if it's only possible to do this with certain languages.

renatoathaydes commented 6 years ago

I think it makes sense to trust them — it's usually going to be inaccurate, because authors cannot predict the future, but it's also usually pretty close to being accurate...

Using the same rationale, it makes sense to trust that if you don't change type signatures, you don't change semantics in a way that could break user's code. I agree verifying semver by looking at types is not 100% effective but it's much better than no checks at all.

Additionally, if gx aims to be a general-purpose package manager it doesn't help us if it's only possible to do this with certain languages.

Correct, which is why if you aim to be a general-purpose package manager, you cannot hope to resolve dependency trees (and the possible conflicts) in a perfect way (or even in a good way, sometimes), unfortunately. Gradle has been trying but the only solution they came up with was to allow users to specify which strategy to use to resolve dependency conflicts, as well as allow forcing certain dependencies to take a specific version explicitly... so I think that's the only way to go if you do not want something more advanced like Elm does.