Skip to content
Merged
130 changes: 130 additions & 0 deletions .github/DEPENDABOT_AUTO_MERGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Dependabot Auto-Merge Workflow

## Overview

The `dependabot-auto-merge.yaml` workflow automatically enables auto-merge for Dependabot pull requests targeting the `main` branch. Once all branch protection requirements are met (approvals, CI checks), the PR will merge automatically.

## How It Works

1. **Triggers:** The workflow runs when:
- A Dependabot PR is opened or updated (`pull_request_target`)
- A review is submitted on a Dependabot PR (`pull_request_review`)
- CI checks complete on a Dependabot PR branch (`check_suite`)

2. **Verification:** The workflow verifies that:
- The PR author is `dependabot[bot]`
- The base branch is `main`

3. **Actions:**
- Enables auto-merge on the PR

4. **Merge:** Once all branch protection requirements are satisfied (including **manual approval** and passing CI checks), GitHub automatically merges the PR.

## Important: Use of `pull_request_target`

This workflow uses the `pull_request_target` event instead of `pull_request` to obtain elevated permissions needed to enable auto-merge on protected branches.

**Why this is needed:**
When workflows are triggered by Dependabot PRs using the `pull_request` event, the `GITHUB_TOKEN` has **read-only permissions** for security reasons, even if the workflow declares `contents: write` and `pull-requests: write`. This is a GitHub security feature to prevent malicious dependency updates from modifying the repository.

**Why this is safe:**
Using `pull_request_target` for Dependabot auto-merge is safe because:

1. We verify the PR author is `dependabot[bot]` before taking any action
2. We don't check out or execute code from the PR
3. We only enable auto-merge, which still requires all branch protection rules to pass
4. Dependabot PRs are created by GitHub's trusted Dependabot service

## Alternative Solutions

If you prefer not to use `pull_request_target`, there are more secure alternatives:

### Option 1: Use a GitHub App (Recommended)

Create a GitHub App with appropriate permissions and use it to generate tokens:

1. Create a GitHub App with `contents: write` and `pull_requests: write` permissions
2. Install the app on the repository
3. Generate an installation access token in the workflow
4. Use the token instead of `secrets.GITHUB_TOKEN`

Example:

```yaml
- uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
- name: Enable auto-merge
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: gh pr merge --auto --merge "$PR_NUMBER"
```

### Option 2: Use a Personal Access Token (PAT)

Create a PAT with appropriate permissions and store it as a repository secret:

1. Create a PAT with `repo` scope
2. Store it as a repository secret (e.g., `DEPENDABOT_PAT`)
3. Use it in the workflow

Example:

```yaml
- name: Enable auto-merge
env:
GH_TOKEN: ${{ secrets.DEPENDABOT_PAT }}
run: gh pr merge --auto --merge "$PR_NUMBER"
```

**Note:** This is simpler than using a GitHub App but less secure because PATs have broader access and don't expire automatically.

### Option 3: Repository Settings

Configure the repository to allow Dependabot to bypass branch protection:

1. Go to repository Settings → Branches → Branch protection rules
2. Edit the rule for `main`
3. Add `dependabot[bot]` to the list of actors who can bypass required pull requests

**Note:** This approach has security implications and is generally not recommended.

## Troubleshooting

### Auto-merge is not being enabled

If the workflow runs but auto-merge is not enabled, check the workflow logs for error messages:

- **"auto-merge is already enabled"** - Auto-merge was already set on a previous run
- **"not authorized for this protected branch"** - The token doesn't have sufficient permissions. This can occur if:
- Branch protection rules require additional permissions beyond what `pull_request_target` provides
- The repository has custom protection rules that prevent even elevated tokens from enabling auto-merge
- Consider using a GitHub App or PAT with appropriate permissions (see Alternative Solutions above)
- **"Required status checks"** - Waiting for CI checks to pass
- **"Required approving review"** - Waiting for approval (the workflow attempts to approve automatically)

### The PR is not merging automatically

Even after auto-merge is enabled, the PR won't merge until:

1. All required reviews are approved
2. All required status checks pass
3. No blocking conversations exist (if required)
4. All other branch protection requirements are met

Check the PR's "Merge" section for the current status.

## Security Considerations

- The workflow only runs on PRs opened by `dependabot[bot]`
- It does not check out or run code from the PR
- Auto-merge still requires all branch protection rules to pass
- The workflow is transparent and auditable (all runs are visible in the Actions tab)

## References

