Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file

version: 2
updates:
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
time: "03:00"
open-pull-requests-limit: 20

- package-ecosystem: "cargo"
directories:
- "/"
directories:
- "/src/sandbox/runtime"
- "/src/code-validator/guest"
schedule:
interval: "daily"
time: "03:00"
open-pull-requests-limit: 20

- package-ecosystem: "npm"
directory: "/src/js-host-api"
directories:
- "/"
- "/src/code-validator/guest"
schedule:
interval: "daily"
time: "03:00"
open-pull-requests-limit: 20
52 changes: 52 additions & 0 deletions .github/workflows/auto-merge-dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Auto Merge Dependabot PRs

on:
schedule:
# Run daily at 04:00 UTC since dependabot runs at 03:00 UTC
- cron: '0 4 * * *'
workflow_dispatch: # Allow manual trigger

# This workflow uses a GitHub App token to approve and merge Dependabot PRs
# The token is created using the `actions/create-github-app-token` action
# The token is used so that the updates are made by the GitHub App instead of GitHub Actions
# and will show up as such in the PR comments and history
# In addition, the token is scoped to only the permissions needed for this workflow
# see https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/making-authenticated-api-requests-with-a-github-app-in-a-github-actions-workflow for details

jobs:
auto-merge-dependabot:
permissions:
contents: read
runs-on: ubuntu-latest
steps:

# Gets the GitHub App token
- uses: actions/create-github-app-token@v3
id: get-app-token
with:
# required
app-id: ${{ secrets.DEPENDABOT_APP_ID }}
private-key: ${{ secrets.DEPENDABOT_APP_KEY }}
permission-pull-requests: write
permission-contents: write
permission-checks: read
permission-actions: read

- name: Checkout code
uses: actions/checkout@v6
with:
token: ${{ steps.get-app-token.outputs.token }}
persist-credentials: false

- name: Setup GitHub CLI
run: |
# GitHub CLI is pre-installed on GitHub-hosted runners
gh --version

- name: Make script executable
run: chmod +x ./scripts/auto-approve-dependabot.sh

- name: Run auto approve script
env:
GITHUB_TOKEN: ${{ steps.get-app-token.outputs.token }}
run: ./scripts/auto-approve-dependabot.sh ${{ github.repository }}
160 changes: 160 additions & 0 deletions scripts/auto-approve-dependabot.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
#!/bin/bash
set -e
set -o pipefail

# This script checks for open PRs from dependabot that have all checks passing and have not been
# modified by another user, and approves+merges them automatically.
# To be run as a GitHub action.

# Check if repository argument is provided
if [ -z "$1" ]; then
echo "Error: Repository name not provided."
echo "Usage: $0 <owner/repo>"
echo "Example: $0 hyperlight-dev/hyperlight"
exit 1
fi

REPO="$1"
echo "Checking for open Dependabot PRs to approve and merge in $REPO..."

# Get all open PRs from dependabot
# We filter so that only PRs that are not from forks and are in branches starting with "dependabot/cargo" are included.
dependabot_prs=$(gh pr list -R "$REPO" --author "dependabot[bot]" --state open --json number,title,reviews,headRepositoryOwner,headRefName | jq --arg repo_owner "$(echo "$REPO" | cut -d'/' -f1)" '[.[] | select(.headRepositoryOwner.login == $repo_owner and (.headRefName | startswith("dependabot/cargo")))]')
Comment thread
simongdavies marked this conversation as resolved.
# Exit early if no PRs found
if [ -z "$dependabot_prs" ] || [ "$dependabot_prs" = "[]" ]; then
echo "No open Dependabot PRs found in $REPO"
exit 0
fi

# Count how many PRs we found
pr_count=$(echo "$dependabot_prs" | jq 'length')
echo "Found $pr_count open Dependabot PRs in $REPO"

# Process each PR
echo "$dependabot_prs" | jq -c '.[]' | while read -r pr; do
pr_number=$(echo "$pr" | jq -r '.number')
pr_title=$(echo "$pr" | jq -r '.title')

echo "Processing PR #$pr_number: $pr_title"

# Check if PR only modifies allowed files
pr_files=$(gh pr view "$pr_number" -R "$REPO" --json files)
invalid_files=$(echo "$pr_files" | jq -r '.files[].path' | grep -v -E '(Cargo\.toml|Cargo\.lock)' || true)

if [ -n "$invalid_files" ]; then
echo " ❌ PR #$pr_number modifies files that are not allowed for auto-merge:"
printf '%s\n' "$invalid_files" | sed 's/^/ - /'
echo " ℹ️ Only changes to Cargo.toml and Cargo.lock are allowed"
continue
fi

echo " ✅ PR #$pr_number only modifies allowed files (Cargo.toml and Cargo.lock)"

# First, get detailed PR information including all checks
pr_details=$(gh pr view "$pr_number" -R "$REPO" --json statusCheckRollup,state)

