Skip to content

Commit 18cf8c7

Browse files
authored
Enable cherry-pick with commit ID's with a milestoned issue (#1752)
It will let the users cherry-pick one or more commits into a release branch. And then it will create a PR.
1 parent 7192ed0 commit 18cf8c7

File tree

7 files changed

+254
-142
lines changed

7 files changed

+254
-142
lines changed

actions/cherry-picker/action.yml

-42
This file was deleted.

actions/cherry_picker/action.yml

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
name: "Cherry-picker when comment is created or issue/pr is closed"
2+
description: "Cherry-picks the commit"
3+
inputs:
4+
triggered-on:
5+
required: true
6+
default: ${{ github.triggered-on }}
7+
pr-number:
8+
required: false
9+
default: ${{ github.pr-number }}
10+
milestone-title:
11+
required: false
12+
default: ${{ github.milestone-title }}
13+
milestoned-issue-number:
14+
required: false
15+
default: ${{ github.milestoned-issue-number }}
16+
is-prod:
17+
required: true
18+
default: ${{ github.is-prod }}
19+
issue-body:
20+
required: false
21+
default: ${{ github.issue-body }}
22+
issue-title:
23+
required: false
24+
default: ${{ github.issue-title }}
25+
runs:
26+
using: "composite"
27+
steps:
28+
- name: Install Python
29+
uses: actions/setup-python@v4
30+
with:
31+
python-version: "3.10"
32+
- name: Install Dependencies
33+
run: |
34+
pip install -r ${{ github.action_path }}/requirements.txt
35+
shell: bash
36+
- name: Pass Inputs to Shell
37+
run: |
38+
echo "INPUT_TRIGGERED_ON=${{ inputs.triggered-on }}" >> $GITHUB_ENV
39+
echo "INPUT_PR_NUMBER=${{ inputs.pr-number }}" >> $GITHUB_ENV
40+
echo "INPUT_MILESTONE_TITLE=${{ inputs.milestone-title }}" >> $GITHUB_ENV
41+
echo "INPUT_MILESTONED_ISSUE_NUMBER=${{ inputs.milestoned-issue-number }}" >> $GITHUB_ENV
42+
echo "INPUT_IS_PROD=${{ inputs.is-prod }}" >> $GITHUB_ENV
43+
44+
echo "INPUT_ISSUE_BODY<<EOF" >> $GITHUB_ENV
45+
echo "${{ inputs.issue-body }}" >> $GITHUB_ENV
46+
echo "EOF" >> $GITHUB_ENV
47+
48+
echo "INPUT_ISSUE_TITLE=${{ inputs.issue-title }}" >> $GITHUB_ENV
49+
shell: bash
50+
- if: ${{ inputs.triggered-on == 'commented' || inputs.triggered-on == 'closed' }}
51+
name: Run python cherrypick_with_milestones.py
52+
run: |
53+
chmod +x ${{ github.action_path }}/cherrypick_with_milestones.py
54+
python -u ${{ github.action_path }}/cherrypick_with_milestones.py
55+
shell: bash
56+
- if: ${{ inputs.triggered-on == 'ondemand' }}
57+
name: Run python cherrypick_with_commits.py
58+
run: |
59+
chmod +x ${{ github.action_path }}/cherrypick_with_commits.py
60+
python -u ${{ github.action_path }}/cherrypick_with_commits.py
61+
shell: bash
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import os, re
2+
from vars import input_data, upstream_repo, cherrypick_with_commits_infos
3+
from functions import cherry_pick, create_pr, issue_comment, get_pr_body, PushCpException, push_to_branch, get_middle_text
4+
5+
milestone_title = os.environ["INPUT_MILESTONE_TITLE"]
6+
milestoned_issue_number = os.environ["INPUT_MILESTONED_ISSUE_NUMBER"]
7+
issue_title = os.environ['INPUT_ISSUE_TITLE']
8+
issue_body = os.environ["INPUT_ISSUE_BODY"]
9+
commits_text = cherrypick_with_commits_infos["commits"]
10+
team_labels_text = cherrypick_with_commits_infos["team_labels"]
11+
reviewers_text = cherrypick_with_commits_infos["reviewers"]
12+
issue_body_dict = {}
13+
issue_body_dict["commits"] = get_middle_text(issue_body, commits_text["left"], commits_text["right"]).replace(" ", "").split(",")
14+
15+
for commit_index in range(len(issue_body_dict["commits"])):
16+
issue_body_dict["commits"][commit_index] = re.sub(r'https://.*/commit/', "", issue_body_dict["commits"][commit_index])
17+
18+
issue_body_dict["labels"] = get_middle_text(issue_body, team_labels_text["left"], team_labels_text["right"]).replace(" ", "").replace("@", "").split(",")
19+
issue_body_dict["reviewers"] = get_middle_text(issue_body, reviewers_text["left"], reviewers_text["right"]).replace(" ", "").replace("@", "").split(",")
20+
21+
release_number = milestone_title.split(" release blockers")[0]
22+
release_branch_name = f"{input_data['release_branch_name_initials']}{release_number}"
23+
target_branch_name = f"cp_ondemand_{milestoned_issue_number}-{release_number}"
24+
head_branch_name = f"{input_data['user_name']}:{target_branch_name}"
25+
reviewers = issue_body_dict["reviewers"]
26+
labels = issue_body_dict["labels"]
27+
requires_clone = True
28+
requires_checkout = True
29+
successful_commits = []
30+
failed_commits = []
31+
32+
for idx, commit_id in enumerate(issue_body_dict["commits"]):
33+
try:
34+
cherry_pick(commit_id, release_branch_name, target_branch_name, requires_clone, requires_checkout, input_data)
35+
msg_body = get_pr_body(commit_id, input_data["api_repo_name"])
36+
success_msg = {"commit_id": commit_id, "msg": msg_body}
37+
successful_commits.append(success_msg)
38+
except PushCpException as pe:
39+
issue_comment(milestoned_issue_number, str(pe), input_data["api_repo_name"], input_data["is_prod"])
40+
raise SystemExit(0)
41+
except Exception as e:
42+
failed_commits.append(commit_id)
43+
requires_clone = False
44+
requires_checkout = False
45+
46+
try:
47+
push_to_branch(target_branch_name)
48+
except Exception as e:
49+
issue_comment(milestoned_issue_number, str(e), input_data["api_repo_name"], input_data["is_prod"])
50+
raise SystemExit(0)
51+
52+
issue_comment_body = ""
53+
if len(successful_commits):
54+
if len(successful_commits) >= 2:
55+
pr_body = f"This PR contains {len(successful_commits)} commit(s).\n\n"
56+
for idx, commit in enumerate(successful_commits):
57+
pr_body += str((idx + 1)) + ")" + commit["msg"] + "\n\n"
58+
elif len(successful_commits) == 1:
59+
pr_body = f"{successful_commits[0]['msg']}"
60+
if "awaiting-review" not in labels: labels.append("awaiting-review")
61+
cherry_picked_pr_number = create_pr(reviewers, release_number, labels, issue_title, pr_body, release_branch_name, target_branch_name, input_data['user_name'])
62+
issue_comment_body = f"The following commits were cherry-picked in https://github.com/{upstream_repo}/pull/{cherry_picked_pr_number}: "
63+
for success_commit in successful_commits:
64+
issue_comment_body += f"https://github.com/{input_data['api_repo_name']}/commit/{success_commit['commit_id']}, "
65+
issue_comment_body = issue_comment_body[::-1].replace(" ,", ".", 1)[::-1]
66+
67+
if len(failed_commits):
68+
failure_commits_str = f"\nFailed commits (likely due to merge conflicts): "
69+
for fail_commit in failed_commits:
70+
failure_commits_str += f"https://github.com/{input_data['api_repo_name']}/commit/{fail_commit}, "
71+
failure_commits_str = failure_commits_str[::-1].replace(" ,", "", 1)[::-1]
72+
failure_commits_str += "\n\nThe failed commits are NOT included in the PR. Please resolve manually.\ncc: @bazelbuild/triage"
73+
issue_comment_body += failure_commits_str
74+
elif len(failed_commits):
75+
issue_comment_body = "Failed commmits (likely due to merge conflicts): "
76+
for fail_commit in failed_commits:
77+
issue_comment_body += f"https://github.com/{input_data['api_repo_name']}/commit/{fail_commit}, "
78+
issue_comment_body = issue_comment_body[::-1].replace(" ,", ".", 1)[::-1] + "\nPlease resolve manually.\ncc: @bazelbuild/triage"
79+
issue_comment(milestoned_issue_number, issue_comment_body, input_data["api_repo_name"], input_data["is_prod"])
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,13 @@
11
import os, requests
2-
from functions import get_commit_id, get_reviewers, extract_release_numbers_data, cherry_pick, create_pr, get_labels, get_pr_title_body, issue_comment
2+
from functions import get_commit_id, get_reviewers, extract_release_numbers_data, cherry_pick, create_pr, get_labels, get_pr_body, issue_comment, push_to_branch
3+
from vars import headers, upstream_repo, input_data
34

45
triggered_on = os.environ["INPUT_TRIGGERED_ON"]
56
pr_number = os.environ["INPUT_PR_NUMBER"] if triggered_on == "closed" else os.environ["INPUT_PR_NUMBER"].split("#")[1]
67
milestone_title = os.environ["INPUT_MILESTONE_TITLE"]
78
milestoned_issue_number = os.environ["INPUT_MILESTONED_ISSUE_NUMBER"]
8-
is_prod = os.environ["INPUT_IS_PROD"]
99

10-
if is_prod == "true":
11-
input_data = {
12-
"is_prod": True,
13-
"api_repo_name": "bazelbuild/bazel",
14-
"master_branch": "master",
15-
"release_branch_name_initials": "release-",
16-
"user_name": "bazel-io",
17-
"action_event": "closed",
18-
"actor_name": {
19-
"copybara-service[bot]"
20-
},
21-
"email": "[email protected]"
22-
}
23-
24-
else:
25-
input_data = {
26-
"is_prod": False,
27-
"api_repo_name": "iancha1992/bazel",
28-
"master_branch": "release_test",
29-
"release_branch_name_initials": "fake-release-",
30-
"user_name": "iancha1992",
31-
"action_event": "merged",
32-
"actor_name": {
33-
"iancha1992",
34-
"Pavank1992",
35-
"chaheein123",
36-
},
37-
"email": "[email protected]"
38-
}
39-
40-
issue_data = requests.get(f"https://api.github.com/repos/{input_data['api_repo_name']}/issues/{pr_number}", headers={'X-GitHub-Api-Version': '2022-11-28'}).json()
10+
issue_data = requests.get(f"https://api.github.com/repos/{input_data['api_repo_name']}/issues/{pr_number}", headers=headers).json()
4111

4212
# Check if the PR is closed.
4313
if issue_data["state"] != "closed":
@@ -60,18 +30,21 @@
6030
labels = get_labels(pr_number, input_data["api_repo_name"])
6131

6232
# Retrieve issue/PR's title and body
63-
pr_title_body = get_pr_title_body(commit_id, input_data["api_repo_name"], issue_data)
33+
pr_body = get_pr_body(commit_id, input_data["api_repo_name"])
6434

6535
# Perform cherry-pick and then create a pr if it's successful.
66-
is_first_time = True
36+
requires_clone = True
6737
for k in release_numbers_data.keys():
6838
release_number = k
6939
release_branch_name = f"{input_data['release_branch_name_initials']}{release_number}"
7040
target_branch_name = f"cp{pr_number}-{release_number}"
7141
issue_number = release_numbers_data[k]
42+
pr_title = issue_data["title"]
7243
try:
73-
cherry_pick(commit_id, release_branch_name, target_branch_name, issue_number, is_first_time, input_data)
74-
create_pr(reviewers, release_number, issue_number, labels, pr_title_body, release_branch_name, target_branch_name, input_data["user_name"], input_data["api_repo_name"], input_data["is_prod"])
44+
cherry_pick(commit_id, release_branch_name, target_branch_name, requires_clone, True, input_data)
45+
push_to_branch(target_branch_name)
46+
cherry_picked_pr_number = create_pr(reviewers, release_number, labels, pr_title, pr_body, release_branch_name, target_branch_name, input_data["user_name"])
47+
issue_comment(issue_number, f"Cherry-picked in https://github.com/{upstream_repo}/pull/{cherry_picked_pr_number}", input_data["api_repo_name"], input_data["is_prod"])
7548
except Exception as e:
7649
issue_comment(issue_number, str(e), input_data["api_repo_name"], input_data["is_prod"])
77-
is_first_time = False
50+
requires_clone = False

0 commit comments

Comments
 (0)