Semantic Versioning

Oct 22, 2010 at 10:47 PM

We had an earlier thread on this which gives good context, and I'd like to resume this discussion.  There are two interesting topics:

  1. Giving semantic to the version string
  2. Dealing with preleased bits

I'd like to start focusing on #1.

I definitely like the simple conventions outlined in http://semver.org.  Quoting from there:

Consider a version format of X.Y.Z (Major.Minor.Patch). Bug fixes not affecting the API increment the patch version, backwards compatible API additions/changes increment the minor version, and backwards incompatible API changes increment the major version.

I would love to adopt this system for NuPack, but what I'm struggling with is how to make that work with the various versioning schemes that are being used by projects out there.

So question #1: does the NuPack package version need to match the library's version

My take is that it has to, otherwise it's very confusing.  The library version needs to be recognized from the package version.  I hope we're mostly all in agreement here.

Question #2: assume yes to #1, how do we deal with packages/libraries that don't use the semver scheme?

e.g. NHibernate has version 2.1.2.4000, which has for number instead of SemVer's 3.  One option is to loosen the SemVer rules to allow this, and simply say that both the 3rd and 4th numbers are treated as the patch, meaning they add no new APIs.  However, this will blow up if that's not the case.  e.g. maybe 2.1.2.x does add new APIs over 2.1.1.x.  And if so, NuPack will make incorrect versioning decisions.

In fact, even if the component uses the X.Y.Z format, there is no guarantee that it's in fact following the SemVer semantic with that version string.

Maybe the answer is to say that to participate in the NuPack world, you have to make sure that your versioning scheme is SemVer compatible.  But would doing this leave out some perfectly good libraries that just happen to do versioning their own way?

Would appreciate your thoughts on this as we try to come up with our NuPack versioning story.

Oct 23, 2010 at 2:21 AM
Edited Oct 23, 2010 at 2:27 AM

Maybe NuPack could work with the Assembly Informational Version (which is also the Product version).  It is a  free-form string but this might server as the focus for some conformity of Package versioning, semantic or otherwise.  I'm not sure if there is a way to avoid confusion if the various version values are inconsistent for a particular package/component (e.g., AssemblyVersion = 2.1.0.21, AssemblyFileVersion=2.1.2314.1, AssemblyInformationalVersion=1.0.1, NuPackVersion=1.0.1).

-tk42

Oct 23, 2010 at 12:29 PM
When it comes to versioning, we should go down the path of guidance. Dictating to people how they should version is a sure way to guarantee they will not participate.
People should be able to version in whatever way they want and NuPack should be able to handle that. Provided they always increase the version number up.
The package version and assembly version should be a match, except when it comes to prereleases. Then the package version should be appended with some text to signify it's a prerelease (as in "beta" or "alpha"). It really doesn't matter what the text is, just that there are alphabetical characters at the end of the package version that signifies it's a prerelease.
#1: does the NuPack package version need to match the library's version?
It should, but we need to give a best practice here. It's not something we should try to force. We need a page detailing how to create packages and things you should consider (we may already have this). When anyone creates packages that don't follow the guidance, the community will chastise them until it's fixed or they won't use their tool. It's as simple as that. Unless they are a big tool like Castle Windsor or NHibernate, they probably will move onto tools that follow the guidance better.
From what we've seen in other communities, this works. Package version == library version as a standard and best practice, but not a requirement.
Question #2: assume yes to #1, how do we deal with packages/libraries that don't use the semver scheme?
We just get packages with the highest number version and detect conflicts with existing libraries and deal with them and move on. NuPack shouldn't do any more than that.
Coordinator
Oct 24, 2010 at 6:19 PM
Agree with Rob (ferventcoder) that this is one case where we may be able to dictate the package version scheme.

In general, packages are associated with a single library. While a package may contain multiple libraries (or none at all in some cases), typically there's one in there that's the "primary" library and most people will version their package according to that library.

However, we have to careful to remind everyone that packages and assemblies are not 1 to 1. What that means is if NHibernate or some other project doesn't follow the SemVer convention for their assembly versions, that doesn't mean they can't follow SemVer for their package version. That's a good thing!

Our guidance would be as follows:
1. We recommend that you follow SemVer versioning with your assemblies.
2. We require that you follow SemVer with your package.

And to clarify, for #2 when I say require, what I really mean is we're going to assume your package version follows SemVer and apply the appropriate semantics and behavior as if it were SemVer.

Make sense?

Oct 24, 2010 at 7:41 PM

