name: Release notes from commits on: release: types: [published] permissions: contents: write jobs: generate-notes: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 # fetch all tags/commits for range and logs - name: Compute previous tag id: prev env: CURRENT_TAG: ${{ github.event.release.tag_name }} shell: bash run: | set -e git fetch --tags --force if [ -z "$CURRENT_TAG" ]; then echo "No current tag found from release payload" 1>&2 exit 1 fi # Sort by version, pick the latest tag that is not the current tag PREV_TAG=$(git tag --sort=-v:refname | grep -v "^${CURRENT_TAG}$" | head -n 1 || true) echo "Previous tag: $PREV_TAG" echo "prev_tag=$PREV_TAG" >> "$GITHUB_OUTPUT" - name: Generate release notes from commits id: gen env: CURRENT_TAG: ${{ github.event.release.tag_name }} PREV_TAG: ${{ steps.prev.outputs.prev_tag }} REPO: ${{ github.repository }} shell: bash run: | set -e if [ -n "$PREV_TAG" ]; then RANGE="$PREV_TAG..$CURRENT_TAG" echo "# Release $CURRENT_TAG" > RELEASE_NOTES.md echo >> RELEASE_NOTES.md echo "[Full Changelog](https://github.com/$REPO/compare/$PREV_TAG...$CURRENT_TAG)" >> RELEASE_NOTES.md echo >> RELEASE_NOTES.md else RANGE="$CURRENT_TAG" echo "# Release $CURRENT_TAG" > RELEASE_NOTES.md echo >> RELEASE_NOTES.md fi echo "## Changes" >> RELEASE_NOTES.md echo >> RELEASE_NOTES.md # Use commit subjects; skip merge commits for clarity git log --no-merges --pretty=format:'- %s (%h) by %an' $RANGE >> RELEASE_NOTES.md || true echo "Generated notes:" && echo "-----" && cat RELEASE_NOTES.md && echo "-----" - name: Update GitHub Release body uses: actions/github-script@v7 with: script: | const fs = require('fs'); const notes = fs.readFileSync('RELEASE_NOTES.md', 'utf8'); const releaseId = context.payload.release.id; await github.rest.repos.updateRelease({ owner: context.repo.owner, repo: context.repo.repo, release_id: releaseId, body: notes, });