Ein Changelog automatisch erstellen

Wer Quellcode zur Verfügung stellt, sei es in Form einer App oder als Opensource-Tool, für den ist es hilfreich ein Changelog mitzuliefern. Mit Git und GruntJS lässt sich so ein Changelog schnell und automatisiert erstellen.

Wir wollen uns möglichst nicht selber um die Erstellung eines Changelogs kümmern, denn die Zeit können wir besser in die Entwicklung investieren. Deshalb bauen wir uns ein einfaches Bash-Script, welches uns die Erstellung des Changelogs abnimmt. Dank Git ist das Script schnell und mit wenigen Zeilen Code umgesetzt.

Die letzten Änderungen ermitteln

Um an die letzten Änderungen zu kommen, verwende ich:

git log

Wie man sieht, sorgt das für eine sehr umfangreiche Ausgabe, die wir so detailliert gar nicht brauchen. Darum schränken wir zunächst das Format ein wenig ein:

git log --format="  * %s"

Auf diese Weise sehen wir nur die Commit-Nachricht mit einem vorangestellten Sternchen:

* umstellung auf grunt

Die Ausgabe einschränken

In unserem Changelog sollen nur die wichtigsten Commit-Nachrichten auftauchen, da nicht jede Nachricht auch für das Changelog interessant ist. Zunächst entfernen wir mögliche Merge-Meldungen:

git log --format="  * %s" --no-merges

Anschließend wollen wir nur relevante Commits anzeigen. Um das zu erreichen, müssen die Commit-Nachrichten eine gewisse Syntax befolgen. Einige Systeme wie z.B. Stash oder GitHub bringen das schon mit sich. Mit Hashtags kann man dort bspw. auf bestimmte Issues oder Storys verweisen, z.B.:

ISSUE-123 #resolve nasty IE Bug
STORY-456 #close do those fancy css-transitions

Wie der Syntax letztendlich aufgebaut ist, liegt bei Ihnen, wichtig ist nur, dass sich jeder daran hält. Haben Sie sich auf einen solchen Syntax festgelegt, können wir nun unser Git log darauf beschränken:

git log --format="  * %s" --no-merges --grep=#resolve --grep=#close

Auf diese Weise werden also nur Commit-Nachrichten angezeigt, die einen der beiden Tags enthalten. Das lässt sich natürlich beliebig erweitern.

Den Zeitraum einschränken

Wir haben nun also unsere Ausgabe so weit, dass wir nur die wichtigsten Nachrichten angezeigt bekommen, allerdings bekommen wir immer noch viel zu viel angezeigt. Wir wollen also den Zeitraum, den wir abfragen, einschränken:

git log --since="2 Weeks" --format="  * %s" --no-merges --grep=#resolve --grep=#close

Damit bekommen wir nur die wichtigsten Commit-Nachrichten der letzten zwei Wochen angezeigt. Das ist schon mal gut, allerdings alles andere als optimal, denn wir wollen schließlich keinen festen Zeitraum angeben, sondern alle Änderungen seit des letzten Releases.

Dazu müssen wir wissen, wann dieser Release war. Wer mit Release-Tags arbeitet, kann diese als Indikator nehmen, ich zeige hier eine einfache Version mittels der Changelog-Datei. Mit jedem Release verpassen wir der Datei ein neues Datum. Zunächst legen wir aber erstmal eine leere Datei an:

touch changelog.txt

Nun können wir das Datum der Datei auslesen und in ein Format bringen, mit dem git log gut arbeiten kann:

ls -l changelog.txt | awk '{print $6,  $7}'
> 16 Sep

Die Ausgabe des Datum kann von System zu System leicht variieren, in den meisten Fällen stellt das aber kein Problem dar.

Autoren ermitteln

In ein Changelog gehören auch die Autoren der Änderungen. Leider habe ich hier noch keinen Weg gefunden, der mich zu hundert Prozent überzeugt. Folgende Lösung benutze ich derzeit:

git log --format='-- %aN' | sort -u 

Das liefert uns die Namen derjenigen, die Commits geliefert haben und entfernt Dopplungen.

Die Versionsnummer ermitteln

Jetzt wird es spaßig, denn wir wollen auch eine Versionsnummer angeben. Bei mir kommt diese aus der package.json, die ich für Grunt benutze:

{
  "name": "Mein Beispiel",
  "version": "1.2.3",
}

Irgendwie müssen wir diese Versionsnummer also auslesen. Dazu benutze ich eine Funktion, die ich hier gefunden habe. Ich habe sie noch ein wenig angepasst, da wir nur die Versionsnummer brauchen, nicht auch noch den Key des Eintrags.

Alles kombinieren

Wir haben nun also unsere Commits auf ein bestimmtes Subset eingeschränkt und können zudem über einer Steuerdatei ermitteln, wann das letzte Release war, so dass wir dieses Datum als Startdatum verwenden können.

Jetzt packen wir das alles zu einem Shellscript zusammen:

#!/bin/bash

function jsonval {
    temp=`echo $json | sed 's/\\\\\//\//g' | sed 's/[{}]//g' | awk -v k="text" '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}' | sed 's/\"\:\"/\|/g' | sed 's/[\,]/ /g' | sed 's/\"//g' | grep -w $prop | cut -d":" -f2| sed -e 's/^ *//g' -e 's/ *$//g'`
    echo ${temp##*|}
}

lastDate=`ls -l changelog.txt | awk '{print $6,  $7}'`
json=`cat package.json`
prop='version'
appversion=`jsonval`

echo "MyCoolTool ($appversion) UNRELEASED; urgency=low\n" > changelog.txt
git log --since="$lastDate" --format="  * %s" --no-merges --grep=#resolve --grep=#close >> changelog.txt
echo "\n" >> changelog.txt
git log --format='-- %aN' | sort -u  >> changelog.txt
exit

Wer will kann noch weitere Angaben ergänzen oder dynamisieren. Mit dem Script haben wir aber auf jeden Fall schon mal eine Changelog-Datei die eine gute Grundlage bietet. Das Script können Sie nun als Git-Hook oder im Build-Job laufen lassen und erzeugen damit automatisch eine neue Release-Info.

Das Shellscript finden Sie auch in meinem Repository auf GitHub.

0
0
blog comments powered by Disqus