Hmmm, I think you and Rob are saying different things here.  We either require the package version to follow SemVer or we don't.  If we do, then it implies that packages like NHibernate need to change their version from 2.1.2.4000 to something else, which will then be different from the version of the assembly it contains (which is the version users know about).

Of course, ideally we would get all the assemblies (and hence the package) to follow SemVer, but that may not be realistic with everything that's out there.

And also, do note that even if an assembly follows the X.Y.Z format, it may not follow SemVer.  e.g. 1.2.3 might add new APIs over 1.2.2, which violates SemVer (and would cause us to make incorrect decisions).

So I think we all agree that we'd like everything to be SemVer, but I'm not seeing a super clean path to get there.  Seems it's the choice between:

  1. Having a package version different from assembly version, probably causing a lot of confusion
  2. Forcing all assemblies to switch to SemVer, which is a tough sell.

Or here is an idea: we can let a package declare whether the package version is using SemVer.  If it is, all is good.  If not, we just don't make any assumptions between two versions of the package, meaning that things will be more painful, but we will avoid making incorrect decisions.

Thoughts?

Oct 24, 2010 at 7:56 PM
As long as people version up, we should be fine. The only restriction we should put on people is that v2.0 is greater than v1.0. That's really all we should set as a requirement. Nearly every library out there does that. In fact I can't really think of any that do not.
____
Rob
"Be passionate in all you do"

http://devlicio.us/blogs/rob_reynolds
http://ferventcoder.com
http://twitter.com/ferventcoder

Oct 24, 2010 at 7:58 PM
In other words, the guidance IMO should be people can version however they want. Please make your package version match your assembly version. And for prereleases, we'll release guidance on how to do that.

SemVer is a suggestion from us as a best practice for versioning in general and how NuPack will do it's internal versioning. Make sense?
____
Rob
"Be passionate in all you do"

http://devlicio.us/blogs/rob_reynolds
http://ferventcoder.com
http://twitter.com/ferventcoder
Oct 24, 2010 at 8:05 PM

Actually, I'm not quite following you. :)  Let's take a specific example:

  • A 1.0 depends on B 2.1.4
  • The feed only has B 2.1.3

Do we use 2.1.3 or fail?  According to SemVer, we can safely use it since 2.1.4 could not have added any new APIs.  But if they're not following SemVer and added APIs in 2.1.4 that A 1.0 needs, things will break.

How then can we make the right decision unless we know whether we can assume SemVer for a package?

Coordinator
Oct 24, 2010 at 8:15 PM
Yes, David's point is exactly my point when I said we would "require" SemVer for the package version. I meant that we would make these kinds of decisions because we're assuming SemVer to be in play. I think if we don't, then what's the point of even providing SemVer guidance. It would make no difference to the package manager.

I see three options:
1. We continue as-is and don't assume SemVer
2. We assume SemVer and apply the sort of behavior David describes.
3. (worst case) We allow packages to flag themselves as following SemVer and in those cases, we follow this behavior, otherwise we follow current behavior.


Oct 24, 2010 at 9:19 PM

I'm mostly with Phil that we should just assume SemVer semantic and accept that some scenarios like above may break if they don't follow it.  The sticky point is that if they use a version number that looks like 2.1.2.4000, it's basically an invalid version string for SemVer.  But we can't disallow such string as they're commonly used by the big guns.

So I'm thinking we should follow a relaxed SemVer such that if you have 2.1.2.4000, we basically treat the last two numbers (2.4000) as SemVer's 'Patch'.

Thoughts?

Oct 24, 2010 at 9:20 PM

+1 #3

By making it explicit opt-in, you also lead devs to reading semver guidance and make a conscious choice (and keep it in mind subsequently).

It also allows flexibility wrt existing libs who don't adhere

from android nexus1

On Oct 24, 2010 5:16 PM, "Haacked" <notifications@codeplex.com> wrote:
> From: Haacked
>
> Yes, David's point is exactly my point when I said we would "require" SemVer for the package version. I meant that we would make these kinds of decisions because we're assuming SemVer to be in play. I think if we don't, then what's the point of even providing SemVer guidance. It would make no difference to the package manager.
> I see three options:1. We continue as-is and don't assume SemVer2. We assume SemVer and apply the sort of behavior David describes.3. (worst case) We allow packages to flag themselves as following SemVer and in those cases, we follow this behavior, otherwise we follow current behavior.
>
>
>
>
Coordinator
Oct 24, 2010 at 9:28 PM
Edited Oct 24, 2010 at 9:29 PM

