Dependabot Auto-merge (2-day soak) #149
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 Auto-merge (2-day soak) | |
| # Runs daily. For any open Dependabot PR that is >= 2 days old, approved, and | |
| # has passing CI — adds it directly to the merge queue. | |
| on: | |
| schedule: | |
| - cron: '0 9 * * *' # 09:00 UTC daily | |
| workflow_dispatch: # allow manual trigger for testing | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| jobs: | |
| automerge: | |
| name: Auto-merge | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Merge eligible Dependabot PRs | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| REPO: ${{ github.repository }} | |
| run: | | |
| SEVEN_DAYS_AGO=$(date -u -d '2 days ago' '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null || \ | |
| date -u -v-2d '+%Y-%m-%dT%H:%M:%SZ') | |
| echo "Looking for Dependabot PRs opened before $SEVEN_DAYS_AGO ..." | |
| gh pr list \ | |
| --repo "$REPO" \ | |
| --author "app/dependabot" \ | |
| --state open \ | |
| --json number,title,createdAt,reviewDecision,mergeStateStatus \ | |
| | jq -c '.[]' \ | |
| | while IFS= read -r pr; do | |
| NUMBER=$(echo "$pr" | jq -r '.number') | |
| TITLE=$(echo "$pr" | jq -r '.title') | |
| CREATED=$(echo "$pr" | jq -r '.createdAt') | |
| REVIEW=$(echo "$pr" | jq -r '.reviewDecision') | |
| MERGE_STATE=$(echo "$pr" | jq -r '.mergeStateStatus') | |
| # Skip only on states that definitively mean "can't merge | |
| # right now": real conflicts (DIRTY) or failing | |
| # required-checks/review (BLOCKED — reviewDecision is | |
| # checked separately below anyway). UNKNOWN is NOT one of | |
| # these — it just means GitHub hasn't finished computing | |
| # mergeability yet (very common right after a PR updates), | |
| # not "already queued." Treating it as "skip" meant every | |
| # PR here was skipped forever, since gh pr list almost | |
| # always returns UNKNOWN on a fresh poll. Let a merge | |
| # attempt happen and fail on its own terms instead of | |
| # pre-filtering on a stale/transient field. | |
| if [ "$MERGE_STATE" = "DIRTY" ] || [ "$MERGE_STATE" = "BLOCKED" ]; then | |
| echo "PR #$NUMBER — merge state is $MERGE_STATE, skipping" | |
| continue | |
| fi | |
| # Skip if not yet approved | |
| if [ "$REVIEW" != "APPROVED" ]; then | |
| echo "PR #$NUMBER — not approved yet ($REVIEW), skipping" | |
| continue | |
| fi | |
| # Skip if less than 2 days old | |
| if [[ "$CREATED" > "$SEVEN_DAYS_AGO" ]]; then | |
| echo "PR #$NUMBER — opened $CREATED, not yet 2 days old, skipping" | |
| continue | |
| fi | |
| echo "PR #$NUMBER ($TITLE) — eligible, adding to merge queue" | |
| # --auto is required here, not optional: this repo's main | |
| # branch only accepts merges via the merge queue, and a | |
| # bare `gh pr merge --squash` (no --auto) just prints | |
| # "the merge strategy for main is set by the merge queue" | |
| # and does nothing — it was never actually enqueuing | |
| # anything even on the rare PR that got this far. | |
| gh pr merge --squash --auto "$NUMBER" --repo "$REPO" || \ | |
| echo "PR #$NUMBER — merge queue failed (may already be queued)" | |
| done |