- [GitHub Docs: Automatically merging a pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/automatically-merging-a-pull-request)
- [GitHub Docs: Automating Dependabot with GitHub Actions](https://docs.github.com/en/code-security/dependabot/working-with-dependabot/automating-dependabot-with-github-actions)
- [Dependabot fetch-metadata action](https://github.com/dependabot/fetch-metadata)
4 changes: 2 additions & 2 deletions .github/workflows/codeql-analysis.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ jobs:
build-path: ${{ env.local_build_path }}

- name: Initialize CodeQL
uses: github/codeql-action/init@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4.32.2
uses: github/codeql-action/init@9e907b5e64f6b83e7804b09294d44122997950d6 # v4.32.3
with:
languages: ${{ matrix.language }}
config-file: ${{ env.local_checkout_path }}/.github/codeql/codeql-config.yml
Expand All @@ -326,7 +326,7 @@ jobs:

# Run CodeQL analysis (uploads results to code scanning)
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4.32.2
uses: github/codeql-action/analyze@9e907b5e64f6b83e7804b09294d44122997950d6 # v4.32.3
with:
checkout_path: ${{ env.local_checkout_path }}
output: results
Expand Down
29 changes: 26 additions & 3 deletions .github/workflows/dependabot-auto-merge.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
name: Dependabot auto-merge
on:
pull_request:
# Use pull_request_target instead of pull_request to get elevated permissions
# This is safe for Dependabot PRs because:
# 1. We verify the PR author is dependabot[bot]
# 2. We don't check out or run code from the PR
# 3. We only enable auto-merge, which requires branch protection to pass
pull_request_target:
types: [opened, reopened, synchronize]
pull_request_review:
types: [submitted]
check_suite:
types: [completed]

Expand All @@ -13,7 +20,7 @@ jobs:
dependabot:
runs-on: ubuntu-latest
if: |
(github.event_name == 'pull_request' && github.event.pull_request.user.login == 'dependabot[bot]' && github.event.pull_request.base.ref == 'main') ||
(github.event_name == 'pull_request_target' && github.event.pull_request.user.login == 'dependabot[bot]' && github.event.pull_request.base.ref == 'main') ||
(github.event_name == 'pull_request_review' && github.event.pull_request.user.login == 'dependabot[bot]' && github.event.pull_request.base.ref == 'main') ||
(github.event_name == 'check_suite' && github.event.check_suite.pull_requests[0] != null && startsWith(github.event.check_suite.head_branch, 'dependabot/'))
steps:
Expand All @@ -36,6 +43,7 @@ jobs:
echo "number=$PR_NUMBER"
} >> "$GITHUB_OUTPUT"
else
# For pull_request_target and pull_request_review, use event data
{
echo "author=${{ github.event.pull_request.user.login }}"
echo "base_ref=${{ github.event.pull_request.base.ref }}"
Expand All @@ -47,16 +55,31 @@ jobs:

- name: Enable auto-merge for Dependabot PRs
if: steps.pr.outputs.author == 'dependabot[bot]' && steps.pr.outputs.base_ref == 'main'
shell: bash
run: |
# Attempt to enable auto-merge. The gh CLI doesn't provide structured error codes,
# so we must parse error messages. Common expected errors:
# - "auto-merge is already enabled" - auto-merge was already set
# - "not authorized for this protected branch" - branch protection requirements not yet met
# NOTE: This typically occurs when the GITHUB_TOKEN doesn't have sufficient permissions.
# For workflows triggered by Dependabot PRs, the token has restricted permissions even
# with contents:write and pull-requests:write. Solutions include:
# 1. Use a GitHub App token (most secure)
# 2. Use a PAT stored in secrets (simpler but less secure)
# 3. Use pull_request_target trigger (has security implications)
# - "Required status checks" - waiting for CI checks to pass
# - "Required approving review" - waiting for approval
set -o pipefail
if ! gh pr merge --auto --merge "${{ steps.pr.outputs.number }}" --repo "${{ github.repository }}" 2>&1 | tee /tmp/gh-output.txt; then
if grep -qE "auto-merge is already enabled|not authorized for this protected branch|[Rr]equired.*status.*check|[Rr]equired.*approv" /tmp/gh-output.txt; then
if grep -qE "auto-merge is already enabled|not authorized for this protected branch|[Rr]equired.*status.*check|[Rr]equired approving review|[Rr]equired.*review" /tmp/gh-output.txt; then
echo "Auto-merge not enabled yet - this is expected when requirements are not met or already enabled"
if grep -q "not authorized for this protected branch" /tmp/gh-output.txt; then
echo ""
echo "NOTE: The 'not authorized for this protected branch' error typically means:"
echo " - The GITHUB_TOKEN has restricted permissions when triggered by Dependabot PRs"
echo " - To fix this, consider using a GitHub App token or PAT with appropriate permissions"
echo " - See workflow comments for more details"
fi
exit 0
else
echo "Unexpected error enabling auto-merge:"
Expand Down
Loading