| #!/usr/bin/env bash
|
|
|
|
|
| if [ -z "$BASH_VERSION" ]; then
|
| echo "This script requires bash. Please run with: bash $0" >&2
|
| exit 1
|
| fi
|
|
|
|
|
| if ((BASH_VERSINFO[0] < 4)); then
|
| echo "This script requires bash version 4 or later" >&2
|
| echo "Current bash version: $BASH_VERSION" >&2
|
| exit 1
|
| fi
|
|
|
|
|
| if [ -z "$GITHUB_ACTIONS" ]; then
|
| : "${GITHUB_SERVER_URL:=https://github.com}"
|
| : "${GITHUB_REPOSITORY:=stackblitz-labs/bolt.diy}"
|
| : "${GITHUB_OUTPUT:=/tmp/github_output}"
|
| touch "$GITHUB_OUTPUT"
|
|
|
|
|
| echo "Running locally - checking for upstream remote..."
|
| MAIN_REMOTE="origin"
|
| if git remote -v | grep -q "upstream"; then
|
| MAIN_REMOTE="upstream"
|
| fi
|
| MAIN_BRANCH="main"
|
|
|
|
|
| git fetch ${MAIN_REMOTE} --tags
|
|
|
|
|
| GITLOG_REF="${MAIN_REMOTE}/${MAIN_BRANCH}"
|
| else
|
|
|
| GITLOG_REF="HEAD"
|
| fi
|
|
|
|
|
| LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
|
|
|
|
|
| echo "# π Release v${NEW_VERSION}" > changelog.md
|
| echo "" >> changelog.md
|
| echo "## What's Changed π" >> changelog.md
|
| echo "" >> changelog.md
|
|
|
| if [ -z "$LATEST_TAG" ]; then
|
| echo "### π First Release" >> changelog.md
|
| echo "" >> changelog.md
|
| echo "Exciting times! This marks our first release. Thanks to everyone who contributed! π" >> changelog.md
|
| echo "" >> changelog.md
|
| COMPARE_BASE="$(git rev-list --max-parents=0 HEAD)"
|
| else
|
| echo "### π Changes since $LATEST_TAG" >> changelog.md
|
| echo "" >> changelog.md
|
| COMPARE_BASE="$LATEST_TAG"
|
| fi
|
|
|
|
|
| get_commit_type() {
|
| local msg="$1"
|
| if [[ $msg =~ ^feat(\(.+\))?:|^feature(\(.+\))?: ]]; then echo "β¨ Features"
|
| elif [[ $msg =~ ^fix(\(.+\))?: ]]; then echo "π Bug Fixes"
|
| elif [[ $msg =~ ^docs(\(.+\))?: ]]; then echo "π Documentation"
|
| elif [[ $msg =~ ^style(\(.+\))?: ]]; then echo "π Styles"
|
| elif [[ $msg =~ ^refactor(\(.+\))?: ]]; then echo "β»οΈ Code Refactoring"
|
| elif [[ $msg =~ ^perf(\(.+\))?: ]]; then echo "β‘ Performance Improvements"
|
| elif [[ $msg =~ ^test(\(.+\))?: ]]; then echo "π§ͺ Tests"
|
| elif [[ $msg =~ ^build(\(.+\))?: ]]; then echo "π οΈ Build System"
|
| elif [[ $msg =~ ^ci(\(.+\))?: ]]; then echo "βοΈ CI"
|
| elif [[ $msg =~ ^chore(\(.+\))?: ]]; then echo ""
|
| else echo "π Other Changes"
|
| fi
|
| }
|
|
|
|
|
| declare -A CATEGORIES
|
| declare -A COMMITS_BY_CATEGORY
|
| declare -A ALL_AUTHORS
|
| declare -A NEW_CONTRIBUTORS
|
|
|
|
|
| while IFS= read -r author; do
|
| ALL_AUTHORS["$author"]=1
|
| done < <(git log "${COMPARE_BASE}" --pretty=format:"%ae" | sort -u)
|
|
|
|
|
| while IFS= read -r commit_line; do
|
| if [[ ! $commit_line =~ ^[a-f0-9]+\| ]]; then
|
| echo "WARNING: Skipping invalid commit line format: $commit_line" >&2
|
| continue
|
| fi
|
|
|
| HASH=$(echo "$commit_line" | cut -d'|' -f1)
|
| COMMIT_MSG=$(echo "$commit_line" | cut -d'|' -f2)
|
| BODY=$(echo "$commit_line" | cut -d'|' -f3)
|
|
|
| if [[ ! $HASH =~ ^[a-f0-9]{40}$ ]]; then
|
| continue
|
| fi
|
|
|
| HASH=$(echo "$commit_line" | cut -d'|' -f1)
|
| COMMIT_MSG=$(echo "$commit_line" | cut -d'|' -f2)
|
| BODY=$(echo "$commit_line" | cut -d'|' -f3)
|
|
|
|
|
|
|
| if [[ ! $HASH =~ ^[a-f0-9]{40}$ ]]; then
|
| echo "WARNING: Invalid commit hash format: $HASH" >&2
|
| continue
|
| fi
|
|
|
|
|
| if [[ $COMMIT_MSG =~ Merge\ pull\ request\
|
|
|
| PR_NUM="${BASH_REMATCH[1]}"
|
|
|
|
|
| PR_TITLE=$(echo "$BODY" | grep -v "^Merge pull request" | head -n 1)
|
|
|
|
|
| CATEGORY=$(get_commit_type "$PR_TITLE")
|
|
|
| if [ -n "$CATEGORY" ]; then
|
|
|
| GITHUB_USERNAME=$(gh pr view "$PR_NUM" --json author --jq '.author.login')
|
|
|
| if [ -n "$GITHUB_USERNAME" ]; then
|
|
|
| AUTHOR_EMAIL=$(git show -s --format='%ae' "$HASH")
|
| if [ -z "${ALL_AUTHORS[$AUTHOR_EMAIL]}" ]; then
|
| NEW_CONTRIBUTORS["$GITHUB_USERNAME"]=1
|
| ALL_AUTHORS["$AUTHOR_EMAIL"]=1
|
| fi
|
|
|
| CATEGORIES["$CATEGORY"]=1
|
| COMMITS_BY_CATEGORY["$CATEGORY"]+="* ${PR_TITLE#*: } ([#$PR_NUM](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/pull/$PR_NUM)) by @$GITHUB_USERNAME"$'\n'
|
| else
|
| COMMITS_BY_CATEGORY["$CATEGORY"]+="* ${PR_TITLE#*: } ([#$PR_NUM](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/pull/$PR_NUM))"$'\n'
|
| fi
|
| fi
|
|
|
| elif [[ $COMMIT_MSG =~ \(
|
|
|
| PR_NUM="${BASH_REMATCH[1]}"
|
|
|
|
|
| CATEGORY=$(get_commit_type "$COMMIT_MSG")
|
|
|
| if [ -n "$CATEGORY" ]; then
|
|
|
| GITHUB_USERNAME=$(gh pr view "$PR_NUM" --json author --jq '.author.login')
|
|
|
| if [ -n "$GITHUB_USERNAME" ]; then
|
|
|
| AUTHOR_EMAIL=$(git show -s --format='%ae' "$HASH")
|
| if [ -z "${ALL_AUTHORS[$AUTHOR_EMAIL]}" ]; then
|
| NEW_CONTRIBUTORS["$GITHUB_USERNAME"]=1
|
| ALL_AUTHORS["$AUTHOR_EMAIL"]=1
|
| fi
|
|
|
| CATEGORIES["$CATEGORY"]=1
|
| COMMIT_TITLE=${COMMIT_MSG%% (#*}
|
| COMMIT_TITLE=${COMMIT_TITLE#*: }
|
| COMMITS_BY_CATEGORY["$CATEGORY"]+="* $COMMIT_TITLE ([#$PR_NUM](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/pull/$PR_NUM)) by @$GITHUB_USERNAME"$'\n'
|
| else
|
| COMMIT_TITLE=${COMMIT_MSG%% (#*}
|
| COMMIT_TITLE=${COMMIT_TITLE#*: }
|
| COMMITS_BY_CATEGORY["$CATEGORY"]+="* $COMMIT_TITLE ([#$PR_NUM](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/pull/$PR_NUM))"$'\n'
|
| fi
|
| fi
|
|
|
| else
|
|
|
|
|
| CATEGORY=$(get_commit_type "$COMMIT_MSG")
|
|
|
| if [ -n "$CATEGORY" ]; then
|
|
|
| AUTHOR_EMAIL=$(git show -s --format='%ae' "$HASH")
|
|
|
|
|
| if [ -n "$GITHUB_ACTIONS" ] || command -v gh >/dev/null 2>&1; then
|
| GITHUB_USERNAME=$(gh api "/repos/${GITHUB_REPOSITORY}/commits/${HASH}" --jq '.author.login' 2>/dev/null)
|
| fi
|
|
|
| if [ -n "$GITHUB_USERNAME" ]; then
|
|
|
| if [ -z "${ALL_AUTHORS[$AUTHOR_EMAIL]}" ]; then
|
| NEW_CONTRIBUTORS["$GITHUB_USERNAME"]=1
|
| ALL_AUTHORS["$AUTHOR_EMAIL"]=1
|
| fi
|
|
|
| CATEGORIES["$CATEGORY"]=1
|
| COMMIT_TITLE=${COMMIT_MSG#*: }
|
| COMMITS_BY_CATEGORY["$CATEGORY"]+="* $COMMIT_TITLE (${HASH:0:7}) by @$GITHUB_USERNAME"$'\n'
|
| else
|
|
|
| AUTHOR_NAME=$(git show -s --format='%an' "$HASH")
|
|
|
| if [ -z "${ALL_AUTHORS[$AUTHOR_EMAIL]}" ]; then
|
| NEW_CONTRIBUTORS["$AUTHOR_NAME"]=1
|
| ALL_AUTHORS["$AUTHOR_EMAIL"]=1
|
| fi
|
|
|
| CATEGORIES["$CATEGORY"]=1
|
| COMMIT_TITLE=${COMMIT_MSG#*: }
|
| COMMITS_BY_CATEGORY["$CATEGORY"]+="* $COMMIT_TITLE (${HASH:0:7}) by $AUTHOR_NAME"$'\n'
|
| fi
|
| fi
|
| fi
|
|
|
| done < <(git log "${COMPARE_BASE}..${GITLOG_REF}" --pretty=format:"%H|%s|%b" --reverse --first-parent)
|
|
|
|
|
| for category in "β¨ Features" "π Bug Fixes" "π Documentation" "π Styles" "β»οΈ Code Refactoring" "β‘ Performance Improvements" "π§ͺ Tests" "π οΈ Build System" "βοΈ CI" "π Other Changes"; do
|
| if [ -n "${COMMITS_BY_CATEGORY[$category]}" ]; then
|
| echo "### $category" >> changelog.md
|
| echo "" >> changelog.md
|
| echo "${COMMITS_BY_CATEGORY[$category]}" >> changelog.md
|
| echo "" >> changelog.md
|
| fi
|
| done
|
|
|
|
|
| if [ ${#NEW_CONTRIBUTORS[@]} -gt 0 ]; then
|
| echo "## β¨ First-time Contributors" >> changelog.md
|
| echo "" >> changelog.md
|
| echo "A huge thank you to our amazing new contributors! Your first contribution marks the start of an exciting journey! π" >> changelog.md
|
| echo "" >> changelog.md
|
|
|
| readarray -t sorted_contributors < <(printf '%s\n' "${!NEW_CONTRIBUTORS[@]}" | sort)
|
| for github_username in "${sorted_contributors[@]}"; do
|
| echo "* π [@$github_username](https://github.com/$github_username)" >> changelog.md
|
| done
|
| echo "" >> changelog.md
|
| fi
|
|
|
|
|
| if [ -n "$LATEST_TAG" ]; then
|
| echo "## π Stats" >> changelog.md
|
| echo "" >> changelog.md
|
| echo "**Full Changelog**: [\`$LATEST_TAG..v${NEW_VERSION}\`](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/compare/$LATEST_TAG...v${NEW_VERSION})" >> changelog.md
|
| fi
|
|
|
|
|
| CHANGELOG_CONTENT=$(cat changelog.md)
|
| {
|
| echo "content<<EOF"
|
| echo "$CHANGELOG_CONTENT"
|
| echo "EOF"
|
| } >> "$GITHUB_OUTPUT"
|
|
|
|
|
| echo "Generated changelog:"
|
| echo "==================="
|
| cat changelog.md
|
| echo "===================" |