Project Versions

As your project evolves, you might reach points where you consider it ready for use at some level. Usually, we mark this point by generating a new release and marking it with a version number.

Versioning is a very project oriented thing, but there are some conventions. The most common system you will see looks something like this:

  • <major>.<minor>.<patch>

Where:

  • major is a version significantly different from a previous version
  • minor is a new version that keeps the same user interface
  • patch fixes problems in the current version

Major versions are not required to keep the old user interface. They can make significant changes to how the application is used. Minor versions always maintain the same user interface, but may add new features, being careful not to break any old features.

Patch versions are issued to fix problems. These changes do not alter the functionality of the program, they just fix bugs that should not be there.

I use some simple commands in my project Makefile to make managing these numbers easier.

When I use this system, I create two hidden files in the project:

  • .version - tracks the current version of the project
  • .build - tracks the number of times I actually build something using Make

Note

That last number is just for my interest. I like to see how my development work proceeds. It is also useful for students, since I can get some idea how they are working when they work on my projects.

Here is a part of my project Makefile that controls versioning:

Makefile snipet
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
VERSION		:= $(shell cat .version)
BUILD		:= $(shell cat .build)

# version management
.PHONY: version
version: ## show current project version
	@if ! ([ -f .version ]); then \
		echo "v0.0.0" > .version; \
	fi
	cat .version

.PHONY: build
build: ## show current project build number
	@if ! ([ -f .build ]); then \
		echo "0" > .build; \
	fi
	cat .build


.PHONY:	tag
tag: ## tag this version
	@echo $(VERSION)
	git tag -a $(VERSION) -m "Auto tag $(VERSION)"

.PHONY: bump-build
bump-build:	## bump version build number
	./scripts/inc_version.sh build;

.PHONY: bump-feature
bump-feature: ## bump version feature number
	./scripts/inc_version.sh minor

.PHONY: bump-release
bump-release: ## bump version release number
	./scripts/inc_version.sh major


Lines 1-2 load the current version and build number into the Make environment so I can use those values later.

The version and build targets use bash shell scripts to check if the hidden .version or .build files are present. If not, they get created. THen the contents of those files is displayed for reference.

The three targets named bump-xxx will increment one of the version number parts. In this example, I named the three fields release, feature and build which reflects how i use them better that the traditional names.

All of thse commands depend on a bash script that does the hard work of breaking the version number down, modifying it, and putting the parts back together.

Here is that script:

inc_version.sh
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/usr/bin/env bash

if !([ -f .version ]); then
    echo "v0.0.0" > .version
fi
version=`cat .version | sed -e "s/v//"`
major=$(echo $version | cut -d\. -f1);
minor=$(echo $version | cut -d\. -f2);
build=$(echo $version | cut -d\. -f3);
if [ "$1" = "major" ]; then 
    major=$(printf "%d" $(($major + 1))) ;
    minor=0 ;
    build=0 ;
fi
if [ "$1" = "minor" ]; then 
    minor=$(printf "%d" $(($minor + 1))) ;
    build=0 ;
fi
if [ "$1" = "build" ]; then 
    echo "building $build"
    build=$(printf "%d" $(($build + 1))) ;
fi
version=$(printf "v%d.%d.%d" $major $minor $build) ;
echo "$version" > .version
echo `cat .version`

The remaining target in the Makefile`` snippet runs a git tag command to mark the last commit. I run this after I commit and amm ready for a new version number.

I also add a command to my main target that increments the build number. Here is the script that does this:

inc_build.sh
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#!/usr/bin/env bash

if !([ -f .build ]); then
    echo "build=0" > .build
fi
build=`cat .build | sed -e "s/build=//"`
if [ "$1" = "reset" ]; then
    build=0
fi
build=$(printf "%d" $(($build + 1))) ;
echo "build=$build" > .build
echo `cat .build`

This is yet another example of using Make to streamline your development process. You should explore how other developers manage their work, learn from them, then implement your own custom tools. What makes YOU more productive is what we re trying to figure out!

Warning

Of course, your boss may already have such procedures. You probably will be asked to use those. But nothing prevents you from working with your team to find better ways to do things. Your team will thank you for that, unless you try t ram your ideas down their throats! YMMV!

Scene: Nick’s Basement, full of ozone!

Ada: Why do people always try to get you to do things THEIR way?

Alan: For some reason, humans always think they know the best way to do things, and the world would be so much better off if everyone just followed their lead!

Nick: Are we talking politics AGAIN?

Ada: I hope not! Those discussions always leave me wanting to run away screaming!

NIck: I am right behind you!

Alan: Well, Nick, let me tell you the right way to run your lightning machine!

Nick and Ada: ALAN! Shut up!