Releasing Firmware Using Git


Read below to find out how to make your project managers and executives happy while reducing time wasted in tracking down bugs and issues in your firmware.

Often I encounter firmware development projects that use a "version.h" header to specify the project's version in terms of numbers.  It is a simple scheme but should not be used for any projects that will be or are producing devices at scale.  It is completely arbitrary and subject to abuse and human error.  The main problem with this scheme is that there is no way to trace what is actually running on a target device to where it came from because a developer can build literally anything by manually setting the version numbers.  What does a version of 1.3 mean?  0.9?  Are there local changes that have not been captured anywhere?  Who knows.

If you follow the simple procedure that I describe below, your firmware release headaches become a thing of the past.  No longer will you spend days tracking down issues as a result of some local developer-created changes, or sending the wrong images to production.  These are all costly mistakes that can be avoided.

In a nutshell, there is a way to use the Git version control system to setup a fully traceable commit-to-binary versioning scheme to bring your firmware project up to production quality standards.

The first thing you need to get your head around is that while you will likely be release firmware under the semantic versioning standard (i.e. major.minor.patch) , which I highly recommend, your firmware's version will still be fundamentally a string and not a series of digits crammed into a uint32_t. 

This string will ultimately convey the Git tag name for release builds but could be longer to hold additional tracing information for developer builds.

Think of this string as your firmware's identity - this version string will uniquely identify your firmware binary image from all others - no matter who built it - with direct tracing back to the Git commit that it came from.  It will even tell you if a developer tried to sneak in some local uncommitted changes into the build.

Alright, so how do we get this version string and what does it look like?

In your project's build script, you use the git describe command to generate a string that you pass to your compiler as a preprocessor define.  Let me show you how.

Windows:

set VERSION_CMD=git describe --tags --always --dirty
for /f "tokens=1" %%a in ('%VERSION_CMD%') do set VERSION=%%a

[Compiler Flags]: -DVERSION_STR=\"%VERSION%\"

Linux Makefile:

VERSION  := ${shell git describe --tags --dirty --always}

[Compiler Flags]: -DVERSION_STR=\"$(VERSION)\"

The describe command produces a very informative version string.  As an example, I am working on a feature branch off of master that had an earlier tagged release of v1.2.3 merged in at some point in the past, and I have made 8 commits to my branch since then and there are also some local changes that I have not yet committed.  Here's what my firmware version will be:

v1.2.3-8-g6f239e1-dirty

  • v1.2.3 is the name of the last tag found in the git history.
  • 8 is the number of commits since that tag.
  • g6f239e1 is the commit hash of the HEAD commit in my branch that I'm working on.
  • dirty means that I've got some local uncommitted changes in my repository at the time I made the build.

As you can see, putting this out into the field makes it plain to all that it is a development testing build and should not have moved further than the developer's local test environment. 

The string can take several bytes, so it is very important that you plan for that in any communication protocols you may employ.  It is very useful to be able to see your device's version from a mobile app if that is your main interface.  Or via console/serial/USB when you are running production and need to verify the firmware version at a test station.

Now, how does this tie into a firmware release flow?  It's really very straightforward.  You should already be using git tags to tag specific points in your repository during releases. If you use GitHub, the "releases" feature requires you to create a release "name", which is just a tag name for that specific release.  Git tags refer to specific commits.

Now, using git describe to define your version string, your firmware release is bound immutably to this specific release tag.  So, you need only create the tag, setting its name to the version string you desire, checkout that tag, and do your build.

For example, if you create a tag called "v1.3.0", running git describe from a clean checkout of this tag will simply be "v1.3.0", exactly what you want as your firmware production release version.  You can tag your local working copy using lightweight tags, then do a release build immediately after, e.g.:

  • git tag v1.0.0
  • git describe --tags --always --dirty
  • v1.0.0

Your tags created locally this way can be pushed to the server with: 

  • git push origin v1.0.0.

I highly recommend, however, that you use a Git hosting platform's service to create the tags like GitHub Releases, where you can later attach the built firmware binaries and other artifacts like release notes.

In summary, I've described a process that I guarantee will eliminate wasting time tracking down issues related to untracked changes during your product lifecycle, and will make your project managers and VPs extremely happy to boot!

About the Author

Evan works with clients around the world to build world-class IoT devices and embedded systems that work at scale. 

0 comments

  • There are no comments yet. Be the first one to post a comment on this article!

Leave a comment

Please note, comments must be approved before they are published