UE4 + Git: Sematic Versioning Tool

Spoiler: This currently does not work for Blueprint-Only Projects with Unreal Engine 4. You could make it work by using a DataTable and generate a csv file instead of a Header file.

Also note this can probably be used for Unity or custom engines too. As long as you change the export to something suitable.

The Problem

  1. I wanted to have semantic versions inside my Unreal projects but not changing any files inside the repository and then committing. Because that would already’ve been another version of the project making it cumbersome to keep track when a version began.
  2. I wanted to use git tags as much as possible.
  3. Do it automatically as much as possible.
  4. Have full control over tagging.

History ( Jump ahead: New version )

I kinda started this as a helper for myself to “automatically” deduce a semantic versioning schema into my Unreal Engine 4 projects.

During the development of Paused I wanted to stamp releases on itch.io with a versioning schema so I can manage bug reports of the team and other people more easily. I already had the build being triggered by a Node.JS file so everyone in the team could configure it by utilizing a local-only json. So as I’ve decided to semver our builds now I’ve extended this small JavaScript file which triggered the UE4 Build Pipeline to do some “Git Tag”-magic before building.

Some lines to gather information from our version control Git prior to running the actual build process.

To gather only the latest tag, even if additional commits where made I used:

git describe --always --tags

which outputs something in the lines of {last-tag}[-{rev}-{hashcode}]. The funny thing is this returns the [-{rev}-{hashcode}] part, only if there was a commit after tagging.

So I counted the rev manually using git rev-list ${SEM_VER}..HEAD --count where ${SEM_VER} was the last tag found. This basically gives you an integer value to just attach on your Version number.

In this revision of the script we used it as a Patch-Number. Yes, each commit was a patch for us.

The result of this script was then thrown into a generated SEMVER.h file inside the Source folder (which was on the .gitignore file).

The good

  • Easy access through a Blueprint Function Library in Blueprint
  • Worked

The flaws

  • Every commit equals a patch
  • Many out of the team never saw a version in the editor, making it obsolete for reporting any bugs or quickly adjusting them
  • Requires to install node.js to make a Build
  • Requires build to get a SEMVER → requires Node to get a SEMVER
  • Cumbersome to update the version
  • A dummy SEMVER.h was created using the TargetRules
  • Unable to tag anything else
  • Does not work with Blueprint-Only projects

A new version

For my recent prototype I upgrade this to do this every time I compile the source code. Also new requirements were added:

  • I might have a version (Major.Minor.Patch) but might also want to have atleast a single meta-tag, for example rc1 or prototype1.
  • I don’t want every commit to be a patch - rather it was renamed to BUILDMETA and appended to the final version string
  • I want it to trigger during compilation

Also since Node.JS is not so common upon game development pipelines I’ve updated the script to Python3 for the script to build up the generated SEMVER.h file.

So the result is now a Python script you basically can throw in your Source folder and it generates a SEMVER.h file for you next to it when run. This basically allows you to put such a file nearly anywhere you like.

The resulting header file looks something like this:

1#define SEMVER_Major 0
2#define SEMVER_Minor 0
3#define SEMVER_Patch 0
4#define SEMVER_BUILDMETA 35
5#define SEMVER_METATAG "prototype1"
6#define SEMVER_BUILD_HASH "a95d0694646edd97f895d0dc"
7#define SEMVER_USE_TAG 1
8#define SEMVER_STR "0.0.0-prototype1+35" 

From the following git graph:

Git Graph (omitted messages and only master)

As you can see I tagged the very first commit with v0.0.0. This was only necessary to get BUILDMETA to work. Otherwise it would’ve been 0.

Prerequisites

  • Installed Python3
  • Installed Git
  • git & py commands in PATH ( available to: cmd (Windows Command-Line) )

TargetRules

To configure that it is run we use the UE built-in TargetRules and their property PreBuildSteps.

The code in my {Project}.Target.cs and {Project}Editor.Target.cs files now looks like this.

PreBuildSteps.Add("py --version");
PreBuildSteps.Add("py $(ProjectDir)/Source/GameModule/semver.py");

Basically I said before we do any compilation or building steps, we print the Python Version we use to the console - which makes it easier to find errors if someone accidentially installed Python2.

Then we run the script inside the module “GameModule” folder. Resulting in a SEMVER.h file next to it.

If this fails, the compilation won’t start, meaning no one can open the project if they cannot get past this. → No more need for a dummy file.

Tagging

The script has some requirements on how you tag.

Version Tags should start with a v. Followed by typical semantic versioning rules ( {Major}.{Minor}.{Patch} - where all three are numbers )

Meta Tags should start with a meta-. The meta- is removed later on and it’s sole purpose is to make it easier identifiable.

The good

  • Fully integrated in compilation
  • Works with Hot Reload
  • Has more information than the last one
  • Normal tagging still works
  • Still easily useable through a Blueprint Function Library inside UMG or Blueprints
  • Consider patches as patches not commit as patches
  • Easier maintainable code & structure

The flaws

  • Does not check for git repository yet.
  • Still does not work with Blueprint-Only projects.

The mixed

  • If you have no tags, you don’t get a warning. You just get 0.0.0+0.
  • If you have no v-tag but a meta-tag you atleast get 0.0.0-{meta}+0.
  • Still unclear if a version should ignore “lower” Meta-Tags 🤔

The Script

The python script is available on itch.io.

If you have any issues, consider posting them here: Gitlab Issue Tracker.