Introduction

Cogitri approached me with a problem, he is writing amazing projects such as gxi and tmplgen and he is a big fan of the AngularJS git commit guidelines.

So he adopted those guidelines for his projects and so far it has been working out fine for him, but he now wants to create release notes using those commits, but sadly all generators he found were heavyweight and feature-packed, he wanted something simple he could stick in his new release script and be done with it.

Requirements

He asked me for it to be:

  • Simple
  • Light
  • Apply to all commits between 2 tags or master and 1 tag
  • Output content similar to markdown
  • Easy to embed in his script
  • Deal with the 'fix' and 'feat' fields only

Writing it down

Seems easy enough, i decided to use bash and git to deal with it, knowing it would be run from his release scripts.

Dealing with arguments

I'm not going to assume a bad input will be sent, it is inside a script that will be called with known values.

#!/usr/bin/env bash
#
# Takes 2 tags and get all commits between them and turns
# it into a release notes based on AngularJS style
if [ -z "$1" ]; then
    echo "Need a starting tag" >&2
    exit 1
fi

stag="$1"

if [ "$2" ]; then
    ftag="$2"
else
    ftag=master
fi

Getting all the commits

Now we get all the commit between the 2 tags and make use of the fact that AngularJS commit guidelines dictate what is written at the start to get the correct commit categories.

commits="$(git log --pretty='%s' ${ftag}...${stag})"

feats=() # New Features
fixes=() # Fixes

while read -r c; do
    # Add all features to the array
    if grep -q '^feat(.*): ' <<< "$c"; then
        feats+=("${c#*feat}")
        continue
    fi

    if grep -q '^fix(.*): ' <<< "$c"; then
        fixes+=("${c#*fix}")
        continue
    fi
done <<< "$commits"

Notice the use of arrays and variable substitution, it is such a nice underused thing that saves a lot of writing and the fact bash can do a while-do-done loop without needing to create a subshell.

Writing it out

We now write out the results in the markdown-like format it was required of us.

if [ ${#feats[@]} -eq 0 ]; then
    echo " - No new features"
else
    for c in "${feats[@]}"; do
        printf "%s\\n" " - $c"
    done
fi

echo ""

printf "%s\\n\\n" "## Bugfixes"

if [ ${#fixes[@]} -eq 0 ]; then
    echo " - No bugfixes"
else
    for c in "${fixes[@]}"; do
        printf "%s\\n" " - $c"
    done
fi

Commits vs Results.

Here is a comparison of the commits to the result given to us.

git log --pretty='%s' master...v0.4.6

feat(edit_view): add newline to end of the file if it doesn't terminate with one
chore(deps): bump serde{,_derive} from 1.0.86 to 1.0.87
feat(edit_view): only draw trailing spaces
fix(pref_storage): don't implement Clone for Config<T>
fix(main): fix loading config
fix(main): don't load config twice
chore(deps): bump serde_json from 1.0.37 to 1.0.38
chore(deps): bump serde_derive from 1.0.85 to 1.0.86
chore(deps): bump proc-macro2 from 0.4.26 to 0.4.27
chore(deps): bump rand_jitter from 0.1.1 to 0.1.2
fix(linecache): fix linecount for wrapped lines
chore(deps): bump serde from 1.0.85 to 1.0.86
fix(ui): re-enable word wrapping, it's pretty complete now
fix(pref_storage): DO NOT clone Config to make sure it's consistent across different windows
fix(edit_view): send 'resize' to xi upon allocating a new size
fix(rpc): correctly handle measure_width
feat(shared_queue): also use for sending stuff to xi
feat(gettext): build against system gettext
chore(README): add travis build badge
fix(meson): only validate appstream if appstream-util is recent enough
chore(ci): use meson instead of cargo

mk-changelog v0.4.6

## Feature changes

 - (edit_view): add newline to end of the file if it doesn't terminate with one
 - (edit_view): only draw trailing spaces
 - (shared_queue): also use for sending stuff to xi
 - (gettext): build against system gettext

## Bugfixes

 - (pref_storage): don't implement Clone for Config<T>
 - (main): fix loading config
 - (main): don't load config twice
 - (linecache): fix linecount for wrapped lines
 - (ui): re-enable word wrapping, it's pretty complete now
 - (pref_storage): DO NOT clone Config to make sure it's consistent across different windows
 - (edit_view): send 'resize' to xi upon allocating a new size
 - (rpc): correctly handle measure_width
 - (meson): only validate appstream if appstream-util is recent enough

Working just fine.

The script

Here is the whole script if you want it, i consider it done, unless bug appears i won't touch it, it is licensed under the MIT license.

#!/usr/bin/env bash
#
# Takes 2 tags and get all commits between them and turns
# it into a release notes based on AngularJS style
if [ -z "$1" ]; then
    echo "Need a starting tag" >&2
    exit 1
fi

stag="$1"

if [ "$2" ]; then
    ftag="$2"
else
    ftag=master
fi

commits="$(git log --pretty='%s' ${ftag}...${stag})"

feats=() # New Features
fixes=() # Fixes

while read -r c; do
    # Add all features to the array
    if grep -q '^feat(.*): ' <<< "$c"; then
        feats+=("${c#*feat}")
        continue
    fi

    if grep -q '^fix(.*): ' <<< "$c"; then
        fixes+=("${c#*fix}")
        continue
    fi
done <<< "$commits"

printf "%s\\n\\n" "## Feature changes"

if [ ${#feats[@]} -eq 0 ]; then
    echo " - No new features"
else
    for c in "${feats[@]}"; do
        printf "%s\\n" " - $c"
    done
fi

echo ""

printf "%s\\n\\n" "## Bugfixes"

if [ ${#fixes[@]} -eq 0 ]; then
    echo " - No bugfixes"
else
    for c in "${fixes[@]}"; do
        printf "%s\\n" " - $c"
    done
fi