name: Generate Release Notes on: workflow_call: inputs: version: description: 'Version number (e.g., 4.25.3)' required: true type: string target_commitish: description: 'Commit SHA or branch (leave empty for current HEAD)' required: false type: string release_notes: description: 'Custom release notes (leave empty to auto-generate)' required: false type: string outputs: release_notes: description: 'Generated or provided release notes' value: ${{ jobs.generate.outputs.release_notes }} secrets: UNRAID_BOT_GITHUB_ADMIN_TOKEN: required: true jobs: generate: name: Generate Release Notes runs-on: ubuntu-latest outputs: release_notes: ${{ steps.generate_notes.outputs.release_notes }} steps: - name: Checkout repo uses: actions/checkout@v6 with: ref: ${{ inputs.target_commitish || github.ref }} fetch-depth: 0 token: ${{ secrets.UNRAID_BOT_GITHUB_ADMIN_TOKEN }} - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: '20' - name: Generate Release Notes id: generate_notes env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | TAG_NAME="v${{ inputs.version }}" VERSION="${{ inputs.version }}" if [ -n "${{ inputs.release_notes }}" ]; then NOTES="${{ inputs.release_notes }}" else CHANGELOG_PATH="api/CHANGELOG.md" if [ -f "$CHANGELOG_PATH" ]; then echo "Extracting release notes from CHANGELOG.md for version ${VERSION}" NOTES=$(awk -v ver="$VERSION" ' BEGIN { found=0; capture=0; output=""; gsub(/\./, "\\.", ver); } /^## \[/ { if (capture) exit; if ($0 ~ "\\[" ver "\\]") { found=1; capture=1; } } capture { if (output != "") output = output "\n"; output = output $0; } END { if (found) print output; else exit 1; } ' "$CHANGELOG_PATH") || EXTRACTION_STATUS=$? if [ ${EXTRACTION_STATUS:-0} -eq 0 ] && [ -n "$NOTES" ]; then echo "✓ Found release notes in CHANGELOG.md" else echo "⚠ Version ${VERSION} not found in CHANGELOG.md, generating with conventional-changelog" PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "") CHANGELOG_GENERATED=false if [ -n "$PREV_TAG" ]; then echo "Generating changelog from ${PREV_TAG}..HEAD using conventional-changelog" npm install -g conventional-changelog-cli TEMP_NOTES=$(mktemp) conventional-changelog -p conventionalcommits \ --release-count 1 \ --output-unreleased \ > "$TEMP_NOTES" 2>/dev/null || true if [ -s "$TEMP_NOTES" ]; then NOTES=$(cat "$TEMP_NOTES") if [ -n "$NOTES" ]; then echo "✓ Generated changelog with conventional-changelog" CHANGELOG_GENERATED=true TEMP_CHANGELOG=$(mktemp) { if [ -f "$CHANGELOG_PATH" ]; then head -n 1 "$CHANGELOG_PATH" echo "" echo "$NOTES" echo "" tail -n +2 "$CHANGELOG_PATH" else echo "# Changelog" echo "" echo "$NOTES" fi } > "$TEMP_CHANGELOG" mv "$TEMP_CHANGELOG" "$CHANGELOG_PATH" echo "✓ Updated CHANGELOG.md with generated notes" else echo "⚠ conventional-changelog produced empty output, using GitHub auto-generation" NOTES=$(gh api repos/${{ github.repository }}/releases/generate-notes \ -f tag_name="${TAG_NAME}" \ -f target_commitish="${{ inputs.target_commitish || github.sha }}" \ -f previous_tag_name="${PREV_TAG}" \ --jq '.body') fi else echo "⚠ conventional-changelog failed, using GitHub auto-generation" NOTES=$(gh api repos/${{ github.repository }}/releases/generate-notes \ -f tag_name="${TAG_NAME}" \ -f target_commitish="${{ inputs.target_commitish || github.sha }}" \ -f previous_tag_name="${PREV_TAG}" \ --jq '.body') fi rm -f "$TEMP_NOTES" else echo "⚠ No previous tag found, using GitHub auto-generation" NOTES=$(gh api repos/${{ github.repository }}/releases/generate-notes \ -f tag_name="${TAG_NAME}" \ -f target_commitish="${{ inputs.target_commitish || github.sha }}" \ --jq '.body' || echo "Release ${VERSION}") fi if [ "$CHANGELOG_GENERATED" = true ]; then BRANCH_OR_SHA="${{ inputs.target_commitish || github.ref }}" if git show-ref --verify --quiet "refs/heads/${BRANCH_OR_SHA}"; then echo "" echo "==========================================" echo "CHANGELOG GENERATED AND COMMITTED" echo "==========================================" echo "" git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" BEFORE_SHA=$(git rev-parse HEAD) git add "$CHANGELOG_PATH" git commit -m "chore: add changelog for version ${VERSION}" git push origin "HEAD:${BRANCH_OR_SHA}" AFTER_SHA=$(git rev-parse HEAD) echo "✓ Changelog committed and pushed successfully" echo "" echo "Previous SHA: ${BEFORE_SHA}" echo "New SHA: ${AFTER_SHA}" echo "" echo "⚠️ CRITICAL: A new commit was created, but github.sha is immutable." echo "⚠️ github.sha = ${BEFORE_SHA} (original workflow trigger)" echo "⚠️ The release tag must point to ${AFTER_SHA} (with changelog)" echo "" echo "Re-run this workflow to create the release with the correct commit." echo "" exit 1 else echo "⚠ Target is a commit SHA, not a branch. Cannot push changelog updates." echo "Changelog was generated but not committed." fi fi fi else echo "⚠ CHANGELOG.md not found, using GitHub auto-generation" PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "") if [ -n "$PREV_TAG" ]; then NOTES=$(gh api repos/${{ github.repository }}/releases/generate-notes \ -f tag_name="${TAG_NAME}" \ -f target_commitish="${{ inputs.target_commitish || github.sha }}" \ -f previous_tag_name="${PREV_TAG}" \ --jq '.body') else NOTES="Release ${VERSION}" fi fi fi echo "release_notes<> $GITHUB_OUTPUT echo "$NOTES" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT