11#! /bin/bash
22# Script to generate release notes filtered by path
33# Usage: generate-release-notes.sh <current-tag> <path-filter>
4- # Example: generate-release-notes.sh js -sdk-v0.4.6 js /
4+ # Example: generate-release-notes.sh py -sdk-v0.7.0 py /
55
6- set -e
6+ set -euo pipefail
77
88if [ $# -lt 2 ]; then
99 echo " ERROR: Required arguments not provided"
@@ -18,37 +18,95 @@ PATH_FILTER=$2
1818SDK_PREFIX=$( echo " $CURRENT_TAG " | sed -E ' s/^([^-]+-[^-]+)-.*/\1/' )
1919
2020# Find the previous tag for this SDK
21- PREVIOUS_TAG=$( git tag --list " ${SDK_PREFIX} -v*" --sort=-v:refname | grep -v " ^${CURRENT_TAG} $" | head -1)
21+ PREVIOUS_TAG=$( git tag --list " ${SDK_PREFIX} -v*" --sort=-v:refname | grep -v " ^${CURRENT_TAG} $" | head -1 || true )
2222
2323if [ -z " $PREVIOUS_TAG " ]; then
2424 PREVIOUS_TAG=$( git rev-list --max-parents=0 HEAD)
2525fi
2626
27+ # Detect the GitHub repository for PR links
28+ REPO_URL=$( git remote get-url origin 2> /dev/null | sed -E ' s|git@github.com:|https://github.com/|; s|\.git$||' )
29+
2730# Generate the changelog
28- CHANGELOG=$( git log ${PREVIOUS_TAG} ..${CURRENT_TAG} --oneline --no-merges -- ${PATH_FILTER} )
31+ CHANGELOG=$( git log " ${PREVIOUS_TAG} ..${CURRENT_TAG} " --oneline --no-merges -- " ${PATH_FILTER} " )
2932
3033if [ -z " $CHANGELOG " ]; then
3134 echo " ## Changelog"
3235 echo " "
3336 echo " No changes found in ${PATH_FILTER} since ${PREVIOUS_TAG} "
3437else
35- echo " ## Changelog"
36- echo " "
38+ # Format a commit message as a markdown list item with PR link
39+ # Args: $1=type prefix, $2=commit message (without hash)
40+ format_line () {
41+ local type=" $1 "
42+ local msg=" $2 "
3743
38- # Format each commit as a markdown list item with PR link
39- while IFS= read -r line; do
40- # Extract commit hash and message
41- COMMIT_HASH=$( echo " $line " | awk ' {print $1}' )
42- COMMIT_MSG=$( echo " $line " | cut -d' ' -f2-)
43-
44- # Extract PR number if present (match the last occurrence)
45- if [[ $COMMIT_MSG =~ \( # ([0-9]+)\)[[:space:]]*$ ]]; then
46- PR_NUM= " ${BASH_REMATCH[1]} "
47- # Remove PR number from message (only the last occurrence)
48- CLEAN_MSG= $( echo " $COMMIT_MSG " | sed -E ' s/[[:space:]]*\(#[0-9]+\)[[:space:]]*$//' )
49- echo " * ${CLEAN_MSG} (#${PR_NUM} )"
44+ # Strip the conventional commit prefix (e.g. "feat: ", "fix(scope): ")
45+ local display
46+ display=$( echo " $msg " | sed -E ' s/^[a-zA-Z]+(\([^)]*\))?:[[:space:]]*//' )
47+
48+ # Capitalize the first letter
49+ display=" $( echo " ${display: 0: 1} " | tr ' [:lower:]' ' [:upper:]' ) ${display: 1} "
50+
51+ # Label perf commits explicitly
52+ if [ " $type " = " perf" ]; then
53+ display=" (perf) ${display} "
54+ fi
55+
56+ # Format PR link if present
57+ if [[ $display =~ \( # ([0-9]+)\)[[:space:]]*$ ]]; then
58+ local pr_num= " ${BASH_REMATCH[1]} "
59+ local clean
60+ clean= $( echo " $display " | sed -E ' s/[[:space:]]*\(#[0-9]+\)[[:space:]]*$//' )
61+ echo " * ${clean} ([#${pr_num} ](${REPO_URL} /pull/${pr_num} ))"
5062 else
51- echo " * ${COMMIT_MSG} "
63+ echo " * ${display} "
64+ fi
65+ }
66+
67+ # Print a changelog section if it has content
68+ print_section () {
69+ local title=" $1 "
70+ local content=" $2 "
71+ if [ -n " $content " ]; then
72+ echo " ### ${title} "
73+ echo " "
74+ printf " %s" " $content "
75+ echo " "
5276 fi
77+ }
78+
79+ # Bucket commits by conventional commit type
80+ FEATURES= " "
81+ FIXES= " "
82+ CHORES= " "
83+ OTHER= " "
84+
85+ while IFS= read -r line; do
86+ # Extract message (skip short hash) and type prefix
87+ msg=" ${line#* } "
88+ type=$( echo " $msg " | sed -E ' s/^([a-zA-Z]+)(\([^)]*\))?:.*/\1/' | tr ' [:upper:]' ' [:lower:]' )
89+
90+ FORMATTED=$( format_line " $type " " $msg " )
91+ case " $type " in
92+ feat|perf) FEATURES=" ${FEATURES}${FORMATTED} " $' \n ' ;;
93+ fix) FIXES=" ${FIXES}${FORMATTED} " $' \n ' ;;
94+ chore|ci|build|docs|style|refactor|test) CHORES=" ${CHORES}${FORMATTED} " $' \n ' ;;
95+ * ) OTHER=" ${OTHER}${FORMATTED} " $' \n ' ;;
96+ esac
5397 done <<< " $CHANGELOG "
98+
99+ echo " ## Changelog"
100+ echo " "
101+
102+ print_section " Features" " $FEATURES "
103+ print_section " Bug Fixes" " $FIXES "
104+ print_section " Maintenance" " $CHORES "
105+ print_section " Other Changes" " $OTHER "
106+
107+ # Extract version from tag (e.g. py-sdk-v0.7.0 -> 0.7.0)
108+ VERSION= $( echo " $CURRENT_TAG " | sed -E ' s/^[^-]+-[^-]+-v//' )
109+ echo " **Package**: https://pypi.org/project/braintrust/${VERSION} /"
110+ echo " "
111+ echo " **Full Changelog**: ${REPO_URL} /compare/${PREVIOUS_TAG} ...${CURRENT_TAG} "
54112fi
0 commit comments