# Ultralytics YOLO 🚀, AGPL-3.0 license
# Automatically merges repository 'main' branch into all open PRs to keep them up-to-date
# Action runs on updates to main branch so when one PR merges to main all others update
name : Merge main into PRs
on :
workflow_dispatch :
# push:
# branches:
# - ${{ github.event.repository.default_branch }}
jobs :
Merge :
if : github.repository == 'ultralytics/ultralytics'
runs-on : ubuntu-latest
steps :
- name : Checkout repository
uses : actions/checkout@v4
with :
fetch-depth : 0
- uses : actions/setup-python@v5
with :
python-version : "3.x"
cache : "pip"
- name : Install requirements
run : |
pip install pygithub
- name : Merge default branch into PRs
shell : python
run : |
from github import Github
import os
import time
g = Github(os.getenv('GITHUB_TOKEN'))
repo = g.get_repo(os.getenv('GITHUB_REPOSITORY'))
# Fetch the default branch name
default_branch_name = repo.default_branch
default_branch = repo.get_branch(default_branch_name)
# Initialize counters
updated_branches = 0
up_to_date_branches = 0
errors = 0
for pr in repo.get_pulls(state='open', sort='created') :
try :
# Label PRs as popular for positive reactions
reactions = pr.as_issue().get_reactions()
if sum([(1 if r.content not in {"-1", "confused"} else 0) for r in reactions]) > 5:
pr.set_labels(*("popular",) + tuple(l.name for l in pr.get_labels()))
# Get full names for repositories and branches
base_repo_name = repo.full_name
head_repo_name = pr.head.repo.full_name
base_branch_name = pr.base.ref
head_branch_name = pr.head.ref
# Check if PR is behind the default branch
comparison = repo.compare(default_branch.commit.sha, pr.head.sha)
if comparison.behind_by > 0 :
print(f"⚠️ PR #{pr.number} ({head_repo_name}:{head_branch_name} -> {base_repo_name}:{base_branch_name}) is behind {default_branch_name} by {comparison.behind_by} commit(s).")
# Attempt to update the branch
try :
success = pr.update_branch()
assert success, "Branch update failed"
print(f"✅ Successfully merged '{default_branch_name}' into PR #{pr.number} ({head_repo_name}:{head_branch_name} -> {base_repo_name}:{base_branch_name}).")
updated_branches += 1
time.sleep(10) # rate limit merges
except Exception as update_error :
print(f"❌ Could not update PR #{pr.number} ({head_repo_name}:{head_branch_name} -> {base_repo_name}:{base_branch_name}): {update_error}")
errors += 1
else :
print(f"✅ PR #{pr.number} ({head_repo_name}:{head_branch_name} -> {base_repo_name}:{base_branch_name}) is already up to date with {default_branch_name}, no merge required.")
up_to_date_branches += 1
except Exception as e :
print(f"❌ Could not process PR #{pr.number}: {e}")
errors += 1
# Print summary
print("\n\nSummary:")
print(f"Branches updated: {updated_branches}")
print(f"Branches already up-to-date: {up_to_date_branches}")
print(f"Total errors: {errors}")
env :
GITHUB_TOKEN : ${{ secrets.PERSONAL_ACCESS_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_REPOSITORY : ${{ github.repository }}