Agree with Fowler. Let's look at what other package managers do. Here's the versioning story for Ruby Gems: http://docs.rubygems.org/read/chapter/7 and this talks about specifying versions http://docs.rubygems.org/read/chapter/16

Coordinator
Oct 24, 2010 at 9:32 PM

After reading through the documentation, there doesn't appear to be any magical Semantic versioning enforcement in Gems.It seems to me their recommendation is to be explicit about specifying a version and version range. Their versioning policy appears to be a guideline "for best results", but the software doesn't enforce it or even rely on that information in any way.

Is that how you read that?

Oct 24, 2010 at 10:38 PM

Hmmm, is it possible that we completely missed Rob's point on SemVer this whole time?  I think we all took it as a set of rules that our software (NuPack) would follow to decide what version can be used.  But maybe he only meant for it to be a convention, such that the author of a package manually defines dependencies based on that convention?

e.g. if I depend on Foo 1.1.1 and I know Foo follows SemVer, then I explicitly say that I need Foo version >= 1.1.0, and <2.  But the software just stays dumb and follows the rules without ever giving semantics to the version parts.

Rob, was that what you meant?

Coordinator
Oct 24, 2010 at 10:57 PM
Rob, please correct me if I'm wrong, but I think that *is* what Rob said all along. ;)

But if that were the case, why is there a bug to support SemVer at all? If the idea is to only supply guidance, then this should be a "Documentation Bug", right?

I'm all for providing this sort of guidance. I think it makes sense, it's a standard that many projects are applying, and it does the entire world some good if we can all just get along and agree on that.

But it appears, there's nothing for us to actually *do* within the NuPack codebase. It seems all we need to do is make the SemVer guidance prominently located in our documentation and start marketing/promoting the guidance that every project should follow it.

If that's the intention all along, I propose we change the bug to state that.

Oct 25, 2010 at 12:47 AM
If there is an explicit version dependency and it is missing, it should fail. Your example below outlines the fail scenario.

Actually, I'm not quite following you. :) Let's take a specific example:
  • A 1.0 depends on B 2.1.4
  • The feed only has B 2.1.3
Do we use 2.1.3 or fail

It should fail. The package author was explicitly stating they wanted a particular version. This helps in the scenario later where nupack can look across multiple feeds to try to find a specific version before failing.
Oct 25, 2010 at 12:49 AM
Yep! Guidance. Not sure what the bug was. Perhaps it was meant to be a documentation bug or that nupack itself should set its own versioning model (internal versioning) to semver.
____
Rob
"Be passionate in all you do"

http://devlicio.us/blogs/rob_reynolds
http://ferventcoder.com
http://twitter.com/ferventcoder

Oct 25, 2010 at 3:13 AM

Great, at least now we're all on the same page :)

But then as Phil says, there is essentially nothing for NuPack to do relating to SemVer.  Well, I do thing we're missing a Strictly Less Than comparison.  Copying my scenario from above:

If I depend on Foo 1.1.1 and I know Foo follows SemVer, then I explicitly say that I need Foo version >= 1.1.0, and <2. 

Currently, but don't have a way of expression '<2', shot of writing something crazy like MaxVer=1.999.

 

Oct 25, 2010 at 3:21 AM
Of course this goes back to the other conversation we started with min/max version.
____
Rob
"Be passionate in all you do"

http://devlicio.us/blogs/rob_reynolds
http://ferventcoder.com
http://twitter.com/ferventcoder
Coordinator
Oct 25, 2010 at 3:26 AM

Ugh. XML bites us in the but. A few ideas:

1. Instead of max/min we have the following four: minVersionInclusive, minVersionExclusive, maxVersionInclusive, maxVersionExcusive.

2. We change max/min to version range and allow a simple syntax: versionRange=">= 1.0 && < 2.0"  (or something like that)

 

Oct 25, 2010 at 3:37 AM

I meant #1 over #2.

But I was also thinking, what would be so wrong with saying something like: 

    <dependency id="Foo" version="1.1.1" SymVer="true" />

And all that would mean is 1.1.0 <= v < 2

So it would be a very shallow SemVer support that just translates it into the proper range driven by the SemVer semantic.  There would be no trace of SemVer once we're done processing the .nuspec file.  It would just be an easy shortcut to let you easily specify the correct range when you know that SemVer is at play.

Yes, all that is somewhat moot in the short term if we don't deal with Binding Redirects (per other thread), but it's still worth ironing out, as we'll get there.

 

Coordinator
Oct 25, 2010 at 4:06 AM

