1+ #! /usr/bin/env bash
2+
3+ # -----------------------------------------------------------------------------
4+ # changelog.sh
5+ #
6+ # Purpose:
7+ # Generates a categorized changelog in markdown format from git commit messages
8+ # between two tags.
9+ #
10+ # Expected commit message format:
11+ # Uses Conventional Commits prefixes for categorization:
12+ # feat: New features
13+ # fix: Bug fixes
14+ # docs: Documentation changes
15+ # refactor: Code refactoring
16+ # test: Test-related changes
17+ # chore: Maintenance tasks
18+ # Any commit not matching these prefixes is categorized as "Other Changes".
19+ #
20+ # Usage:
21+ # ./changelog.sh <previous_tag> <current_tag>
22+ #
23+ # Output:
24+ # Writes a markdown changelog to 'changelog.md' in the current directory.
25+ # -----------------------------------------------------------------------------
26+ set -euo pipefail
27+
28+ # === Configuration ===
29+ PREVIOUS_TAG=" ${1:- } "
30+ CURRENT_TAG=" ${2:- } "
31+
32+ if [[ -z " $PREVIOUS_TAG " || -z " $CURRENT_TAG " ]]; then
33+ echo " Usage: $0 <previous_tag> <current_tag>"
34+ exit 1
35+ fi
36+
37+ echo " 🔍 Generating changelog from $PREVIOUS_TAG → $CURRENT_TAG "
38+
39+ # Validate tags exist
40+ if ! git rev-parse --verify " $PREVIOUS_TAG " > /dev/null 2>&1 ; then
41+ echo " ❌ Previous tag '$PREVIOUS_TAG ' does not exist."
42+ exit 1
43+ fi
44+ if ! git rev-parse --verify " $CURRENT_TAG " > /dev/null 2>&1 ; then
45+ echo " ❌ Current tag '$CURRENT_TAG ' does not exist."
46+ exit 1
47+ fi
48+
49+ # Collect commits
50+ COMMITS=$( git log " $PREVIOUS_TAG " .." $CURRENT_TAG " --pretty=format:" %s (@%an)" )
51+
52+ # Categorize exclusively
53+ FEATURES=" "
54+ FIXES=" "
55+ DOCS=" "
56+ REFACTOR=" "
57+ TESTS=" "
58+ CHORES=" "
59+ OTHERS=" "
60+ while IFS= read -r commit; do
61+ if [[ " $commit " =~ ^feat ]]; then
62+ FEATURES+=" $commit " $' \n '
63+ elif [[ " $commit " =~ ^fix ]]; then
64+ FIXES+=" $commit " $' \n '
65+ elif [[ " $commit " =~ ^docs ]]; then
66+ DOCS+=" $commit " $' \n '
67+ elif [[ " $commit " =~ ^refactor ]]; then
68+ REFACTOR+=" $commit " $' \n '
69+ elif [[ " $commit " =~ ^test ]]; then
70+ TESTS+=" $commit " $' \n '
71+ elif [[ " $commit " =~ ^chore ]]; then
72+ CHORES+=" $commit " $' \n '
73+ else
74+ OTHERS+=" $commit " $' \n '
75+ fi
76+ done <<< " $COMMITS"
77+
78+ # Build markdown file
79+ OUTFILE=" changelog.md"
80+
81+ {
82+ echo " ## 📝 Changelog for $CURRENT_TAG "
83+ echo " "
84+
85+ if [ -n " $FEATURES " ]; then
86+ echo " ### ✨ Features"
87+ echo " $FEATURES " | sed ' /^$/d; s/^/- /'
88+ echo " "
89+ fi
90+ if [ -n " $FIXES " ]; then
91+ echo " ### 🐛 Bug Fixes"
92+ echo " $FIXES " | sed ' /^$/d; s/^/- /'
93+ echo " "
94+ fi
95+ if [ -n " $DOCS " ]; then
96+ echo " ### 🧾 Documentation"
97+ echo " $DOCS " | sed ' /^$/d; s/^/- /'
98+ echo " "
99+ fi
100+ if [ -n " $REFACTOR " ]; then
101+ echo " ### 🔧 Refactoring"
102+ echo " $REFACTOR " | sed ' /^$/d; s/^/- /'
103+ echo " "
104+ fi
105+ if [ -n " $TESTS " ]; then
106+ echo " ### 🧪 Tests"
107+ echo " $TESTS " | sed ' /^$/d; s/^/- /'
108+ echo " "
109+ fi
110+ if [ -n " $CHORES " ]; then
111+ echo " ### 🧰 Chores"
112+ echo " $CHORES " | sed ' /^$/d; s/^/- /'
113+ echo " "
114+ fi
115+ if [ -n " $OTHERS " ]; then
116+ echo " ### 🔹 Other Changes"
117+ echo " $OTHERS " | sed ' /^$/d; s/^/- /'
118+ echo " "
119+ fi
120+ } > " $OUTFILE "
121+
122+ echo " ✅ Changelog written to $OUTFILE "
0 commit comments