Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 91 additions & 14 deletions .github/workflows/changelog-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,25 @@ on:
pull_request:
types:
- closed
workflow_dispatch:
inputs:
pr_number:
description: "PR number to add to the changelog (must be a merged PR)"
required: true
type: string

permissions:
contents: write
pull-requests: write
actions: read

env:
CHANGELOG_BASE_BRANCH: master
Comment thread
yCodeTech marked this conversation as resolved.

jobs:
update_changelog:
name: Update Changelog
if: github.event.pull_request.merged == true
if: github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest

steps:
Expand All @@ -25,16 +34,56 @@ jobs:
.github/scripts/update-changelog.mjs
sparse-checkout-cone-mode: false

- name: Resolve PR data
id: resolve-pr
uses: actions/github-script@v7
with:
script: |
let pr;

if (context.eventName === 'workflow_dispatch') {
const rawPrNumber = (context.payload.inputs?.pr_number ?? '').trim();
if (!/^\d+$/.test(rawPrNumber) || Number(rawPrNumber) <= 0) {
core.setFailed(`Invalid workflow input "pr_number": "${rawPrNumber}". Please provide a positive integer PR number.`);
return;
}

const prNumber = Number(rawPrNumber);
console.log(`🔍 Fetching PR #${prNumber} from API...`);

const { data } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: prNumber,
});

if (!data.merged) {
core.setFailed(`PR #${prNumber} has not been merged. Only merged PRs can be added to the changelog.`);
return;
}

pr = data;
console.log(`✅ Resolved PR #${prNumber}: ${pr.title}`);
} else {
pr = context.payload.pull_request;
}

const prJson = JSON.stringify(pr);
core.setOutput('pr-json-base64', Buffer.from(prJson, 'utf8').toString('base64'));

- name: Check if PR should be excluded
id: check-exclusions
uses: actions/github-script@v7
with:
script: |
const { default: checkExclusions } = await import('${{ github.workspace }}/.github/scripts/check-changelog-exclusions.mjs');
const pr = JSON.parse(Buffer.from(process.env.RESOLVED_PR_BASE64, 'base64').toString('utf8'));
return await checkExclusions({
pr: context.payload.pull_request,
pr,
core
});
env:
RESOLVED_PR_BASE64: "${{ steps.resolve-pr.outputs.pr-json-base64 }}"

- name: Changelog update skipped
if: steps.check-exclusions.outputs.should-skip == 'true'
Expand All @@ -56,7 +105,7 @@ jobs:
if: steps.check-exclusions.outputs.should-skip == 'false'
id: check-existing-changelog-pr
run: |
BASE_BRANCH="${{ github.event.pull_request.base.ref }}"
BASE_BRANCH="${{ env.CHANGELOG_BASE_BRANCH }}"

# Find existing changelog PR
CHANGELOG_PR=$(gh pr list --base "$BASE_BRANCH" --state open --json number,title,headRefName,body --jq '.[] | select(.title | test("^docs: Update changelog"; "i"))')
Expand Down Expand Up @@ -99,12 +148,12 @@ jobs:

if [ "${{ steps.check-existing-changelog-pr.outputs.changelog-pr-exists }}" = "true" ]; then
echo "🔄 Checking out existing branch: $BRANCH_NAME"
git fetch origin $BRANCH_NAME
git checkout $BRANCH_NAME
git pull origin $BRANCH_NAME
git fetch origin "$BRANCH_NAME"
git checkout "$BRANCH_NAME"
git reset --hard "origin/$BRANCH_NAME"
else
echo "✨ Creating new branch: $BRANCH_NAME"
git checkout -b $BRANCH_NAME
git checkout -b "$BRANCH_NAME"
fi

- name: Update Changelog
Expand All @@ -114,10 +163,13 @@ jobs:
with:
script: |
const { default: updateChangelog } = await import('/tmp/update-changelog.mjs');
const pr = JSON.parse(Buffer.from(process.env.RESOLVED_PR_BASE64, 'base64').toString('utf8'));
return await updateChangelog({
pr: context.payload.pull_request,
pr,
core
});
env:
RESOLVED_PR_BASE64: "${{ steps.resolve-pr.outputs.pr-json-base64 }}"

- name: Prettify Changelog
id: prettify-changelog
Expand All @@ -133,23 +185,48 @@ jobs:
git diff CHANGELOG.md

- name: Commit and push changes
id: commit-push
if: steps.check-exclusions.outputs.should-skip == 'false' && steps.update-changelog.outputs.changelog-updated == 'true'
run: |
set -euo pipefail
BRANCH_NAME="${{ steps.check-existing-changelog-pr.outputs.changelog-pr-branch-name }}"
echo "pushed=false" >> "$GITHUB_OUTPUT"

git add CHANGELOG.md

# Avoid failing when there's nothing new to commit
if git diff --cached --quiet; then
echo "No changelog changes to commit."
exit 0
fi

git commit -m "docs: update changelog for PR #${{ steps.update-changelog.outputs.pr-number }}"

if [ "${{ steps.check-existing-changelog-pr.outputs.changelog-pr-exists }}" = "true" ]; then
git push origin $BRANCH_NAME
else
git push -u origin $BRANCH_NAME
# Push and recover from race by rebasing once and retrying
if ! git push -u origin "$BRANCH_NAME"; then
echo "Push rejected, retrying after rebase..."
if git ls-remote --exit-code --heads origin "$BRANCH_NAME" >/dev/null 2>&1; then
git fetch origin "$BRANCH_NAME"
if ! git rebase "origin/$BRANCH_NAME"; then
echo "Rebase failed while retrying push to $BRANCH_NAME." >&2
git rebase --abort || true
exit 1
fi
else
echo "Remote branch $BRANCH_NAME not found during retry; retrying push without rebase."
fi
if ! git push -u origin "$BRANCH_NAME"; then
echo "Failed to push changelog changes after retry." >&2
exit 1
fi
fi

echo "pushed=true" >> "$GITHUB_OUTPUT"

- name: Create or update changelog PR
if: steps.check-exclusions.outputs.should-skip == 'false' && steps.update-changelog.outputs.changelog-updated == 'true'
if: steps.check-exclusions.outputs.should-skip == 'false' && steps.update-changelog.outputs.changelog-updated == 'true' && steps.commit-push.outputs.pushed == 'true'
run: |
BASE_BRANCH="${{ github.event.pull_request.base.ref }}"
BASE_BRANCH="${{ env.CHANGELOG_BASE_BRANCH }}"
BRANCH_NAME="${{ steps.check-existing-changelog-pr.outputs.changelog-pr-branch-name }}"
PR_NUMBER="${{ steps.update-changelog.outputs.pr-number }}"
CHANGELOG_PR_EXISTS="${{ steps.check-existing-changelog-pr.outputs.changelog-pr-exists }}"
Expand Down
Loading