| #!/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 "===================" |