I like it! If we're going to promote SemVer as our guidance, having this shortcut will provide a benefit to those who actually listen to us numskulls! :) Guidance is one thing. But Guidance with benefits is even better! :)

Oct 25, 2010 at 5:12 AM
I'm all about guidance with benefits. :D
____
Rob
"Be passionate in all you do"

http://devlicio.us/blogs/rob_reynolds
http://ferventcoder.com
http://twitter.com/ferventcoder
Oct 25, 2010 at 6:42 PM

See http://codebetter.com/blogs/sebastien_lambla/archive/2010/10/15/building-polyglot-packages-for-openwrap-and-nupack.aspx for my current advice for nupack packages compat with openwrap. Any change that'd break the resolution would be a additional burden in supporting the nupack packages, and would make versioning impossible across those multiple repositories.

Not against semver, but that's already covered by the very simple = 2.1 dependency, while still letting ranges happen. Introducing a special semver mode is problematic. You're semver, you rely on something not semver, is your consumer semver-friendly?

Either it's semver all the way with prescriptive semver enforced on package publishers, or verioning as it stands should be enough as it is compatible with providers that do provide semver, provided the consumer specifies their dependencies correctly.

Oct 25, 2010 at 7:02 PM
Seb - good stuff. I firmly believe that semver is guidance. SemVer just says use the first three digits and leave the last 0. Which is really close to what you are doing. For you the revision can be ignored b/c it's for patches kind of concept. SemVer states that the third number is for patches (i.e. 2.5.0 to 2.5.1 as in Castle.Windsor). So SemVer ignores the last two really. But it's guidance, the package manager shouldn't do anything itself to require it. Doing some fancy stuff based on a setting in a spec is probably alright as long as it's not a requirement (which is where I believe Phil was going).

For me a package manager should only compare two version numbers and decide the higher number increment is the newer version. Anymore than that and it's doing more than it should.

Just a curiosity, and this is really for all package management tools. The concept of Pre-Releases.

I see a pre-release attaching something alphabetical to the end of the package version as we see as the current working model with gems.

As in nhibernate.v3.0.0beta1 would mean it's a pre-release and I would do something special to get it.

How do you plan to implement the pre-release feature for OW (if at all)?

I have my own opinions here but it's good to get other perspectives. :D

Oct 25, 2010 at 8:19 PM

@Rob, what you're saying here seems different from the understanding we had above.  e.g. above you agreed that if I depend on 2.1.4 and the feed only has 2.1.3, then it should fail.  But here you say "a package manager should only compare two version numbers and decide the higher number increment is the newer version. Anymore than that and it's doing more than it should" which seems to imply that the 3rd number should be ignored, and hence that we would successfully bind to 2.1.3.

Can you clarify?  We really need to be crystal clear about the semantic we want to follow to make sure we don't end up with a misunderstanding later.

Oct 25, 2010 at 8:35 PM

When trying to figure out the most recent version, it should get the highest non prereleased version number. That's where I was making that statement from.

Dependency management is different and where a package manager should be really smart.
When someone wants an explicit version, they've stated that for a reason. It would be deceptive IMO to give them a different version than the package is asking for. If they ask for an explicit version and it is not there, it should fail getting the dependency. Does this make sense?

