ci(forgejo): group release notes by prefix and fork vs merges

Bucket commits into New/Fix/Improve/Other from conventional-style
subjects. When upstream main is available, split direct fork commits
from merge commits under separate headings.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
littlecodedragon 2026-05-09 22:17:56 +02:00
parent 7fab10c8ef
commit 9ce3f0ba29

View file

@ -112,18 +112,138 @@ jobs:
git fetch --no-tags --depth=1 "https://github.com/${upstream_repo}.git" "$upstream_base" || true
fi
use_fork_merge_split=0
log_range=""
if [ -n "$upstream_base" ] && git cat-file -e "${upstream_base}^{commit}" 2>/dev/null; then
commit_logs="$(git log --pretty=format:'- %s (@%an)' "${upstream_base}..HEAD")"
use_fork_merge_split=1
elif [ -n "$previous_tag" ]; then
commit_logs="$(git log --pretty=format:'- %s (@%an)' "${previous_tag}..HEAD")"
log_range="${previous_tag}..HEAD"
fi
commit_log_file="$(mktemp /tmp/komikku-commit-logs.XXXXXX)"
declare -a new_lines fix_lines improve_lines other_lines
bucket_for_subject() {
local s="$1"
local b=other
shopt -s nocasematch
local r_feat r_feature r_add r_fix r_perf r_refactor r_chore r_docs r_style r_test r_ci r_build r_improve
r_feat='^feat\([^)]*\):'
r_feature='^feature\([^)]*\):'
r_add='^add\([^)]*\):'
r_fix='^fix\([^)]*\):'
r_perf='^perf\([^)]*\):'
r_refactor='^refactor\([^)]*\):'
r_chore='^chore\([^)]*\):'
r_docs='^docs\([^)]*\):'
r_style='^style\([^)]*\):'
r_test='^test\([^)]*\):'
r_ci='^ci\([^)]*\):'
r_build='^build\([^)]*\):'
r_improve='^improve\([^)]*\):'
if [[ "$s" =~ ^feat: ]] || [[ "$s" =~ $r_feat ]]; then b=new
elif [[ "$s" =~ ^feature: ]] || [[ "$s" =~ $r_feature ]]; then b=new
elif [[ "$s" =~ ^add: ]] || [[ "$s" =~ $r_add ]]; then b=new
elif [[ "$s" =~ ^fix: ]] || [[ "$s" =~ $r_fix ]]; then b=fix
elif [[ "$s" =~ ^perf: ]] || [[ "$s" =~ $r_perf ]]; then b=improve
elif [[ "$s" =~ ^refactor: ]] || [[ "$s" =~ $r_refactor ]]; then b=improve
elif [[ "$s" =~ ^chore: ]] || [[ "$s" =~ $r_chore ]]; then b=improve
elif [[ "$s" =~ ^docs: ]] || [[ "$s" =~ $r_docs ]]; then b=improve
elif [[ "$s" =~ ^style: ]] || [[ "$s" =~ $r_style ]]; then b=improve
elif [[ "$s" =~ ^test: ]] || [[ "$s" =~ $r_test ]]; then b=improve
elif [[ "$s" =~ ^ci: ]] || [[ "$s" =~ $r_ci ]]; then b=improve
elif [[ "$s" =~ ^build: ]] || [[ "$s" =~ $r_build ]]; then b=improve
elif [[ "$s" =~ ^improve: ]] || [[ "$s" =~ $r_improve ]]; then b=improve
fi
shopt -u nocasematch
printf '%s' "$b"
}
clear_buckets() {
new_lines=() fix_lines=() improve_lines=() other_lines=()
}
fill_buckets_from_git() {
local rev_range="$1"
shift
while IFS= read -r line || [ -n "${line:-}" ]; do
[ -z "${line:-}" ] && continue
subject="${line%%$'\x1f'*}"
author="${line#*$'\x1f'}"
entry="- ${subject} (@${author})"
case "$(bucket_for_subject "$subject")" in
new) new_lines+=("$entry") ;;
fix) fix_lines+=("$entry") ;;
improve) improve_lines+=("$entry") ;;
*) other_lines+=("$entry") ;;
esac
done < <(git log "$@" --pretty=tformat:'%s%x1f%an' "$rev_range")
}
emit_buckets_from_arrays() {
local any=0
if [ ${#new_lines[@]} -gt 0 ]; then
printf '%s\n' "##### New" ""
printf '%s\n' "${new_lines[@]}"
printf '\n'
any=1
fi
if [ ${#fix_lines[@]} -gt 0 ]; then
printf '%s\n' "##### Fix" ""
printf '%s\n' "${fix_lines[@]}"
printf '\n'
any=1
fi
if [ ${#improve_lines[@]} -gt 0 ]; then
printf '%s\n' "##### Improve" ""
printf '%s\n' "${improve_lines[@]}"
printf '\n'
any=1
fi
if [ ${#other_lines[@]} -gt 0 ]; then
printf '%s\n' "##### Other" ""
printf '%s\n' "${other_lines[@]}"
printf '\n'
any=1
fi
[ "$any" -eq 1 ]
}
if [ "$use_fork_merge_split" -eq 1 ]; then
{
printf '%s\n' "#### Fork changes" ""
clear_buckets
fill_buckets_from_git "${upstream_base}..HEAD" --no-merges
if [ ${#new_lines[@]} -eq 0 ] && [ ${#fix_lines[@]} -eq 0 ] && [ ${#improve_lines[@]} -eq 0 ] && [ ${#other_lines[@]} -eq 0 ]; then
printf '%s\n' "_No direct commits in this range._" ""
else
commit_logs='- Initial release'
emit_buckets_from_arrays
fi
clear_buckets
fill_buckets_from_git "${upstream_base}..HEAD" --merges
if [ ${#new_lines[@]} -gt 0 ] || [ ${#fix_lines[@]} -gt 0 ] || [ ${#improve_lines[@]} -gt 0 ] || [ ${#other_lines[@]} -gt 0 ]; then
printf '%s\n' "#### Merge commits" ""
emit_buckets_from_arrays
printf '\n'
fi
} > "${commit_log_file}"
elif [ -n "$log_range" ]; then
clear_buckets
fill_buckets_from_git "$log_range"
{
if emit_buckets_from_arrays; then
:
else
printf '%s\n' "##### Other" "" "- Initial release" ""
fi
} > "${commit_log_file}"
else
printf '%s\n' "##### Other" "" "- Initial release" "" > "${commit_log_file}"
fi
# Do not pass multiline commit text via step outputs / env (breaks some Forgejo/act_runner
# runners before the shell starts — empty logs). Only pass a temp file path.
commit_log_file="$(mktemp /tmp/komikku-commit-logs.XXXXXX)"
printf '%s\n' "$commit_logs" > "${commit_log_file}"
echo "COMMIT_LOG_FILE=${commit_log_file}" >> "${GITHUB_ENV}"
- name: Set up Gradle
@ -395,7 +515,7 @@ jobs:
fi
commit_logs="$(printf '%s' "$commit_logs" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
if [ -z "$commit_logs" ]; then
commit_logs="- Initial release"
commit_logs=$'##### Other\n\n- Initial release\n'
fi
full_changelog=""
@ -412,12 +532,6 @@ jobs:
printf '%s\n' \
"#### What's Changed" \
"" \
"##### New" \
"" \
"##### Improve" \
"" \
"##### Fix" \
"" \
"${commit_logs}" \
"" \
"##### Based on" \