From c339597f8a0b0ffc9d02354b63ba816bf3afcd86 Mon Sep 17 00:00:00 2001 From: Arkadiusz Fal Date: Fri, 13 Feb 2026 19:57:13 +0100 Subject: [PATCH] Add git-cliff based changelog generator Bash wrapper around git-cliff with cliff.toml config for regex-based commit parsers handling skip patterns and category matching. --- bin/generate-changelog | 66 ++++++++++++++++++++++++++++++++++++++++++ cliff.toml | 46 +++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100755 bin/generate-changelog create mode 100644 cliff.toml diff --git a/bin/generate-changelog b/bin/generate-changelog new file mode 100755 index 00000000..824d1940 --- /dev/null +++ b/bin/generate-changelog @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +# +# Generate Changelog +# +# Generates CHANGELOG.md from git commits since the last tagged release +# using git-cliff for deterministic, regex-based commit categorization. +# +# Usage: +# ./bin/generate-changelog # Write CHANGELOG.md and open in $EDITOR +# ./bin/generate-changelog --dry-run # Print to stdout only +# ./bin/generate-changelog --from TAG # Use a specific base tag +# + +set -euo pipefail +cd "$(dirname "$0")/.." + +dry_run=false +from_tag="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --dry-run) + dry_run=true + shift + ;; + --from) + from_tag="$2" + shift 2 + ;; + -h|--help) + echo "Usage: $0 [--dry-run] [--from TAG]" + echo "" + echo " --dry-run Print to stdout instead of writing file" + echo " --from TAG Base tag (default: latest tag)" + exit 0 + ;; + *) + echo "Unknown option: $1" >&2 + exit 1 + ;; + esac +done + +# Determine base tag +if [[ -z "$from_tag" ]]; then + from_tag=$(git tag --sort=-creatordate | head -1) + if [[ -z "$from_tag" ]]; then + echo "Error: no git tags found" >&2 + exit 1 + fi +fi + +# Build git-cliff arguments +cliff_args=(--config cliff.toml --unreleased "${from_tag}..HEAD") + +if "$dry_run"; then + git-cliff "${cliff_args[@]}" +else + git-cliff "${cliff_args[@]}" --output CHANGELOG.md + count=$(git log "${from_tag}..HEAD" --oneline | wc -l | tr -d ' ') + echo "Wrote CHANGELOG.md (${count} commits from ${from_tag}..HEAD)" + + if [[ -n "${EDITOR:-}" ]]; then + exec "$EDITOR" CHANGELOG.md + fi +fi diff --git a/cliff.toml b/cliff.toml new file mode 100644 index 00000000..a51ea652 --- /dev/null +++ b/cliff.toml @@ -0,0 +1,46 @@ +# git-cliff configuration for Yattee changelog generation +# https://git-cliff.org/docs/configuration + +[changelog] +header = "" +body = """ +{%- if version %} +## What's Changed +{%- else %} +## What's Changed +{%- endif %} +{% for group, commits in commits | group_by(attribute="group") %} +### {{ group | split(pat=" ") | slice(start=1) | join(sep=" ") }} +{% for commit in commits %} +* {{ commit.message | split(pat="\n") | first }} +{%- endfor %} +{% endfor %} +""" +trim = true + +[git] +conventional_commits = false +filter_unconventional = false +split_commits = false +sort_commits = "newest" + +commit_parsers = [ + # Skip noise commits + { message = "^Bump build number", skip = true }, + { message = "^Fix build number", skip = true }, + { message = "^Update localizable", skip = true }, + { message = "^Fix locales", skip = true }, + { message = "^Merge pull request", skip = true }, + { message = "^Update changelog", skip = true }, + { message = "^Remove excessive logging", skip = true }, + { message = "^Auto-increment build number", skip = true }, + + # Categorize by commit message prefix + { message = "^(?i:Add|Show|Persist|Implement)\\b", group = "0 New Features" }, + { message = "^(?i:Update|Change|Move|Improve)\\b", group = "1 Improvements" }, + { message = "^(?i:Fix)\\b", group = "2 Bug Fixes" }, + { message = "^(?i:Refactor)\\b", group = "3 Development" }, + + # Catch-all + { message = ".*", group = "4 Other" }, +]