Auto Merge Pull Requests A Comprehensive Guide With GitHub Actions
This article delves into the process of automating the merging of pull requests in a GitHub repository using GitHub Actions. We will explore how to create a workflow that automatically merges pull requests based on specific criteria, such as the user initiating the pull request and the presence of a specific keyword in the title. This automation not only streamlines the development workflow but also ensures that only authorized changes are merged into the main branch.
Understanding the Need for Auto-Merging
In many software development projects, pull requests are a crucial part of the code review and integration process. However, manually merging pull requests can be time-consuming and prone to errors. Automating this process can significantly improve efficiency, reduce the workload on maintainers, and ensure consistent merging practices. By setting up specific conditions for auto-merging, you can maintain control over your codebase while accelerating the integration of changes.
Creating a GitHub Actions Workflow for Auto-Merging
To implement auto-merging, we will create a GitHub Actions workflow. This workflow will define the steps to be executed when a pull request is opened, synchronized, or reopened. The workflow will include checks to ensure that the pull request meets our criteria for auto-merging, such as being initiated by a specific user and having a title containing a specific keyword. Let’s dive into the details of setting up this workflow.
Workflow Configuration
The first step is to create a new workflow file in your repository. This file will be located in the .github/workflows
directory. The workflow file will define the triggers, jobs, and steps for the auto-merging process. The configuration begins with specifying the name of the workflow and the events that trigger it. In our case, we will use the pull_request_target
event, which is triggered when a pull request is opened, synchronized, or reopened. This ensures that our workflow is activated whenever there are changes to a pull request.
name: Auto Merge PR
on:
pull_request_target:
types: [opened, synchronize, reopened]
Defining the Auto-Merge Job
Next, we define the auto-merge
job, which contains the logic for merging the pull request. This job includes several key steps, such as checking out the main branch, configuring Git, getting the patch of the commit in the pull request, applying the patch to the main branch, and merging the changes. The job is configured to run on an ubuntu-latest
runner and requires specific permissions to write to the repository contents and pull requests.
jobs:
auto-merge:
if: |
github.event.pull_request.user.login == '13x1' &&
contains(github.event.pull_request.title, '[merge]')
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
# Steps will be added here
The if
condition in the job definition ensures that the workflow only runs if the pull request is initiated by the user 13x1
and the title contains the keyword [merge]
. This is a crucial security measure to prevent unauthorized merges.
Checking Out the Main Branch
The first step within the job is to check out the main
branch. This is necessary to apply the changes from the pull request to the main branch. We use the actions/checkout@v4
action to perform this task. The fetch-depth: 0
option ensures that the entire Git history is fetched, which is required for creating patches.
- name: Checkout main branch
uses: actions/checkout@v4
with:
ref: main
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
Configuring Git
Before we can make any changes, we need to configure Git with the user name and email. This is important for attributing the commits made by the workflow to the github-actions[bot]
user. This ensures that the commits are properly tracked and attributed.
- name: Configure Git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
Getting the Patch of the Commit
The next step is to get the patch of the commit in the pull request. This involves fetching the pull request branch and creating a patch file containing the changes. We use Git commands to fetch the pull request and generate the patch. This step is crucial for applying the changes to the main branch.
- name: Get PR commits and create patch
id: get-patch
run: |
# Get the PR head commit SHA
PR_HEAD_SHA="${{ github.event.pull_request.head.sha }}"
PR_BASE_SHA="${{ github.event.pull_request.base.sha }}"
echo "PR Head SHA: $PR_HEAD_SHA"
echo "PR Base SHA: $PR_BASE_SHA"
# Fetch the PR branch
git fetch origin pull/${{ github.event.number }}/head:pr-branch
# Create patch from the PR commits
git format-patch $PR_BASE_SHA..pr-branch --stdout > pr-changes.patch
echo "Patch created successfully"
echo "patch-file=pr-changes.patch" >> $GITHUB_OUTPUT
Applying the Patch to the Main Branch
Once we have the patch file, we need to apply it to the main branch. This step involves checking if the patch can be applied cleanly and, if necessary, attempting a 3-way merge to resolve any conflicts. If the patch cannot be applied even with a 3-way merge, the workflow will exit with an error. This ensures that only cleanly applied patches are merged.
- name: Apply patch to main branch
run: |
# Apply the patch
if git apply --check pr-changes.patch; then
echo "Patch can be applied cleanly"
git apply pr-changes.patch
else
echo "Patch conflicts detected, attempting 3-way merge"
git apply --3way pr-changes.patch || {
echo "Failed to apply patch even with 3-way merge"
exit 1
}
fi
# Stage all changes
git add .
Committing and Force Merging
After applying the patch, we need to commit the changes and push them to the main branch. This step includes checking if there are any changes to commit and, if so, committing the changes with a message indicating that they were auto-merged. We use a force push to ensure that the changes are merged, even if there are conflicts. This step finalizes the merging process.
- name: Commit and force merge
run: |
# Check if there are any changes to commit
if git diff --staged --quiet; then
echo "No changes to commit"
else
# Commit the changes
git commit -m "Auto-merge: ${{ github.event.pull_request.title }}" \
-m "Merged from PR #${{ github.event.number }}" \
-m "Original author: ${{ github.event.pull_request.user.login }}"
# Force push to main (force merge)
git push origin main --force-with-lease
echo "Successfully merged and pushed to main branch"
fi
Closing the Pull Request
The final step is to close the pull request and add a comment indicating that it was auto-merged successfully. We use the actions/github-script@v7
action to interact with the GitHub API and perform these tasks. This step provides feedback to the pull request author and keeps the repository clean.
- name: Close PR
uses: actions/github-script@v7
with:
script: |
await github.rest.pulls.update({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: ${{ github.event.number }},
state: 'closed'
});
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: ${{ github.event.number }},
body: '✅ Auto-merged successfully! Changes have been applied to the main branch with force merge.'
});
Complete Workflow File
Here is the complete workflow file:
name: Auto Merge PR
on:
pull_request_target:
types: [opened, synchronize, reopened]
jobs:
auto-merge:
if: |
github.event.pull_request.user.login == '13x1' &&
contains(github.event.pull_request.title, '[merge]')
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout main branch
uses: actions/checkout@v4
with:
ref: main
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
- name: Configure Git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Get PR commits and create patch
id: get-patch
run: |
# Get the PR head commit SHA
PR_HEAD_SHA="${{ github.event.pull_request.head.sha }}"
PR_BASE_SHA="${{ github.event.pull_request.base.sha }}"
echo "PR Head SHA: $PR_HEAD_SHA"
echo "PR Base SHA: $PR_BASE_SHA"
# Fetch the PR branch
git fetch origin pull/${{ github.event.number }}/head:pr-branch
# Create patch from the PR commits
git format-patch $PR_BASE_SHA..pr-branch --stdout > pr-changes.patch
echo "Patch created successfully"
echo "patch-file=pr-changes.patch" >> $GITHUB_OUTPUT
- name: Apply patch to main branch
run: |
# Apply the patch
if git apply --check pr-changes.patch; then
echo "Patch can be applied cleanly"
git apply pr-changes.patch
else
echo "Patch conflicts detected, attempting 3-way merge"
git apply --3way pr-changes.patch || {
echo "Failed to apply patch even with 3-way merge"
exit 1
}
fi
# Stage all changes
git add .
- name: Commit and force merge
run: |
# Check if there are any changes to commit
if git diff --staged --quiet; then
echo "No changes to commit"
else
# Commit the changes
git commit -m "Auto-merge: ${{ github.event.pull_request.title }}" \
-m "Merged from PR #${{ github.event.number }}" \
-m "Original author: ${{ github.event.pull_request.user.login }}"
# Force push to main (force merge)
git push origin main --force-with-lease
echo "Successfully merged and pushed to main branch"
fi
- name: Close PR
uses: actions/github-script@v7
with:
script: |
await github.rest.pulls.update({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: ${{ github.event.number }},
state: 'closed'
});
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: ${{ github.event.number }},
body: '✅ Auto-merged successfully! Changes have been applied to the main branch with force merge.'
});
Conclusion
Auto-merging pull requests with GitHub Actions can significantly streamline your development workflow. By setting up a workflow that automates the merging process based on specific criteria, you can improve efficiency and maintain control over your codebase. This article has provided a comprehensive guide to creating such a workflow, including detailed explanations of each step. By following these steps, you can implement auto-merging in your repository and reap the benefits of this powerful automation tool.