Update your web app version dynamically with git-describe

Git tags help you to mark a point in your git history, we use this to mark our code/software version.

A little more about tags

In git we have two "kinds" of tags .

Annotated tag — Is store with some information

git tag -a v1.0 -m "First version"

Lightweight — Is an simple commit checksum without any extra information

git tag v1.0

This is important because `git description`treats this “kind”of tags different.

Git-describe

git describe is a git command that display the last reachable tag , this can be useful if you want to know the exact version of your code.

In your CI for instance, if fail you want to know what was the exact version of your code that failed.

Let’s do to understand

Let's build a very simple calculator.

❯ yarn init . && yarn add assert -D && git init .

Our first version will sum two numbers

// calc.test.js
const assert = require('assert');
const calc = require('./calc');
assert.equal(calc.sum(1,1), 2);
// calc.js
module.exports = {
sum: (n1, n2) => n1 + n2
}

Our first commit will be like this

❯ git add . && git commit -am "Sum feature"

If we execute `git describe` the result will be..

❯ git describe 
fatal: No names found, cannot describe anything.

The reason is because there are nothing to describe, in this moment we don't have any tag just commits.

The documentation said that.

Describe a commit using the most recent tag reachable from it

So let's add an tag for our version

❯ git tag v1.0.0

As mention before, there are two kinds of tags, annotated and unannotated. The command above just create an unannotated tag.

There reason this is important is because git describe, by default, only shows annotated tags

❯ git describe 
fatal: No annotated tags can describe ‘544a96f6fb66dcb858df2ec0172def47430297dc’.
However, there were unannotated tags: try — tags.
By default (without — all or — tags) git describe only shows annotated tags.

We can see our most recent tag using ` — tags` flag

❯ git describe — tags 
v1.0.0

But this not helps us if our CI fail and we need to check the exact version that failed. Let’s try instead use unannotated tags, use more descriptive (annotated) tags.

Adding subtract feature

// calc.test.js
assert.equal(calc.sub(10,5), 5);
// calc.js
sub: (n1, n2) => n1 - n2

And publish our app with version 1.1.0

❯ git add . && git commit -am "Add sub feature"
❯ git tag -a v1.1.0 -m "Subtract feature"

Now we have two tags `v1.0.0` with sum feature and `v1.1.0` with subtract feature.

❯ git tag 
v1.0.0
v1.1.0

As we annotated our last tag, the `git describe` will show the most recent annotated tag.

❯ git describe 
v1.1.0
The command finds the most recent tag that is reachable from a commit. If the tag points to the commit, then only the tag is shown.

Until now we have a publish 1.1.0 version of our app with sum and subtract feature.

But, what's happen if we keep changing our code and don't put any tag?

Adding multiply feature

// calc.test.js
assert.equal(calc.mult(5,5), 25);
// calc.js
mult: (n1, n2) => n1 * n2

This time we will not publish a new tag(version), we simple commit the code

git add . && git commit -am "Add multiply feature"

The trick here is, we are no longer on version `1.1.0` , we are ahead of this version, if the test fails all information that we have is the git hash. Is when describe feature comes to handy.

❯ git describe
v1.1.0–1-g831f2d6

Let's split this ..

  • v1.1.0 — Is the last tag
  • 1 — Is how many commits we area ahead
  • g — Stands for “git” and is used to allow describing the version of a software depending on the SCM the software is managed with.
  • 831f2d6 — 7-char abbreviation for the tip commit of parent

Now describe can be useful, in our CI we know the exactly version and commit that fail.

We can keep adding features..

// calc.test.js
assert.equal(calc.div(10,2), 5);
// calc.js
div: (n1, n2) => n1 / n2

The git describe will be..

❯ git add . && git commit -am "Add divider feature"
❯ git describe
v1.1.0-2-gdc8528b

2 because we are 2 commits ahead of the last tag, and dc8528b as 7-char abbreviation for the tip commit.

As you can see, is more descriptive way to display the exactly version of your code.

Use git-describe to manager our app version

git describe helps you to identify the exact version of your code, and we can use this to automatic display the app version.

Let’s do to understand

Let's build a web app that use our calc.js module. To do this we will use webpack in order generate our bundle and HTML.

❯ yarn add webpack html-webpack-plugin -D

And configure

// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
entry: './app.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'app.js',
},
plugins: [new HtmlWebpackPlugin({
title: 'Calculator',
template: 'index.ejs',
})
]
};
-----
// app.js
const calc = require('./calc');

const n1El = document.getElementById('n1');
const n2El = document.getElementById('n2');
const resultEl = document.getElementById('result');

const execOperation = (operation) => {
const n1 = parseInt(n1El.value);
const n2 = parseInt(n2El.value);
resultEl.textContent = operation(n1, n2);
}
const applyOperation = (elementId, operation) => {
document
.getElementById(elementId)
.addEventListener("click", function() {
execOperation(operation)
});
}
applyOperation("sumBtn", calc.sum);
applyOperation("subBtn", calc.sub);
applyOperation("multBtn", calc.mult);
applyOperation("divBtn", calc.div);
-------
// index.ejs
<!DOCTYPE html>
<html>
<head>
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<label for="n1">N1</label><input type="number" id="n1" />
<label for="n2">N2</label><input type="number" id="n2" />
<button id="sumBtn">+</button>
<button id="subBtn">-</button>
<button id="multBtn">*</button>
<button id="divBtn">/</button>
= <span id="result"></span>
<hr>
<footer>
<span id="version"></span>
</footer>
</body>
</html>

Run webpack will generate in dist folder our index.html with script tag point to app.js .

And our `index.html` on browser will be like this

The question here is how to add git describe version in our html on tag #version.

To archive this we need to use the git-revision-webpack-plugin.

yarn add git-revision-webpack-plugin -D

This is a very simple and useful plugin that use git describe to get our code version and allow us to use it our webpack configuration.

We can use this in conjunction with DefinePlugin to "export" our version to HTML.

Let's do it

First change the 'webpack.config.js' as follow

// Add ..
const webpack = require('webpack');
const GitRevisionPlugin = require('git-revision-webpack-plugin');
const gitRevisionPlugin = new GitRevisionPlugin();
...
// And in plugins
plugins: [
...
new webpack.DefinePlugin({
'VERSION': JSON.stringify(gitRevisionPlugin.version()),
'COMMITHASH': JSON.stringify(gitRevisionPlugin.commithash()),
'BRANCH': JSON.stringify(gitRevisionPlugin.branch()),
})
]

Now in our app.js we have access to these variables, we can simple change the version.

const versionEl = document.getElementById('version');

versionEl.textContent = `VERSION: "${VERSION}" COMMITHASH: "${COMMITHASH}" BRANCH: "${BRANCH}"`;
Calculator with version from git

And after publish a new version.

❯ git add . && git commit -am "Add web interface"
❯ git tag -a v1.2.0 -m "Add web interface to access the calculator features"

Our describe will display the current version.

❯ git describe 
v1.2.0

And our interface will display the same.

Conclusion

As demonstrated git-describe can be use to identify our code in CI or display version in our app.

Check my github repository with the full code.

Like this? Please click ❤️ bellow so other people can find it.

Reference

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.