worldhopper/scripts/generate-changelog.sh
Felipe M. b366652ddf
feat: add Woodpecker CI for automated releases
- Add .woodpecker.yml configuration for tag-triggered builds
- Create scripts/generate-changelog.sh for grouped changelog generation
- Create scripts/create-release.sh for Forgejo release publishing
- Add comprehensive scripts/README.md with usage examples

Pipeline triggers on version tags (v*.*.*) and performs:
1. Build release APK with Flutter
2. Generate grouped changelog from conventional commits
3. Create Forgejo release with APK attachment and changelog

Scripts are standalone and can be run locally for testing.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-28 22:31:26 +01:00

131 lines
3.5 KiB
Shell
Executable file

#!/bin/bash
# Generate changelog from git history for Woodpecker 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"