Git info on a Hugo static website

January 22, 2022    Article    666 words    4 mins read

Since Hugo is a static website builder (it only outputs HTML and XML from your Markdown files), if you want a way to display the latest git commits from a specific git repository on your website, you will be in a bit of a pickle. To achieve this level of Zen-ness we need to use the secret teachings of a man formally known as Bearly Grill: adapt, improvise and overcome.

Keep in mind that the bash part can probably be done by someone in a one-liner using just sed and channeling the power of your inner Stallman but this is something I quickly wrote so I can have some stats on the Civitas project page. Feel free to improve and email me the changes if you want. Or not.

So, basically we need two parts: one that will parse the output of the git log command into a JSON file (so that it can be loaded into the static Hugo website) and the second part that loads the JSON file and builds the DOM structure.

Git log processing

Remember to replace <PATH_TO_WEBSITE> with the correct path to the Hugo website content. If you change the /static/data/git-info.json path remember to change it in the JSON-loading part too. If you want more than 5 results, change the -n 5 parameter to git log.

git log -n 5 --date=format:%c --pretty=format:"%h|%an|%G?|%GP|%ad|%D|%s" \
	> "/tmp/gitlog.txt"
cat "/tmp/gitlog.txt" | jq --slurp --raw-input --raw-output \
	'split("\n") | map(split("|")) | map({
		"hash": .[0],
		"author": .[1],
		"gpg_status": .[2],
		"gpg_fp": .[3],
		"date": .[4],
		"branch": .[5],
		"message": .[6]
	})' > "<PATH_TO_WEBSITE>/static/data/git-info.json"

This bash block will export log data from the specified git repository into a temporary file (you can skip that part and pipe the data dirrectly into jq but you might want to keep the log exports or something), process the file using jq and output a JSON file with the specific data we want into the static/data/git-info.json file (name and path can be whatever you want). If you want to understand what "%h|%an|%G?|%GP|%ad|%D|%s" means, you should read the git log manual or look into the jq mapping part (spoiler, "hash|author name|gpg status|gpg fingerprint|date|branch|message").

You will need to call this (you should make it a bash function and call it inside a hugo deploy wrapper) before you do the Hugo website deploy. Proof of concept:

#!/usr/bin/env bash

website_dir="/home/richardstallman/website"
git_dir="/home/richardstallman/secret-git-repo"

function stats() {
	echo -e "-------- iLog --------"
	cd $git_dir && \
		git log -n 5 --date=format:%c --pretty=format:"%h|%an|%G?|%GP|%ad|%D|%s" \
		> "/tmp/gitlog.txt"
	cd $website_dir
	cat "/tmp/gitlog.txt" | jq --slurp --raw-input --raw-output \
		'split("\n") | map(split("|")) | map({
			"hash": .[0],
			"author": .[1],
			"gpg_status": .[2],
			"gpg_fp": .[3],
			"date": .[4],
			"branch": .[5],
			"message": .[6]
		})' > "$website_dir/static/data/git-info.json"
}

function build () {
	echo -e "-------- iBuilt --------"
	hugo --cleanDestinationDir --quiet -s $website_dir
}

function deploy () {
	echo -e "-------- iDeploy --------"
	rsync --stats -amh --delete "$website_dir/public/" user@hostname:~/public
}

echo -e "-------- iStart --------"
build
stats
deploy
echo -e "-------- We dun gut! --------"

Website JSON parsing

For the next step we need fetch that JSON file using vanilla JavaScript (or jQuery because it’s easier and something in your site already depends on jQuery fo’shure) and build the DOM structure. This is again a proof of concept, you might want to use proper divs and CSS stylesheets for making this the most informative list of git logs IN THE FUCKING UNIVERSE!! Remember to check for errors, kids.

<div class="git-container">Loading, please wait.</div>
<script>
document.addEventListener("DOMContentLoaded", function(event) {
	$.getJSON('/data/git-info.json', function(json_data) {
		let html = '';
		$.each(json_data, function(key, val) {
			html += '<div class="commit">' +
				'Hash: ' + val.hash + '<br />' +
				(val.branch !== "" ? 'Branch: ' + val.branch + '<br />' : '') +
				'Author name: ' + val.author + '<br />' +
				'Commit date: ' + val.date + '<br />' +
				'GPG signature status: ' + val.gpg_status + '<br />' +
				(val.gpg_fp !== "" ? 'Fingerprint of GPG key: ' + val.gpg_fp.slice(-8) + '<br />' : '') +
				'Message: ' + val.message + 
			'</div>';
		});
		$('.git-container').empty().html(html);
	});
});
</script>

Demo?

Yessir.