waitui/scripts/changelog.sh
2022-05-24 22:58:02 +02:00

312 lines
7.6 KiB
Bash
Executable File

#!/usr/bin/env sh
config_file=.version.json
changelog_file=CHANGELOG.md
changelog_header="# Changelog
All notable changes to this project will be documented in this file. See [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) for commit guidelines.
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
----
"
reverse() {
if which tac >/dev/null 2>&1; then
tac
else
tail -r
fi
}
generate_config() {
jq --null-input --arg version "0.0.0" --arg sha "" '{ "version": $version, "sha": $sha}' > $config_file
}
update_config() {
sha="$(git rev-parse --short HEAD)"
config=$(jq '.sha |= $sha | .version |= $version' --arg sha "$sha" --arg version "$1" $config_file)
echo "$config" > $config_file
}
calculate_version() {
version_number=$(echo "$1" | cut -d'-' -f1 | cut -d'+' -f1)
pre_release=$(echo "$1" | cut -d'-' -f2 | cut -d'+' -f1)
build=$(echo "$1" |cut -d'-' -f2 | cut -d'+' -f2)
if [ "$pre_release" = "$1" ]; then
pre_release=""
fi
if [ "$build" = "$1" ] || [ "$build" = "$pre_release" ]; then
build=""
fi
major=$(echo "$version_number" | cut -d'.' -f1)
minor=$(echo "$version_number" | cut -d'.' -f2)
patch=$(echo "$version_number" | cut -d'.' -f3)
pre_release_identifier=$(echo "$pre_release." | cut -d'.' -f2)
pre_release=""
build=""
case "$2" in
major)
if [ -n "$major" ]; then
major=$((major+1))
minor=0
patch=0
fi
;;
minor)
if [ -n "$minor" ]; then
minor=$((minor+1))
patch=0
fi
;;
patch)
if [ -n "$patch" ]; then
patch=$((patch+1))
fi
;;
rc)
if [ -z "$pre_release_identifier" ]; then
pre_release_identifier=1
else
pre_release_identifier=$((pre_release_identifier+1))
fi
pre_release="rc.$pre_release_identifier"
;;
esac
if [ "$3" = "build" ]; then
build="$(git rev-parse --short HEAD)"
fi
version_number="${major}.${minor}.${patch}"
if [ -n "$pre_release" ]; then
pre_release="-$pre_release"
fi
if [ -n "$build" ]; then
build="+$build"
fi
echo "${version_number}${pre_release}${build}"
}
get_git_commits() {
since_hash="$(jq '.sha' < $config_file | tr -d '"')"
if [ -n "$since_hash" ]; then
git rev-list "$since_hash..HEAD"
else
git rev-list HEAD
fi
}
get_git_commit_subject() {
git show -s --format=%s "$1"
}
get_git_commit_body() {
git show -s --format=%b "$1"
}
get_commit_type() {
echo "$1" | sed -r -n 's/([a-z]+)(\(?|:?).*/\1/p' | tr '[:upper:]' '[:lower:]'
}
get_commit_scope() {
echo "$1" | sed -r -n 's/[a-z]+\(([^)]+)\).*/\1/p'
}
get_commit_description() {
echo "$1" | sed -r -n 's/.*:[ ]+(.*)/\1/p'
}
get_commit_footer() {
with_colon=$(echo "$1" | sed -n '/^[A-Za-z-]\{1,\}: /,//{p;}')
with_hash=$(echo "$1" | sed -n '/^[A-Za-z-]\{1,\} #/,//{p;}')
fallback=$(echo "$1" | reverse | awk '/^$/{exit}1' | reverse)
if [ -z "$with_colon" ] && [ -z "$with_hash" ]; then
echo "$fallback"
elif [ ${#with_colon} -gt ${#with_hash} ]; then
echo "$with_colon"
else
echo "$with_hash"
fi
}
get_commit_body() {
pattern=$(get_commit_footer "$1" | head -n 1)
if [ -n "$pattern" ]; then
echo "$1" | sed -e "/$pattern/,\$d"
else
echo "$1"
fi
}
is_breaking_change() {
if echo "$1" | grep -q '!' || echo "$2" | grep -q 'BREAKING[ -]CHANGE: '; then
echo 1
else
echo 0
fi
}
get_breaking_change() {
with_colon=$(echo "$1" | sed -n '/^BREAKING[ -]CHANGE: /,/^[A-Za-z-]\{1,\}: /{ s/BREAKING[ -]CHANGE: \(.*\)/\1/; /^[A-Za-z-]\{1,\}: /!p;}')
with_hash=$(echo "$1" | sed -n '/^BREAKING[ -]CHANGE: /,/^[A-Za-z-]\{1,\} #/{ s/BREAKING[ -]CHANGE: \(.*\)/\1/; /^[A-Za-z-]\{1,\} #/!p;}')
if [ -n "$with_colon" ] && ! echo "$with_colon" | grep -q '[A-Za-z-]\{1,\} #'; then
echo "$with_colon"
else
echo "$with_hash"
fi
}
generate_changelog_entry() {
is_major=0
is_minor=0
is_patch=0
version="$(jq '.version' < $config_file | tr -d '"')"
breakingchanges="### BREAKING CHANGES\n"
features="### Features\n"
bugfixes="### Bug fixes\n"
builds="### Build System"
chores="### Chores\n"
test="### Chores\n"
for commit in $(get_git_commits); do
git_subject=$(get_git_commit_subject "$commit")
git_body=$(get_git_commit_body "$commit")
type=$(get_commit_type "$git_subject")
scope=$(get_commit_scope "$git_subject")
description=$(get_commit_description "$git_subject")
body=$(get_commit_body "$git_body")
footer=$(get_commit_footer "$git_body")
breakingchange=$(get_breaking_change "$footer")
is_breaking=$(is_breaking_change "$git_subject" "$footer")
if [ -n "$scope" ]; then
entry="**$scope:** $description"
else
entry="$description"
fi
entry=$(echo "$entry" | sed -e '2,$s/^[ ]*/ /')
if [ "$is_breaking" -eq 1 ] && [ -z "$breakingchange" ]; then
breakingchange="$body"
fi
if [ "$is_breaking" -eq 1 ]; then
is_major=1
breakingchange=$(echo "$breakingchange" | sed -e '2,$s/^[ ]*/ /')
breakingchanges="$breakingchanges\n* $breakingchange\n"
fi
case "$type" in
feat)
is_minor=1
features="$features\n* $entry\n"
;;
fix)
is_patch=1
bugfixes="$bugfixes\n* $entry\n"
;;
build)
builds="$builds\n* $entry\n"
;;
chore)
chores="$chores\n* $entry\n"
;;
ci)
;;
docs)
;;
style)
;;
refactor)
;;
perf)
;;
test)
;;
revert)
;;
esac
done
if [ $is_major -eq 1 ]; then
version=$(calculate_version "$version" "major")
elif [ $is_minor -eq 1 ]; then
version=$(calculate_version "$version" "minor")
elif [ $is_patch -eq 1 ]; then
version=$(calculate_version "$version" "patch")
fi
update_config "$version"
entry=""
if [ "$breakingchanges" != "### BREAKING CHANGES\n" ]; then
entry="$entry$breakingchanges\n\n"
fi
if [ "$bugfixes" != "### Bug fixes\n" ]; then
entry="$entry$bugfixes\n\n"
fi
if [ "$features" != "### Features\n" ]; then
entry="$entry$features\n\n"
fi
if [ -n "$entry" ]; then
entry="## [$version] ($(date '+%Y-%m-%d'))\n\n\n$entry"
fi
echo "$entry"
}
new_changelog() {
echo "$changelog_header" > $changelog_file
}
new_changelog_entry() {
entry=$(generate_changelog_entry)
if [ -n "$entry" ]; then
changelog=$(sed -n '/^----/,//{/^----/!p;}' < $changelog_file)
if [ -n "$changelog" ]; then
changelog="$changelog_header\n$entry\n$changelog"
else
changelog="$changelog_header\n$entry"
fi
echo "$changelog" > $changelog_file
fi
}
init() {
if [ ! -f "$config_file" ]; then
echo "Generating a config ... "
generate_config
new_changelog
else
echo "Using existing config"
fi
new_changelog_entry
}
init