Sep 14th, 2018 - written by Kimserey with .
Few weeks ago I explained how Gitversion can be used to apply semantic versioning to projects based on commit and tag history. Today I will dive deeper in the purpose of versioning and introduce a flow which can be followed to version open source project libraries by looking into four important parts in the lifecycle of an open source library.
Semantic versioning is ideal for versioning libraries. It is formed by three numbers {major}.{minor}.{patch}
. Each number is used to indicate to the user the level of safety for upgrading the library.
Following this format allows to have a predictable version number which can be used by applications or libraries to setup upgrade rules and avoiding version lock.
For example, consider the following:
library A
on version 1.5.0
library B
references our library A
on version 1.5.0
application A
references library A
on version 1.5.0
and library B
We release a new version 1.6.0
with a new feature which application A
wants to use.
If the versions aren’t predictable, both application A
and library B
will need to upgrade to library A
1.6.0
.
With Semantic version, the version number is predictable and a bump of the minor
specify no breaking changes. Therefore a compatibility rule on library A
can be set to support any version of library B
1.x.x
as long as it is higher 1.5.0
.
The initial commits before release follow the version 0.x.x
. Before the first major
bump any releases are to be considered as alpha
.
For a library, the easiest way is to manage everything around a single master
branch and make any changes in a branch (or fork in Github) which will be merged via pull-request
.
master
and pull-request
‘d back into master
master
and pull-request
‘d back into master
master
and pull-request
‘d back into the main repository master
Every single commit has a version attributed to it. This is handle as such:
master
, the version format is constructed as such {major}.{minor}.{patch}+{commit}
. For example, for the first commit after release tag 1.2.0
, the version will be 1.2.1+1
while for the fifth commit it will be 1.2.1+5
{major}.{minor}.{patch}-{branch}+{commit}
. For example, 1.2.1-mybranch.1+1
, 1.2.1-mybranch.1+5
or 1.2.1-mybranch.1+3
1.2.1-PullRequest.1+1
Once the branches and pull requests are merged, the resulting merge is another commit which then has an attributed version +{commit}
.
Having a version for each commit becomes very handy when working in a continuously integrated pipeline which we will see next.
Continuous Integration is a must for open source projects. We must be able to know that master
branch is always in a workable state. We also must ensure that all pull-requests are valid and do present a danger tobreak master
. And lastly, we must ensure that other branches created to develop new features are also valid. To do that we would setup a CI server or use a service like Appveyor.
This is where a unique version for each commit is handy as on each commit pushed to the repository, a build will be triggered where the name of the build will be set to the version of the commit. This allows us to easily identify how the build relates to the repository as we will be able to know for which branch, for which version and which commit from the last release is the build trigger for.
The last important point is to handle releases. Since the library code changes all occur on master
branch, we do not want to operate in a continuous delivery approach were every change on master
would yield a release. Instead we can use tag
to trigger a release which for a .NET library will produce a NuGet package .nupkg
and generate a release on GitHub with the package attached to the release. Whenever we want to release, we tag the master
branch with the exact release number.
Today we saw the different aspects of versioning for an open source library. We started by looking at semantic versioning and the reason why we would follow it. Then we moved to look into branching and how commits are versioned. Lastly we talked about continuous integration and releases. Hope you liked this post, in the next post, I will show how we can achieve the points we discussed today for a .NET library, see you next time!