Skip to content

Dependabot security auto-merge #14

Dependabot security auto-merge

Dependabot security auto-merge #14

name: Dependabot security auto-merge
on:
workflow_run:
workflows: ["CI"]
types: [completed]
jobs:
automerge:
# Only proceed if CI passed and the workflow was triggered by a pull request
if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request'
concurrency:
group: dependabot-automerge-${{ github.event.workflow_run.head_sha }}
cancel-in-progress: true
runs-on: ubuntu-latest
permissions:
pull-requests: write
contents: write
steps:
- name: Get PR number for this workflow run
id: get-pr
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
run: |
# Prefer PR number from the workflow_run payload when available
PR_NUMBER="${{ github.event.workflow_run.pull_requests[0].number }}"
# Fallback: search all open PRs (with pagination) for matching HEAD_SHA
if [ -z "$PR_NUMBER" ]; then
PR_NUMBER=$(gh api repos/${{ github.repository }}/pulls --paginate \
--jq ".[] | select(.head.sha == \"$HEAD_SHA\") | .number" | head -1)
fi
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
- name: Get PR author and changed files
id: pr-info
if: steps.get-pr.outputs.pr_number != ''
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ steps.get-pr.outputs.pr_number }}
run: |
AUTHOR=$(gh api repos/${{ github.repository }}/pulls/$PR_NUMBER \
--jq '.user.login')
FILES=$(gh api repos/${{ github.repository }}/pulls/$PR_NUMBER/files \
--jq '[.[].filename] | join(" ")')
TITLE=$(gh api repos/${{ github.repository }}/pulls/$PR_NUMBER \
--jq '.title')
echo "author=$AUTHOR" >> $GITHUB_OUTPUT
echo "files=$FILES" >> $GITHUB_OUTPUT
echo "title=$TITLE" >> $GITHUB_OUTPUT
- name: Check bumped package against open Dependabot alerts
id: security-check
if: steps.get-pr.outputs.pr_number != ''
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TITLE="${{ steps.pr-info.outputs.title }}"
# Dependabot PR titles look like "build(deps): bump <pkg> from X to Y"
PKG=$(echo "$TITLE" | sed -n 's/^.*[Bb]ump \([^ ]*\) from.*$/\1/p')
IS_SECURITY=false
if [ -n "$PKG" ]; then
MATCH=$(gh api repos/${{ github.repository }}/dependabot/alerts --paginate \
--jq --arg pkg "$PKG" '.[] | select(.state == "open" and .dependency.package.name == $pkg) | .number' | head -1)
if [ -n "$MATCH" ]; then
IS_SECURITY=true
fi
fi
echo "package=$PKG" >> $GITHUB_OUTPUT
echo "is_security=$IS_SECURITY" >> $GITHUB_OUTPUT
- name: Auto-approve and merge security-only lock file updates
if: |
steps.pr-info.outputs.author == 'dependabot[bot]' &&
(steps.pr-info.outputs.files == 'package-lock.json' || steps.pr-info.outputs.files == 'Gemfile.lock') &&
steps.security-check.outputs.is_security == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ steps.get-pr.outputs.pr_number }}
run: |
gh pr review $PR_NUMBER --approve --repo ${{ github.repository }} \
--body "Auto-approving: security patch, lock file only."
for attempt in 1 2 3 4 5; do
if gh pr merge $PR_NUMBER --squash --repo ${{ github.repository }}; then
exit 0
fi
echo "Merge attempt $attempt failed, retrying in 10s..."
sleep 10
done
echo "::error::Failed to merge PR $PR_NUMBER after 5 attempts"
exit 1