name: Dependabot Rebase & Conflict Resolution
on:
pull_request:
types: [opened, synchronize, labeled]
schedule:
- cron: '0 */6 * * *' workflow_dispatch:
inputs:
pr_number:
description: 'PR number to rebase (leave empty for all)'
required: false
type: string
permissions:
contents: write
pull-requests: write
issues: write
jobs:
rebase-dependabot-prs:
runs-on: ubuntu-latest
if: ${{ github.repository == 'hongkongkiwi/rusty-commit' }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Get Dependabot PRs to process
id: get-prs
uses: actions/github-script@v8
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const specificPR = context.payload.inputs?.pr_number;
const { data: prs } = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
per_page: 100
});
// Filter for Dependabot PRs and check mergeable state
const prsToProcess = [];
for (const pr of prs) {
// Skip non-Dependabot PRs unless specific PR requested
if (pr.user?.login !== 'dependabot[bot]' && !specificPR) continue;
if (specificPR && pr.number !== parseInt(specificPR)) continue;
console.log(`Processing PR #${pr.number}: ${pr.title}`);
// Check mergeable state
// mergeable: null = calculating, true = clean, false = conflicts
if (pr.mergeable === null) {
console.log(` Skipping PR #${pr.number}: mergeable state still calculating`);
continue;
}
if (pr.mergeable === true) {
console.log(` PR #${pr.number} is clean, no action needed`);
continue;
}
console.log(` PR #${pr.number} has merge conflicts - marking for rebase`);
prsToProcess.push({
number: pr.number,
branch: pr.head.ref,
base: pr.base.ref,
title: pr.title
});
}
// Output as JSON for next step
core.setOutput('prs', JSON.stringify(prsToProcess));
console.log(`Found ${prsToProcess.length} PR(s) with conflicts to rebase`);
- name: Install jq
if: steps.get-prs.outputs.prs != '[]'
run: |
sudo apt-get update && sudo apt-get install -y jq
- name: Rebase conflicted PRs
if: steps.get-prs.outputs.prs != '[]'
run: |
# Parse the PR list from JSON
prs='${{ steps.get-prs.outputs.prs }}'
# Process each PR
echo "$prs" | jq -c '.[]' | while read -r pr; do
pr_number=$(echo "$pr" | jq -r '.number')
pr_branch=$(echo "$pr" | jq -r '.branch')
base_branch=$(echo "$pr" | jq -r '.base')
pr_title=$(echo "$pr" | jq -r '.title')
echo "Rebasing PR #$pr_number: $pr_title"
echo " Branch: $pr_branch -> $base_branch"
# Fetch branches
git fetch origin "$base_branch":"$base_branch" || {
echo "Failed to fetch base branch $base_branch"
echo "pr_${pr_number}_status=fetch_failed" >> "$GITHUB_OUTPUT"
continue
}
git fetch origin "$pr_branch":"$pr_branch" || {
echo "Failed to fetch PR branch $pr_branch"
echo "pr_${pr_number}_status=fetch_failed" >> "$GITHUB_OUTPUT"
continue
}
# Checkout PR branch
git checkout "$pr_branch" || {
echo "Failed to checkout PR branch $pr_branch"
echo "pr_${pr_number}_status=checkout_failed" >> "$GITHUB_OUTPUT"
continue
}
# Attempt rebase
if git rebase "$base_branch"; then
# Push rebased branch
if git push --force-with-lease origin "$pr_branch"; then
echo "pr_${pr_number}_status=rebased" >> "$GITHUB_OUTPUT"
echo "Successfully rebased PR #$pr_number"
else
echo "pr_${pr_number}_status=push_failed" >> "$GITHUB_OUTPUT"
echo "Failed to push rebased branch for PR #$pr_number"
fi
else
# Rebase failed
echo "pr_${pr_number}_status=rebase_failed" >> "$GITHUB_OUTPUT"
echo "Rebase failed for PR #$pr_number"
git rebase --abort 2>/dev/null || true
fi
done
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Comment on rebased PRs
if: steps.get-prs.outputs.prs != '[]'
uses: actions/github-script@v8
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const prs = JSON.parse('${{ steps.get-prs.outputs.prs }}');
for (const pr of prs) {
try {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
body: '🤖 **Auto-rebase successful**\n\nThis PR has been automatically rebased to resolve merge conflicts.'
});
console.log(`Commented on PR #${pr.number}`);
} catch (error) {
console.error(`Failed to comment on PR #${pr.number}:`, error.message);
}
}
- name: Close PRs with unresolvable conflicts
if: failure()
uses: actions/github-script@v8
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const prs = JSON.parse('${{ steps.get-prs.outputs.prs }}');
for (const pr of prs) {
try {
// Close the PR
await github.rest.pulls.update({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pr.number,
state: 'closed'
});
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
body: `🤖 **Auto-rebase failed**\n\nThis Dependabot PR has merge conflicts that could not be automatically resolved. The PR has been closed.\n\n**Next steps:**\n- Dependabot will automatically create a new PR with updated dependencies\n- Or you can manually update dependencies and resolve conflicts`
});
console.log(`Closed PR #${pr.number} due to unresolvable conflicts`);
} catch (error) {
console.error(`Error closing PR #${pr.number}:`, error.message);
}
}