Repository navigation
Dependabot security auto-merge #14
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |