|
| 1 | +#!/bin/sh |
| 2 | + |
| 3 | +# Source: https://github.com/smichard/conventional_changelog/blob/main/generate_changelog_local.sh |
| 4 | + |
| 5 | +# Exit on error |
| 6 | +set -e |
| 7 | + |
| 8 | +# Error handling |
| 9 | +trap 'echo "An error occurred at line $LINENO. Exiting."' ERR |
| 10 | + |
| 11 | +# Path to the Git repository (current directory) |
| 12 | +REPO_DIR="." |
| 13 | +CHANGELOG_FILE="$REPO_DIR/CHANGELOG.md" |
| 14 | +GITHUB_REPO_URL=$(git remote get-url origin 2>/dev/null | sed 's/^git@/https:\/\//;s/github\.com:/github.com\//;s/\.git$//') |
| 15 | +if [ -z "$GITHUB_REPO_URL" ]; then |
| 16 | + GITHUB_REPO_URL=0 |
| 17 | +fi |
| 18 | + |
| 19 | +echo "Starting changelog generation script..." |
| 20 | +echo "Repository:" |
| 21 | +echo $GITHUB_REPO_URL |
| 22 | +# Create or clear the changelog file |
| 23 | +> $CHANGELOG_FILE |
| 24 | + |
| 25 | +# Add the introductory text to the changelog |
| 26 | +echo "# Changelog" >> $CHANGELOG_FILE |
| 27 | +echo "" >> $CHANGELOG_FILE |
| 28 | +echo "All notable changes to this project will be documented in this file." >> $CHANGELOG_FILE |
| 29 | +echo "" >> $CHANGELOG_FILE |
| 30 | +echo "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and to [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)." >> $CHANGELOG_FILE |
| 31 | +echo "" >> $CHANGELOG_FILE |
| 32 | + |
| 33 | +# Go to the repository directory |
| 34 | +cd $REPO_DIR |
| 35 | + |
| 36 | +# Fetch the latest changes |
| 37 | +git fetch --tags |
| 38 | +echo "Fetched latest tags." |
| 39 | + |
| 40 | +# Get the latest tag |
| 41 | +LATEST_TAG=$(git describe --tags --abbrev=0) |
| 42 | + |
| 43 | +# Get tags in reverse order |
| 44 | +TAGS=$(git tag --sort=-v:refname) |
| 45 | + |
| 46 | +# Check if there are any tags |
| 47 | +if [ -z "$TAGS" ]; then |
| 48 | + echo "No tags found in the repository." |
| 49 | + exit 1 |
| 50 | +fi |
| 51 | + |
| 52 | +echo "Found tags: $TAGS" |
| 53 | + |
| 54 | +# Placeholder for the previous tag |
| 55 | +TAG_TO=HEAD |
| 56 | + |
| 57 | +# Define categories |
| 58 | +CATEGORIES="feat fix perf chore docs refactor test" |
| 59 | + |
| 60 | +# Regular expression for matching conventional commits |
| 61 | +CONVENTIONAL_COMMIT_REGEX="^.* (feat|fix|perf|chore|docs|refactor|test)(\(.*\))?: " |
| 62 | + |
| 63 | +print_tag() { |
| 64 | + echo "Processing tag: $2" |
| 65 | + TAG_DATE=$(git log -1 --format=%ai $2 | cut -d ' ' -f 1) |
| 66 | + if [ "$2" = "HEAD" ]; then |
| 67 | + if [ $(git rev-parse $1) != $(git rev-parse "HEAD") ]; then |
| 68 | + echo "## Unreleased changes" >> $CHANGELOG_FILE |
| 69 | + echo "" >> $CHANGELOG_FILE |
| 70 | + fi |
| 71 | + else |
| 72 | + echo "## $2 ($TAG_DATE)" >> $CHANGELOG_FILE |
| 73 | + echo "" >> $CHANGELOG_FILE |
| 74 | + fi |
| 75 | + |
| 76 | + |
| 77 | + # Collect all commits for this tag range |
| 78 | + if [ -z "$1" ]; then |
| 79 | + ALL_COMMITS=$(git log $2 --oneline --always) |
| 80 | + else |
| 81 | + ALL_COMMITS=$(git log $1..$2 --oneline --always) |
| 82 | + fi |
| 83 | + |
| 84 | + # Process each category |
| 85 | + for KEY in $CATEGORIES; do |
| 86 | + CATEGORY_COMMITS=$(echo "$ALL_COMMITS" | grep -E "^.* $KEY(\(.*\))?: " || true) |
| 87 | + if [ ! -z "$CATEGORY_COMMITS" ]; then |
| 88 | + case $KEY in |
| 89 | + "feat") CATEGORY_NAME="Feature" ;; |
| 90 | + "fix") CATEGORY_NAME="Bug Fixes" ;; |
| 91 | + "perf") CATEGORY_NAME="Performance Improvements" ;; |
| 92 | + "chore") CATEGORY_NAME="Chore" ;; |
| 93 | + "docs") CATEGORY_NAME="Documentation" ;; |
| 94 | + "refactor") CATEGORY_NAME="Refactor" ;; |
| 95 | + "test") CATEGORY_NAME="Test" ;; |
| 96 | + esac |
| 97 | + echo "### $CATEGORY_NAME" >> $CHANGELOG_FILE |
| 98 | + echo "Listing commits for category: $CATEGORY_NAME under tag $2" |
| 99 | + echo "$CATEGORY_COMMITS" | while read -r COMMIT; do |
| 100 | + HASH=$(echo $COMMIT | awk '{print $1}') |
| 101 | + MESSAGE=$(echo $COMMIT | sed -E "s/^$HASH $KEY(\(.*\))?: //") |
| 102 | + if [ "$GITHUB_REPO_URL" != "0" ]; then |
| 103 | + echo "- $MESSAGE [\`$HASH\`]($GITHUB_REPO_URL/commit/$HASH)" >> $CHANGELOG_FILE |
| 104 | + else |
| 105 | + echo "- $MESSAGE" >> $CHANGELOG_FILE |
| 106 | + fi |
| 107 | + done |
| 108 | + echo "" >> $CHANGELOG_FILE |
| 109 | + fi |
| 110 | + done |
| 111 | + |
| 112 | + # Process 'Other' category |
| 113 | + OTHER_COMMITS=$(echo "$ALL_COMMITS" | grep -v -E "$CONVENTIONAL_COMMIT_REGEX" || true) |
| 114 | + if [ ! -z "$OTHER_COMMITS" ]; then |
| 115 | + echo "### Other" >> $CHANGELOG_FILE |
| 116 | + echo "Listing commits for category: Other under tag $2" |
| 117 | + echo "$OTHER_COMMITS" | while read -r COMMIT; do |
| 118 | + HASH=$(echo $COMMIT | awk '{print $1}') |
| 119 | + MESSAGE=$(echo $COMMIT | sed -E 's/^[^ ]* //') |
| 120 | + if [ "$GITHUB_REPO_URL" != "0" ]; then |
| 121 | + echo "- $MESSAGE [\`$HASH\`]($GITHUB_REPO_URL/commit/$HASH)" >> $CHANGELOG_FILE |
| 122 | + else |
| 123 | + echo "- $MESSAGE" >> $CHANGELOG_FILE |
| 124 | + fi |
| 125 | + done |
| 126 | + echo "" >> $CHANGELOG_FILE |
| 127 | + fi |
| 128 | + |
| 129 | + echo "Completed processing tag: $2" |
| 130 | + # Update the previous tag |
| 131 | +} |
| 132 | + |
| 133 | +# Iterate over tags |
| 134 | +for TAG_FROM in $TAGS; do |
| 135 | + print_tag $TAG_FROM $TAG_TO |
| 136 | + TAG_TO=$TAG_FROM |
| 137 | +done |
| 138 | +print_tag "" $TAG_FROM |
| 139 | + |
| 140 | +echo "Changelog generation complete." |
0 commit comments