From d5b509e000cf9d8e6f684fe8f948386df2a3d90b Mon Sep 17 00:00:00 2001 From: Ian McBurnie <38065+ianmcburnie@users.noreply.github.com> Date: Tue, 29 Sep 2026 16:55:02 -0700 Subject: [PATCH 1/2] fix: detect Dependabot security PRs by cross-checking alerts, not body text Co-Authored-By: Claude Sonnet 5 --- .github/workflows/dependabot-automerge.yml | 43 +++++++++++++++++----- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/.github/workflows/dependabot-automerge.yml b/.github/workflows/dependabot-automerge.yml index f7dfa562..ba46e7e1 100644 --- a/.github/workflows/dependabot-automerge.yml +++ b/.github/workflows/dependabot-automerge.yml @@ -46,26 +46,51 @@ jobs: --jq '.user.login') FILES=$(gh api repos/${{ github.repository }}/pulls/$PR_NUMBER/files \ --jq '[.[].filename] | join(" ")') - BODY=$(gh api repos/${{ github.repository }}/pulls/$PR_NUMBER \ - --jq '.body') - if echo "$BODY" | grep -qE '(GHSA-[A-Za-z0-9-]+|CVE-[0-9]{4}-[0-9]+|dependabot-automerge-start)'; then - IS_SECURITY=true - else - IS_SECURITY=false - fi + 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 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.is_security == 'true' + 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, package-lock.json only." - gh pr merge $PR_NUMBER --squash --repo ${{ github.repository }} + + 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 From 9fac81333a97fbe2d93b9f97d371a6fbfe9944be Mon Sep 17 00:00:00 2001 From: Ian McBurnie <38065+ianmcburnie@users.noreply.github.com> Date: Wed, 30 Sep 2026 15:12:59 -0700 Subject: [PATCH 2/2] fix: handle grouped/ancestor Dependabot bump titles in security check Co-Authored-By: Claude Sonnet 5 --- .github/workflows/dependabot-automerge.yml | 23 +++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/.github/workflows/dependabot-automerge.yml b/.github/workflows/dependabot-automerge.yml index ba46e7e1..8499715c 100644 --- a/.github/workflows/dependabot-automerge.yml +++ b/.github/workflows/dependabot-automerge.yml @@ -60,17 +60,26 @@ jobs: run: | TITLE="${{ steps.pr-info.outputs.title }}" # Dependabot PR titles look like "build(deps): bump from X to Y" - PKG=$(echo "$TITLE" | sed -n 's/^.*[Bb]ump \([^ ]*\) from.*$/\1/p') + # or, for grouped/ancestor bumps, "build(deps): bump , and " + REST=$(echo "$TITLE" | sed -n 's/^.*[Bb]ump \(.*\)$/\1/p') + REST=$(echo "$REST" | sed -E 's/ from .* to .*$//') + mapfile -t PACKAGES < <(echo "$REST" | sed -E 's/, /\n/g; s/ and /\n/g') + + OPEN_ALERT_PACKAGES=$(gh api repos/${{ github.repository }}/dependabot/alerts --paginate \ + --jq '.[] | select(.state == "open") | .dependency.package.name' | sort -u) 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 + MATCHED_PKG="" + for PKG in "${PACKAGES[@]}"; do + PKG=$(echo "$PKG" | xargs) + [ -z "$PKG" ] && continue + if echo "$OPEN_ALERT_PACKAGES" | grep -Fxq "$PKG"; then IS_SECURITY=true + MATCHED_PKG="$PKG" + break fi - fi - echo "package=$PKG" >> $GITHUB_OUTPUT + done + echo "package=$MATCHED_PKG" >> $GITHUB_OUTPUT echo "is_security=$IS_SECURITY" >> $GITHUB_OUTPUT - name: Auto-approve and merge security-only lock file updates