# Check if all status checks have passed (regardless of required or not)
all_checks_pass=true
has_pending_checks=false
failed_checks=""

# First identify checks that are still in progress
pending_checks=$(echo "$pr_details" | jq -r '.statusCheckRollup[] | select(.status == "IN_PROGRESS" or .status == "QUEUED" or .status == "PENDING") | .name')

# Check for permission-required checks
permission_required_checks=$(echo "$pr_details" | jq -r '.statusCheckRollup[] | select(.status == "WAITING" or .status == "ACTION_REQUIRED" or (.status == "QUEUED" and .conclusion == null and .detailsUrl != null and (.detailsUrl | contains("waiting-for-approval")))) | .name')

# Don't approve if there are checks required that need permission to run
if [ -n "$permission_required_checks" ]; then
echo " 🔐 PR #$pr_number has checks waiting for permission:"
echo "$permission_required_checks" | sed 's/^/ - /'
echo " ❌ Skipping auto-approval due to permission-required checks"
continue
fi

if [ -n "$pending_checks" ]; then
echo " ⏳ PR #$pr_number has pending checks:"
echo "$pending_checks" | sed 's/^/ - /'
echo " ℹ️ We will still approve the PR so it can merge automatically once all checks pass"
has_pending_checks=true
fi

# Check for failed checks - only include checks that have a conclusion and are not still running
# Explicitly exclude checks with status IN_PROGRESS, QUEUED, or PENDING
failed_checks=$(echo "$pr_details" | jq -r '.statusCheckRollup[] |
select(.conclusion != null and
.conclusion != "SUCCESS" and
.conclusion != "NEUTRAL" and
.conclusion != "SKIPPED" and
.status != "IN_PROGRESS" and
.status != "QUEUED" and
.status != "PENDING") | .name')

if [ -n "$failed_checks" ]; then
echo " ❌ PR #$pr_number has failed checks:"
echo "$failed_checks" | sed 's/^/ - /'
all_checks_pass=false
continue
fi

# If we've reached here, either all checks have passed or some are pending
if [ "$has_pending_checks" = false ]; then
echo " ✅ All status checks passed for PR #$pr_number"
fi

# Check if PR has been modified by someone other than dependabot
pr_commits=$(gh pr view "$pr_number" -R "$REPO" --json commits)
non_dependabot_authors=$(echo "$pr_commits" | jq -r '.commits[].authors[].login' | grep -v -e "dependabot\[bot\]" -e "^$" || true)

if [ -n "$non_dependabot_authors" ]; then
echo " ❌ PR #$pr_number has been modified by users other than dependabot: $non_dependabot_authors"
continue
fi

# Check if PR needs approval (i.e., hasn't been approved already)
already_approved=$(echo "$pr" | jq -r '.reviews[] | select(.state == "APPROVED") | .state' | grep -c "APPROVED" || true)

if [ "$already_approved" -eq 0 ]; then
echo " ✅ Approving PR #$pr_number"
gh pr review "$pr_number" -R "$REPO" --approve -b "Automatically approved by dependabot auto-approve workflow"
else
echo " ℹ️ PR #$pr_number is already approved"
fi

if [ "$has_pending_checks" = true ] || [ "$all_checks_pass" = true ]; then
# Check if PR is draft and whether it is up-to-date with base branch
pr_merge_info=$(gh pr view "$pr_number" -R "$REPO" --json isDraft,mergeStateStatus)
is_draft=$(echo "$pr_merge_info" | jq -r '.isDraft')
merge_status=$(echo "$pr_merge_info" | jq -r '.mergeStateStatus')

if [ "$is_draft" = "true" ]; then
echo " ⚠️ PR #$pr_number is a draft PR; skipping merge"
continue
fi

if [ "$merge_status" != "CLEAN" ]; then
echo " ⚠️ PR #$pr_number is not up to date (status: $merge_status)"
# Enable auto-merge to merge once checks pass
echo " ✅ Enabling auto-merge (squash strategy) for PR #$pr_number"
if gh pr merge "$pr_number" -R "$REPO" --auto --squash; then
echo " ✅ Auto-merge enabled for PR #$pr_number"
else
echo " ⚠️ Failed to enable auto-merge for PR #$pr_number; continuing to next PR"
continue
fi
else
echo " ✅ PR #$pr_number is up to date with base branch"
# PR is already clean/mergeable - merge directly instead of enabling auto-merge
echo " ✅ Merging PR #$pr_number directly (squash strategy)"
if gh pr merge "$pr_number" -R "$REPO" --squash; then
echo " ✅ PR #$pr_number merged successfully"
else
echo " ⚠️ Failed to merge PR #$pr_number; continuing to next PR"
continue
fi
fi
fi

done

echo "Finished processing Dependabot PRs for $REPO"
Loading