131 lines
3.5 KiB
Shell
Executable file
131 lines
3.5 KiB
Shell
Executable file
#!/bin/bash
|
|
# Generate changelog from git history for CI releases
|
|
# Groups commits by conventional commit types (feat, fix, chore, refactor)
|
|
#
|
|
# Usage: ./generate-changelog.sh <tag> [output-file]
|
|
# tag: Git tag to generate changelog for (e.g., v1.0.0)
|
|
# output-file: Optional output file path (default: CHANGELOG.md)
|
|
|
|
set -e
|
|
|
|
# Check if tag argument is provided
|
|
if [ -z "$1" ]; then
|
|
echo "Error: Tag argument required"
|
|
echo "Usage: $0 <tag> [output-file]"
|
|
exit 1
|
|
fi
|
|
|
|
TAG="$1"
|
|
OUTPUT_FILE="${2:-CHANGELOG.md}"
|
|
|
|
echo "Generating changelog for ${TAG}..."
|
|
|
|
# Get previous tag (if exists)
|
|
PREV_TAG=$(git describe --tags --abbrev=0 "${TAG}^" 2>/dev/null || echo "")
|
|
|
|
if [ -z "$PREV_TAG" ]; then
|
|
echo "No previous tag found. This is the first release."
|
|
# Get the first commit in the repository
|
|
FIRST_COMMIT=$(git rev-list --max-parents=0 HEAD)
|
|
COMMIT_RANGE="${FIRST_COMMIT}..${TAG}"
|
|
echo "Generating changelog from first commit to ${TAG}"
|
|
else
|
|
COMMIT_RANGE="${PREV_TAG}..${TAG}"
|
|
echo "Generating changelog from ${PREV_TAG} to ${TAG}"
|
|
fi
|
|
|
|
# Initialize changelog file
|
|
echo "# Release ${TAG}" > "$OUTPUT_FILE"
|
|
echo "" >> "$OUTPUT_FILE"
|
|
|
|
if [ -z "$PREV_TAG" ]; then
|
|
echo "First release of Worldhopper! 🎉" >> "$OUTPUT_FILE"
|
|
echo "" >> "$OUTPUT_FILE"
|
|
fi
|
|
|
|
# Get all commits in range
|
|
COMMITS=$(git log "${COMMIT_RANGE}" --pretty=format:"%H|%s" 2>/dev/null || echo "")
|
|
|
|
if [ -z "$COMMITS" ]; then
|
|
echo "No commits found in range" >> "$OUTPUT_FILE"
|
|
else
|
|
# Arrays to hold categorized commits
|
|
FEATURES=""
|
|
FIXES=""
|
|
CHORES=""
|
|
REFACTORS=""
|
|
OTHERS=""
|
|
|
|
# Parse commits and categorize
|
|
while IFS='|' read -r hash message; do
|
|
# Skip empty lines
|
|
[ -z "$hash" ] && continue
|
|
|
|
case "$message" in
|
|
feat:*|feat\(*)
|
|
FEATURES="${FEATURES}- ${message#feat:} (${hash:0:7})\n"
|
|
;;
|
|
fix:*|fix\(*)
|
|
FIXES="${FIXES}- ${message#fix:} (${hash:0:7})\n"
|
|
;;
|
|
chore:*|chore\(*)
|
|
CHORES="${CHORES}- ${message#chore:} (${hash:0:7})\n"
|
|
;;
|
|
refactor:*|refactor\(*)
|
|
REFACTORS="${REFACTORS}- ${message#refactor:} (${hash:0:7})\n"
|
|
;;
|
|
*)
|
|
OTHERS="${OTHERS}- ${message} (${hash:0:7})\n"
|
|
;;
|
|
esac
|
|
done <<< "$COMMITS"
|
|
|
|
# Write categorized sections
|
|
if [ -n "$FEATURES" ]; then
|
|
echo "## ✨ Features" >> "$OUTPUT_FILE"
|
|
echo "" >> "$OUTPUT_FILE"
|
|
echo -e "$FEATURES" >> "$OUTPUT_FILE"
|
|
fi
|
|
|
|
if [ -n "$FIXES" ]; then
|
|
echo "## 🐛 Bug Fixes" >> "$OUTPUT_FILE"
|
|
echo "" >> "$OUTPUT_FILE"
|
|
echo -e "$FIXES" >> "$OUTPUT_FILE"
|
|
fi
|
|
|
|
if [ -n "$REFACTORS" ]; then
|
|
echo "## ♻️ Refactoring" >> "$OUTPUT_FILE"
|
|
echo "" >> "$OUTPUT_FILE"
|
|
echo -e "$REFACTORS" >> "$OUTPUT_FILE"
|
|
fi
|
|
|
|
if [ -n "$CHORES" ]; then
|
|
echo "## 🔧 Chores" >> "$OUTPUT_FILE"
|
|
echo "" >> "$OUTPUT_FILE"
|
|
echo -e "$CHORES" >> "$OUTPUT_FILE"
|
|
fi
|
|
|
|
if [ -n "$OTHERS" ]; then
|
|
echo "## 📝 Other Changes" >> "$OUTPUT_FILE"
|
|
echo "" >> "$OUTPUT_FILE"
|
|
echo -e "$OTHERS" >> "$OUTPUT_FILE"
|
|
fi
|
|
fi
|
|
|
|
echo "" >> "$OUTPUT_FILE"
|
|
echo "---" >> "$OUTPUT_FILE"
|
|
|
|
# Add full changelog link if we have git forge info
|
|
if [ -n "$CI_FORGE_URL" ] && [ -n "$CI_REPO" ]; then
|
|
COMPARE_BASE="${PREV_TAG:-$(git rev-list --max-parents=0 HEAD | head -1)}"
|
|
echo "**Full Changelog**: ${CI_FORGE_URL}/${CI_REPO}/compare/${COMPARE_BASE}...${TAG}" >> "$OUTPUT_FILE"
|
|
elif [ -n "$PREV_TAG" ]; then
|
|
echo "**Commits**: \`git log ${PREV_TAG}..${TAG}\`" >> "$OUTPUT_FILE"
|
|
else
|
|
echo "**First Release**" >> "$OUTPUT_FILE"
|
|
fi
|
|
|
|
echo ""
|
|
echo "Changelog generated successfully: ${OUTPUT_FILE}"
|
|
echo ""
|
|
cat "$OUTPUT_FILE"
|