From ed387f72b8e55dfd749b5c0ae0b10ad7e010dbe6 Mon Sep 17 00:00:00 2001 From: felixzsh Date: Thu, 20 Aug 2026 10:44:22 -0500 Subject: [PATCH] ci: auto generate changelog --- .github/workflows/release.yml | 104 ++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..9c540e0 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,104 @@ +name: release + +on: + push: + tags: + - "v*" + +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + fetch-tags: true + + - name: Generate changelog + id: changelog + run: | + set -euo pipefail + TAG="${GITHUB_REF_NAME}" + REPO="${GITHUB_REPOSITORY}" + + # previous tag (parent of this tag) + PREV_TAG="$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")" + + if [ -z "$PREV_TAG" ]; then + RANGE="HEAD" + echo "No previous tag, using all history" + else + RANGE="${PREV_TAG}..HEAD" + echo "Range: $RANGE" + fi + + FEATS="" + FIXES="" + + # %H = hash, %s = subject (first line) + while IFS= read -r line; do + [ -z "$line" ] && continue + sha="${line%% *}" + subject="${line#* }" + # match conventional commits: feat, fix (with optional scope) + if echo "$subject" | grep -qE '^(feat|fix)(\(.+\))?: '; then + type="$(echo "$subject" | sed -E 's/^([a-z]+)(\(.+\))?:.*/\1/')" + # strip type prefix for display, keep rest as title + title="$(echo "$subject" | sed -E 's/^[a-z]+(\(.+\))?: //')" + short="$(echo "$sha" | cut -c1-7)" + url="https://github.com/${REPO}/commit/${sha}" + entry="- ${title} ([\`${short}\`](${url}))" + if [ "$type" = "feat" ]; then + FEATS="${FEATS}${entry}"$'\n' + elif [ "$type" = "fix" ]; then + FIXES="${FIXES}${entry}"$'\n' + fi + fi + done < <(git log "$RANGE" --pretty=format:"%H %s" --no-merges) + + { + echo "# ${TAG}" + echo "" + if [ -n "$FEATS" ]; then + echo "## Features" + echo "" + printf "%s" "$FEATS" + echo "" + fi + if [ -n "$FIXES" ]; then + echo "## Fixes" + echo "" + printf "%s" "$FIXES" + echo "" + fi + if [ -z "$FEATS" ] && [ -z "$FIXES" ]; then + echo "_No relevant conventional commits (feat/fix) in this release._" + echo "" + fi + echo "---" + echo "" + if [ -n "$PREV_TAG" ]; then + echo "**Full diff:** [\`${PREV_TAG}...${TAG}\`](https://github.com/${REPO}/compare/${PREV_TAG}...${TAG})" + else + echo "**Full history up to ${TAG}**" + fi + } > /tmp/notes.md + + cat /tmp/notes.md + # multiline output for next step + { + echo "notes<> "$GITHUB_OUTPUT" + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ github.ref_name }} + name: ${{ github.ref_name }} + body: ${{ steps.changelog.outputs.notes }} + generate_release_notes: false