Skip to content

Instantly share code, notes, and snippets.

@JakeGinnivan
Last active August 24, 2018 06:37
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JakeGinnivan/6908885 to your computer and use it in GitHub Desktop.
Save JakeGinnivan/6908885 to your computer and use it in GitHub Desktop.
Versioning, CI/CD

I have been doing a heap of reading about semver and how you can include build meta-data and other things, but I am really struggling to find a way to fit SemVer and Continous Delivery together.

Currently I am the primary maintainer or a main contributor for for:
https://github.com/TestStack/White
https://github.com/JakeGinnivan/VSTOContrib
https://github.com/DbUp/DbUp
https://github.com/Code52/DownmarkerWPF
https://github.com/TestStack/ConventionTests

And quite a few other smaller projects (https://github.com/JakeGinnivan?tab=repositories)

Most of these projects do not use SemVer, and I hit a button on TeamCity and it takes the last successful build and pushes it to NuGet, or the clickonce installers to Azure. How can SemVer work into the release process better, I have ideas that for clients SemVer could also be used to describe the type of changes in a project to the StakeHolders, they see a Major Version release = New Features, Minor = Adaption/Improvement to current features and Patch = Bug Fixes. Basically, I want a way of working which works in most scenarios as a default, then change from there. Currently it seems SemVer and Continuous Delivery are incompatible, which is a real shame.

I use GitHub Flow most of the time, I really like this as it works very well with Continous Delivery and Open Source. It also works awesome on commercial projects with large teams.

I have written two blog posts on different ways of releasing SemVer software, trying to put the WHOLE picture in place, including VCS management and build server. They are http://jake.ginnivan.net/release-nuget-semver-packages-from-teamcity and the second is using Git-Flow and GitFlowVersion to version http://jake.ginnivan.net/git-flow-versioning

My issues

  1. v2.0.0+ci5 is semantically the same as v2.0.0, so I can't publish it as a nightly. Honestly I think v2.0.0-whatever should be behind v2.0.0 and v2.0.0+whatver should be after v2.0.0. This would make conventions of nightlies or even CI builds of your package simply X number of builds after the last released version. Then I release the next version, it is tagged and it builds from there..
  2. Continuous delivery means that any CI build which goes through your pipeline should be able to be promoted, but SemVer really is a checkpoint and communicating the type of changes in that release via version numbers. Do we really mean that the CI package is a candidate which can be promoted, then if I tag, that is a new build which can be promoted separately?

I have other issues/questions, but they are mainly related to the above points, I would love to see how others are using CI and SemVer together. There doesn't seem to be many examples out there...

An idea?

Lets take GitHub flow, and GitFlowVersions ideas and try to create a simpler more lightweight solution.

  1. Follow GitHubFlow
  2. Builds are v{lastmajor}.{lastminor}.{lastpatch+1}+ci{numberOfCommitsSinceLastTag}
  3. Introduce conventions of floating tags of MajorChange and MinorChange. If at any point a pull request is a breaking major change, or minor change (rather than a patch) you can use one of these tags to affect the generated version. For instance if there is a MajorChange tag since last release then the version will be v{major+1}.0.0+ci{numberOfCommitsSinceLastTag}
  4. Beta's/RC's are also floating tags, making the current version {lastversion} + {floating tags}. A MinorChange tag plus a Beta tag a few commits later would create a build with version v{lastMajor}.{lastMinor+1}.0-beta, subsiquent commits would have the suffix -beta{numberOfCommitsSinceBetaTag}

This could be a really light weight way to keep track of the SemVer, then when you release you simply tag. You can delete or leave those floating tags, it doesnt really matter. If they are behind the last release, they mean nothing. Do we even need to track the potential next version? This would also make the publish as pre-release a convention, only version tags are released as stable versions. And all released versions can be rebuilt at any time.

Feedback on this topic would be great

@shiftkey
Copy link

Question The First

v2.0.0+ci5 is semantically the same as v2.0.0, so I can't publish it as a nightly. Honestly I think v2.0.0-whatever should be behind v2.0.0 and v2.0.0+whatver should be after v2.0.0.

Well, this isn't adhering to semver any more - but I don't think this is essential to the discussion at hand.

Personally, I'd have different sorts of releases without needing to abuse the custom metadata:

  • release candidates - code which might be unstable - v2.0.0-rc1 for example
  • patch/point releases - code with minor changes and bugfixes - v2.0.456 (the fact the number is so big - meh)
  • minor releases - code with additional features which remains compatible with previous releases - v2.1.0
  • major releases - a release with breaking changes or significant rewritten code (feature set is irrelevant) - v3.0.0

There's a finite set of transitions between states here, so even though there's four different types it's still easy to grok what to expect from a release (exactly the point of semver).

By leaning too much on the build server for generating your version number, you probably tie yourself to a specific axis of changing these versions. In some of my side-project I've found myself switching back to scripts which update the version locally, rather than having to jump through hoops to manually bump the version on the build server.

This would make conventions of nightlies or even CI builds of your package simply X number of builds after the last released version. Then I release the next version, it is tagged and it builds from there..

I guess it comes down to how you co-ordinate your releases. CD generally wants every change to be something you could possibly ship (which is good), but on the other hand you want to be really controlled about your releases (which is also good). The benefits of mixing them together, however, I'm a bit skeptical about.

Question The Second

Continuous delivery means that any CI build which goes through your pipeline should be able to be promoted, but SemVer really is a checkpoint and communicating the type of changes in that release via version numbers. Do we really mean that the CI package is a candidate which can be promoted, then if I tag, that is a new build which can be promoted separately?

Keeping your code in a close-to-shippable state is awesome, and something we all should strive for. If a side-effect of that is you do more interim releases that pass your same pipeline, what's the downside?

A couple I can think of:

  • unless you write the documentation or release notes up front for changes, it probably won't happen
  • using your pipeline as a poor substitute for a QA process (and defects slipping through to users)

But those are things we can mitigate/resolve these risks:

  • documentation/release notes must be written before integrating changes
  • CI builds are done to a separate feed (see MyGet) and can be promoted to the "official" feed after a QA process is completed

On the Idea

I think doing less CD (note: still do the same degree of CI) and more self-versioning (I just made that word up) would make for a much simpler experience.

A couple of things I'll call out:

Introduce conventions of floating tags of MajorChange and MinorChange.

Major/breaking changes are significant enough that I think automating this is a bit scary.

If at any point a pull request is a breaking major change, or minor change (rather than a patch) you can use one of these tags to affect the generated version.

Why not just rev the version inside the pull request before merging it in?

Beta's/RC's are also floating tags, making the current version {lastversion} + {floating tags}.

Generally you're either making release candidates or not (i.e. things have stabilized enough). Does that require it's own tag? I don't think so.

@JakeGinnivan
Copy link
Author

@shiftkey, in terms of it being scary about automating, that is not my intention. I was thinking.

Oh this PR is a breaking change, but worth bringing in. Merge then tag master as 'MajorChange'. That way i haven't released, but people can see there is a breaking major change which is inreleased. The build numbers on the CI server will reflect this.

Thinking this could be just as easy as a script which bumps versions, it just uses tags instead

@JakeGinnivan
Copy link
Author

But generally agree with you.

Master should always be shippable, but shipping is a decision. And I am happy that the version bump along causes a rebuild, then that is released.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment