-
Notifications
You must be signed in to change notification settings - Fork 277
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat 16099: Rebase strategy for backport PRs
- Loading branch information
1 parent
7d2ae0b
commit 7fccbde
Showing
4 changed files
with
182 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
pipeline{ | ||
agent any | ||
environment{ | ||
GITHUB_TOKEN = credentials('github-token') | ||
REPO_URL = 'https://github.com/opensearch-project/opensearch-build.git' | ||
REPO_DIR = 'opensearch-build' | ||
} | ||
stages{ | ||
stage('Determine PR type'){ | ||
steps{ | ||
echo 'Determining if the PR is Stalled or Backport..' | ||
script{ | ||
def prType = determinePRType() | ||
if(prType == 'stalled'){ | ||
echo 'Processing stalled PR..' | ||
processStalledPR() | ||
} | ||
else if(prType == 'backport'){ | ||
echo 'Processing Backport PR...' | ||
processBackportPR() | ||
} | ||
else{ | ||
echo 'PR does not match criteria. Exiting Pipeline.' | ||
} | ||
} | ||
} | ||
} | ||
} | ||
post{ | ||
success{ | ||
echo 'Pipeline completed successfully' | ||
} | ||
failure{ | ||
echo 'Pipeline failed. Check logs for details' | ||
} | ||
} | ||
} | ||
|
||
def determinePRType(token, repoURL, prId){ | ||
def result = sh(script: """ | ||
curl -s -H "Authorization: Bearer ${token}" -H "Accept: application/vnd.github.v3+json" \ | ||
${repoURL}/pulls/${prId} | jq '.labels | map(.name)' | ||
""", returnStdout: true).trim() | ||
if(result.contains('stalled')){ | ||
return 'stalled' | ||
}else if (result.contains('backport')){ | ||
return 'backport' | ||
}else{ | ||
return null | ||
} | ||
} | ||
|
||
def processStalledPR(){ | ||
echo 'Rebasing stalled PR branch onto target branch...' | ||
sh """ | ||
scripts/pr-management/StalledPRs.py --repo $REPO_REPO_URL --token $GITHUB_TOKEN | ||
""" | ||
} | ||
|
||
def processBackportPR(){ | ||
echo 'Resolving backport PR conflicts...' | ||
sh """ | ||
scripts/pr-management/BackportPRs.py --repo $REPO_URL --token $GITHUB_TOKEN | ||
""" | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import os | ||
import requests | ||
import subprocess | ||
|
||
GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN") | ||
HEADERS = {"Authorization": f"Bearer {GITHUB_TOKEN}", "Accept": "application/vnd.github.v3+json"} | ||
BASE_URL = "https://api.github.com" | ||
|
||
def fetch_backport_prs(owner, repo): | ||
"""Fetch backport PR's with the `backport` label""" | ||
url = f"{BASE_URL}/search/issues" | ||
query = f"repo:{owner}/{repo} label:backport is:pr is:open" | ||
response = requests.get(url, headers=HEADERS, params={"q":query}) | ||
response.raise_for_status() | ||
return response.json()["items"] | ||
|
||
def fetch_pr_details(owner, repo, pr_number): | ||
"""Fetch PR details to get source and target branches""" | ||
url = f"{BASE_URL}/repos/{owner}/{repo}/pulls/{pr_number}" | ||
response = requests.get(url, header=HEADERS) | ||
response.raise_for_status() | ||
return response.json()["items"] | ||
|
||
def resolve_changelog_conflict(repo_dir, pr_branch, target_branch): | ||
"""Resolve conflicts in CHANGELOG.md""" | ||
subprocess.run(["git","checkout",pr_branch], cwd=repo_dir) | ||
subprocess.run(["git","fetch","origin", target_branch], cwd=repo_dir) | ||
subprocess.run(["git","rebase",f"origin/{target_branch}"], cwd=repo_dir) | ||
|
||
conflicted_files = subprocess.check_output( | ||
["git","diff","--name-only","--diff-filter=U"], cwd=repo_dir | ||
).decode().strip().split("\n") | ||
if "CHANGELOG.md" in conflicted_files: | ||
print("Conflict detected in CHANGELOG.md. Resolving....") | ||
changelog_file = f"{repo_dir}/CHANGELOG.md" | ||
with open(changelog_file, "r") as file: | ||
lines = file.readlines() | ||
start_old = lines.index("<<<<<<< HEAD\n") | ||
middle = lines.index("=======\n") | ||
end_new = lines.index(">>>>>>> ", middle) | ||
old_changes = lines[start_old + 1: middle] | ||
new_changes = lines[middle + 1: end_new] | ||
resolved_changes = ( | ||
["# CHANGELOG\n\n"] | ||
+ ["## Existing Changes (from target branch):\n"] + old_changes | ||
+ ["\n## New Changes (from backport PR):\n"] + new_changes | ||
) | ||
with open(changelog_file, "w") as file: | ||
file.writelines(resolved_changes) | ||
subprocess.run(["git", "add", "CHANGELOG.md"], cwd=repo_dir) | ||
subprocess.run(["git","commit","-m","Resolved CHANGELOG.md conflict"], cwd=repo_dir) | ||
subprocess.run(["git","push","--force-with-lease"], cwd=repo_dir) | ||
|
||
def main_backport(owner, repo, repo_dir): | ||
"""Main function to handle backport PRs""" | ||
backport_prs = fetch_backport_prs(owner,repo) | ||
for pr in backport_prs: | ||
pr_number = pr["number"] | ||
pr_details = fetch_pr_details(owner, repo, pr_number) | ||
pr_branch = pr_details["head"]["ref"] | ||
target_branch = pr_details["base"]["ref"] | ||
print(f"Handling Backport PR #{pr_number}: {pr_branch} -> {target_branch}") | ||
resolve_changelog_conflict(repo_dir, pr_branch, target_branch) | ||
|
||
|
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# PR Management Scripts | ||
|
||
This folder contains scripts for automating tasks related to pull requests. | ||
|
||
## BackportPRs.py | ||
|
||
This script handles backport PRs by: | ||
- Detecting and resolving conflicts in `CHANGELOG.md`. | ||
- Combining old and new changes during conflict resolution. | ||
- Committing the resolved file back to the PR branch. | ||
|
||
## StalledPRs.py | ||
|
||
This script handles Stalled PRs by: | ||
- Fetching all the stalled PRs using the Stalled label | ||
- Rebase the PRs onto the latest target branch and push updates |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import os | ||
import requests | ||
import subprocess | ||
|
||
|
||
GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN") | ||
HEADERS = {"Authorization": f"Bearer {GITHUB_TOKEN}", "Accept": "application/vnd.github.v3+json"} | ||
BASE_URL = "https://api.github.com" | ||
|
||
def fetch_stalled_prs(owner, repo): | ||
"""Fetch stalled PRs with the `stalled` label""" | ||
url = f"{BASE_URL}"/search/issues | ||
query = f"repo:{owner}/{repo} label:stalled is:pr is:open" | ||
response = requests.get(url, headers=HEADERS, params={"q": query}) | ||
response.raise_for_status() | ||
return response.json()["items"] | ||
|
||
def rebase_pr(repo_dir, pr_branch, target_branch): | ||
"""Rebase a stalled PR onto the target branch.""" | ||
subprocess.run(["git","checkout",pr_branch], cwd=repo_dir) | ||
subprocess.run(["git","fetch","origin", target_branch], cwd=repo_dir) | ||
subprocess.run(["git","rebase",f"origin"/{target_branch}], cwd=repo_dir) | ||
subprocess.run(["git","push","--force-with-lease"], cwd=repo_dir) | ||
|
||
def main_stalled(owner, repo, repo_dir): | ||
"""Main function to handle stalled PRs""" | ||
stalled_prs = fetch_stalled_prs(owner,repo) | ||
for pr in stalled_prs: | ||
pr_number = pr["number"] | ||
pr_details = fetch_stalled_prs(owner, repo, pr_number) | ||
pr_branch = pr_details["head"]["ref"] | ||
target_branch = pr_details["base"]["ref"] | ||
print(f"Handling Stalled PR #{pr_number}: {pr_branch} -> {target_branch}") | ||
rebase_pr(repo_dir, pr_branch, target_branch) | ||
|
||
|