Version the code when you build a release on your CI server.
Normally this would be on a check in to develop, master if you don't have a develop branch
If you version the feature branches you will get into trouble as there is no clear definition of which is 'the latest' version.
Because you are lacking the develop branch, and both changes are incompatible you will have to:
- merge foo into master, build v2
- merge master into bar, fix incompatiblities
- merge bar into master, build v3
I've used the notion of a release candidate for that purpose.
I assume you need a unique version number for every set of deliverables that you release towards QA. Lets do an example:
You are starting sprint 5. The version you are working off of in sprint 5 is 1.0.0
. You expect this release to be a feature release so the version for the software after sprint 5 would be 1.1.0
; therefore, the team is currently working on 1.1.0-RC1
.
The team finishes the last story in sprint 5. You assign the Version 1.1.0-RC1
to that commit/revision. The deliverables for 1.1.0-RC1
go to QA and they come back with 3 bugs which can be fixed within the remaining 2 days in sprint 5.
However, other team members have already begun working on sprint 6. You need a new VCS branch here! Since you expect sprint 6 to be another feature release, sprint 6 would yield version 1.2.0
; so these team members are working on 1.2.0-RC1
.
The bugfixes (that resulted from testing 1.1.0-RC1
) have to be done in another branch. The version they go into is 1.1.0-RC2
.
Work on 1.1.0-RC2
is done (that means: all known issues are fixed). The deliverables for 1.1.0-RC2
pass QA. The same commit as 1.1.0-RC2
in your VCS (or a new one if you keep the version number under version control) becomes 1.1.0
. That version is what you use towards the customer / end users.
You basically keep increasing the -RC number until QA is satisfied with the deliverables. The most recent RC is the same code version that you can ship without the -RC suffix.
If you expect more than 10 RCs for a sprint (which you shouldn't; that'd be too many bugs if you ask me) you should use leading 0s for the RC number. That makes sure the version numbers are sorted chronologically, e.g. 1.1.0-RC01
through 1.1.0-RC26
.
As to when the work from a sprint is not to be released to end users: i'd just keep on versioning as if you would release. This will of course create gaps in the public version history but thats totally fine IMHO.
If management wants a consistent public version number i'd still keep on labelling my versions as if i released at every iteration; maybe with an -INT
suffix. And at the same time keep a mapping of internal and public versions. Somewhat like this:
o @1.1.0-INT @1.1.0
|
...
|
o @1.2.0-INT
|
...
|
o @1.3.0-INT
...
|
o @1.4.0-INT @1.2.0
If you choose that route make sure that everyone involved (development team, management, support staff, marketing people, ...) are aware of this and know how to look up the interal<->external mapping whenever they feel like it. Otherwise, ... you can imagine.
On branching
I strongly suggest that you branch RC2 off of RC1 and continue with 1.2.0
in the main branch (e.g. develop
in git flow). When 1.1.0
passes QA you can merge it back into the main branch. Here is a bunch of git graphs showing how you could implement what i described above with git flow:
All stories for 1.1.0 complete; you tag the head of develop with 1.1.0-RC1
. Work on 1.2.0
continues in develop
o branch develop @1.0.0
|
... implement stuff
|
o @1.1.0-RC1
|
o
|
o
When the QA result for 1.1.0-RC1 comes in you branch off for 1.1.0-RC2. Work can then continune in parallel. You can merge the final RC of 1.1.0 back into your main branch once QA for that passes (or ealier if there is another urgend need).
o @1.0.0
|
...
|
o @1.1.0-RC1
|\
o o branch release/1.1.0-RC2
| |
o o
| |
o o @1.1.0-RC2 @1.1.0
|/
o merge release/1.1.0-RC2 into develop
|
o @1.2.0-RC1
In Git flow you'd not tag the version in the realease/*
branch but rather merge that into master
and tag that commit with 1.1.0
.
Best Answer
Read the version from metadata generated at build time
One thing you can do is to read the version from your Git tags at build time. Generate the version from your git tags after commiting, as a part of the release process. After determining the version at the beginning of the build process, store it as metadata in a text or key/value pair file, in a location with other build artifacts or metadata that won't get stored in your repo (it should be generated as a part of the build, after a commit has been made, to the local working directory only).
The version is therefore metadata that is a build artifact regenerated every build rather than stored in the repo. It is still available to your code because the file is generated at build time, before final linking or storage of external resources into your DLL or executable. Therefore, the final executable has access to this version information at runtime.
To accomplish this, you will likely need to create a script to generate the version file or a value/pair entry file (JSON, etc.). As a part of your build process, run the script, then run the rest of the build.
After a build, you will have the metadata file available, so if the source is run in a script environment, it can access the version, but it will always be the old version from the last build.
For example, in the base Python Packaging module,
setuptools
, there is an extension available called PBR that uses this method. It autogenerates the version info from git tags, including automatically incrementing based on a rule for every commit that doesn't have a tag. It then writes the version info into anxxx.egg-info/PKG-INFO
file that is a standard metadata file for Python packages. There are a functions to pull the version and other metadata info if needed in the live scripts. These files are typically included in.gitignore
, so they must be regenerated as a part of a package release or running tools that generate an executable.