If a dependent package is using semver (let's call it bar), I can, in providing my package of foo that depends on bar, give it a range instead of a specific version. That's the benefit of following the guidance. The tool itself may also give some benefit as well, but it's a little outside the context of where I am at at the moment.

____
Rob
"Be passionate in all you do"

http://devlicio.us/blogs/rob_reynolds
http://ferventcoder.com
http://twitter.com/ferventcoder

Oct 25, 2010 at 9:01 PM

Re-read what you were asking. I believe you were talking about ignoring the patch number if someone wanted a specific version. As in someone is using SemVer and they want 2.5.2 but 2.5.3 is available from a package that states it is using SemVer.

I didn't really think about that. I think I would want it to still use the explicit reference I asked for, otherwise I could define a range.

I would say when a literal (explicit) version is asked for, we should give it to them.

On the other hand, if you know a package is using SemVer and you take a dependency on it, would it be acceptable to put 2.5 when the version coming from that package is 2.5.1? Then it would automatically upgrade to 2.5.2.

I still don't think I like it though. That's what the ranges are for. I think if someone states an explicit version, that is what they get. If it's not there, it should fail.

The guidance benefit to me may come more in other libraries trusting your versioning concepts more if your package uses SemVer and I want to depend on it. Them I am more confident to give it a range when I define my spec.

Thoughts?

Oct 25, 2010 at 9:09 PM

If you are happy with 2.5, why define the dependency as 2.5.1? If the last component is being ignored, that's what we do with the revision (the fourth digit is ignored in dependency and automatically upgraded to the lastest revision found).

For me, it doesn't matter for users, but the update behavior in OW will be different.

As for the string for pre-release, the feature is already designed, we put those pacakges in different namespaces. Unless you define that exact namespace, we won't find it in any repository.

Oct 25, 2010 at 9:17 PM
Completely different concept for a pre-release. That's based on when you create the wrap from a packaging perspective then?

Interesting. Don't know what direction we'll go. I still like the elegant way that gems does it. It's so simple in design yet effective.
Coordinator
Oct 25, 2010 at 10:48 PM

I’m curious what you mean by a different “namespace” Seb. When you say “namespace”, do you mean that as an OpenWrap concept? I think we’ve been leaning towards the SemVer means of specifying a pre-release version per what Rob described.

So for a released version, you use a normal version number: 2.1.1

For a pre-release version, you add alpha chars to the end. 2.1.1beta1

By default, we would filter out any versions of a package with the pre-release moniker at the end. Keep it simple for v1.

Oct 26, 2010 at 11:02 AM

We do the same thing, except the namespace (here, beta1) is part of the name of the package, rather than the version. From the command line, you'd add-wrap nhibernate [-alpha | -beta | -edge] and that would go and add a reference to the edge/nhibernate package.

Then versioning "just works", because a query for depends: edge/nhibernate 2.1 always triggers either a resolution in the edge namespace or in the default namespace, automatically updating to release when one happens.

Note that there's a two-way compat between the namespace scheme and semver's alphas, we just make it part of the identifier rather than part of the version, one level up in the architecture.

Coordinator
Oct 26, 2010 at 4:26 PM

Ah, ok. I think I understand. So the primary difference is using the package name rather than package version to indicate pre-release. In either case, the pre-release package is still a different package.

Out of curiosity, when you say "edge", what does that mean?

Also, I'm still a little confused. You mention that it's part of the name of the package. But then your example uses "edge/nhibernate". Is that the name of the package? Or is it in a URL "subfolder"?

Oct 26, 2010 at 5:14 PM

Yes, either way works.  I think using the package name is a bit cleaner.  In the end, it makes little difference to end users which way we go, as long as the client tool knows about this convention and makes it available via special switches.

I'm guessing 'edge' means daily build, and 'edge/nhibernate 2.1' is just the package name that happens to contain a /.

I think we may as well match the OW convention here to make interop between the two easier.

Oct 28, 2010 at 2:18 PM

edge would be CI builds, alpha and beta would be what they mean, and if you want to create your own qualifier you can do that too.

Basically, the name of the package is edge/nhibernate, which is a different name (and as such a different package) than nhibernate. This means you can have beta/nhibernate-2.1 and nhibernate-2.1 coexisting withou conflict, and versioning ranges can still work as usual.

 

Oct 28, 2010 at 5:50 PM

@serialseb: sure, you can create any qualifier, but only a known set will get special treatment on the client side.  e.g. you can do [-alpha | -beta | -edge], but you can't do -foo.

Editor
Feb 25, 2011 at 11:27 PM

Restarting this thread. I like a convention based idea, but also like the idea of building the knowledge into the tool like -beta or -edge....

Feb 20, 2012 at 9:18 PM
Edited Feb 20, 2012 at 9:18 PM

In SemVer 2.0.0-rc.1, pre-release and build versions are covered in points 10 and 11. I'd really like to see proper support for these in Nuget.

Currently, the pre-release version only has partial support - only one identifier is allowed, whereas SemVer specifies many dot separated identifiers.

Build version is not supported at all.

On the subject of enforcement of SemVer, I don't think this should be the case. SemVer is a good standard to follow but IMHO it should be optional. NuGet should, ideally, recognise and support it but not force it. A good case in point is the versioning soup which log4net has recently entered into - see my comments at http://haacked.com/archive/2012/02/16/changing-a-strong-name-is-a-major-breaking-change.aspx#feedback

Developer
Feb 20, 2012 at 9:24 PM

We do have a work item to umake this change. however making a schema breaking change (older versions of NuGet would not be able to parse this version) tends to be quite a bit of work. We're focussing on some other aspects of NuGet which makes it hard to address this issue in this release. Perhaps we could get to this in the next (1.8) release. We debated enforcing Semantic Version in the past, however that would break a a ton of existing packages on the gallery so we decided against it.