Skip to content

Commit 4eb672c

Browse files
authored
chore: add a script to create an empty translation PR (#13634)
1 parent 20ef8a4 commit 4eb672c

File tree

1 file changed

+266
-0
lines changed

1 file changed

+266
-0
lines changed
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
# This script can create an empty PR in your target repo based on your specified source language PR.
2+
# Before running this script:
3+
# 1. Get a GitHub personal access token (https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) and save it in a text file.
4+
# 2. Ensure that you have forked your target repo because the changes in the new PR will be committed to your forked repository first.
5+
6+
import requests
7+
import base64
8+
import re
9+
10+
source_pr_url = "https://github.com/pingcap/docs-cn/pull/13895" # update this URL to your PR link
11+
my_github_id = "qiancai" # update this ID to your GitHub ID
12+
my_github_token_file_path = r"/Users/grcai/Documents/PingCAP/Python_scripts/GitHub/gh_token5.txt"
13+
14+
# Read the GitHub personal access token from your local file.
15+
with open(my_github_token_file_path, "r") as f:
16+
access_token = f.read().strip()
17+
18+
# For getting the information about the source language PR
19+
def get_pr_info(pr_url):
20+
url_parts = pr_url.split("/")
21+
source_repo_owner = url_parts[3]
22+
source_repo_name = url_parts[4]
23+
pr_number = url_parts[6]
24+
25+
url = f"https://api.github.com/repos/{source_repo_owner}/{source_repo_name}/pulls/{pr_number}"
26+
headers = {"Accept": "application/vnd.github.v3+json"}
27+
28+
response = requests.get(url, headers=headers)
29+
try:
30+
response.raise_for_status()
31+
pr_data = response.json()
32+
33+
source_title = pr_data["title"]
34+
source_description = pr_data["body"]
35+
exclude_labels = ["size", "translation", "status", "first-time-contributor", "contribution"]
36+
source_labels = [label["name"] for label in pr_data.get("labels", []) if not any(exclude_label in label["name"] for exclude_label in exclude_labels)]
37+
38+
base_repo = pr_data["base"]["repo"]["full_name"]
39+
base_branch = pr_data["base"]["ref"]
40+
head_repo = pr_data["head"]["repo"]["full_name"]
41+
head_branch = pr_data["head"]["ref"]
42+
43+
print(f"Getting source language PR information was successful. The head branch name is: {head_branch}")
44+
45+
return source_title, source_description, source_labels, base_repo, base_branch, head_repo, head_branch, pr_number
46+
except requests.exceptions.HTTPError as e:
47+
print(f"Failed to get source language PR information: {response.text}")
48+
raise e
49+
50+
# For syncing the corresponding branch of my forked repository to the latest upstream
51+
def sync_my_repo_branch(target_repo_owner, target_repo_name,my_repo_owner, my_repo_name, base_branch):
52+
# Get the branch reference SHA of the upstream repository.
53+
api_url = f"https://api.github.com/repos/{target_repo_owner}/{target_repo_name}/git/refs/heads/{base_branch}"
54+
headers = {"Authorization": f"Bearer {access_token}"}
55+
56+
response = requests.get(api_url, headers=headers)
57+
58+
if response.status_code == 200:
59+
upstream_sha = response.json().get("object", {}).get("sha")
60+
else:
61+
print("Failed to get the upstream repository branch reference.")
62+
exit()
63+
64+
# Update the branch reference of your fork repository
65+
api_url = f"https://api.github.com/repos/{my_repo_owner}/{my_repo_name}/git/refs/heads/{base_branch}"
66+
data = {
67+
"sha": upstream_sha,
68+
"force": True
69+
}
70+
71+
print("Syncing the latest content from the upstream branch...")
72+
73+
response = requests.patch(api_url, headers=headers, json=data)
74+
75+
if response.status_code == 200:
76+
print("The content sync is successful!")
77+
else:
78+
print("Failed to sync the latest content from the upstream branch.")
79+
80+
81+
# For creating a new branch in my forked repository
82+
def create_branch(repo_owner, repo_name, branch_name, base_branch, access_token):
83+
api_url = f"https://api.github.com/repos/{repo_owner}/{repo_name}/git/refs"
84+
headers = {"Authorization": f"Bearer {access_token}"}
85+
86+
# Get a reference to the base branch
87+
base_branch_url = f"heads/{base_branch}"
88+
response = requests.get(f"{api_url}/{base_branch_url}", headers=headers)
89+
90+
if response.status_code == 200:
91+
base_branch_ref = response.json().get("object", {}).get("sha")
92+
93+
if base_branch_ref:
94+
# Create a reference to a new branch
95+
branch_ref = f"refs/heads/{branch_name}"
96+
data = {
97+
"ref": branch_ref,
98+
"sha": base_branch_ref
99+
}
100+
101+
response = requests.post(api_url, headers=headers, json=data)
102+
103+
if response.status_code == 201:
104+
branch_url = f"https://github.com/{repo_owner}/{repo_name}/tree/{branch_name}"
105+
print(f"A new branch is created successfully. The branch address is: {branch_url}")
106+
return branch_url
107+
else:
108+
print(f"Failed to create the branch: {response.text}")
109+
raise requests.exceptions.HTTPError(response.text)
110+
else:
111+
print("Base branch reference not found.")
112+
raise ValueError("Base branch reference not found.")
113+
else:
114+
print(f"Failed to get base branch reference: {response.text}")
115+
raise requests.exceptions.HTTPError(response.text)
116+
117+
# For adding a temporary temp.md file to the new branch
118+
def create_file_in_branch(repo_owner, repo_name, branch_name, access_token, file_path, file_content, commit_message):
119+
url = f"https://api.github.com/repos/{repo_owner}/{repo_name}/contents/{file_path}"
120+
headers = {
121+
"Authorization": f"Bearer {access_token}",
122+
"Accept": "application/vnd.github.v3+json"
123+
}
124+
data = {
125+
"message": commit_message,
126+
"branch": branch_name,
127+
"content": base64.b64encode(file_content.encode()).decode()
128+
}
129+
response = requests.put(url, headers=headers, json=data)
130+
response.raise_for_status()
131+
print("A temp file is created successfully!")
132+
133+
# For creating a PR in the target repository and adding labels to the target PR
134+
def create_pull_request(target_repo_owner, target_repo_name, base_branch, my_repo_owner, my_repo_name, new_branch_name, access_token, title, body, labels):
135+
url = f"https://api.github.com/repos/{target_repo_owner}/{target_repo_name}/pulls"
136+
headers = {
137+
"Accept": "application/vnd.github.v3+json",
138+
"Authorization": f"Bearer {access_token}"
139+
}
140+
data = {
141+
"title": title,
142+
"body": body,
143+
"head": f"{my_repo_owner}:{new_branch_name}",
144+
"base": base_branch
145+
}
146+
response = requests.post(url, headers=headers, json=data)
147+
try:
148+
response.raise_for_status()
149+
pr_data = response.json()
150+
pr_url = pr_data["html_url"]
151+
print(f"Your target PR is created successfully. The PR address is: {pr_url}")
152+
url_parts = pr_url.split("/")
153+
pr_number = url_parts[6]
154+
# Add labels to the created PR
155+
label_url = f"https://api.github.com/repos/{target_repo_owner}/{target_repo_name}/issues/{pr_number}/labels"
156+
payload = labels # which is an array containing the labels to be added
157+
response_labels = requests.post(label_url, headers=headers, json=payload)
158+
if response_labels.status_code == 200:
159+
print("Labels are added successfully.")
160+
else:
161+
print("Failed to add labels.")
162+
return pr_url
163+
164+
except requests.exceptions.HTTPError as e:
165+
print(f"Fails to create the target PR: {response.text}")
166+
raise e
167+
168+
# For changing the description of the translation PR
169+
def update_pr_description(source_description):
170+
171+
source_pr_CLA = "https://cla-assistant.io/pingcap/" + base_repo
172+
new_pr_CLA = "https://cla-assistant.io/pingcap/" + target_repo_name
173+
new_pr_description = source_description.replace(source_pr_CLA, new_pr_CLA)
174+
175+
new_pr_description = new_pr_description.replace("This PR is translated from:", "This PR is translated from: " + source_pr_url)
176+
177+
if "tips for choosing the affected versions" in source_description:
178+
new_pr_description = re.sub(r'.*?\[tips for choosing the affected version.*?\n\n?',"",new_pr_description)
179+
180+
return new_pr_description
181+
182+
# For deleting temp.md
183+
def delete_file_in_branch(repo_owner, repo_name, branch_name, access_token, file_path, commit_message):
184+
url = f"https://api.github.com/repos/{repo_owner}/{repo_name}/contents/{file_path}"
185+
headers = {
186+
"Authorization": f"Bearer {access_token}",
187+
"Accept": "application/vnd.github.v3+json"
188+
}
189+
params = {
190+
"ref": branch_name
191+
}
192+
193+
# Get information about the file
194+
response = requests.get(url, headers=headers, params=params)
195+
196+
if response.status_code == 200:
197+
198+
file_info = response.json()
199+
200+
# Prepare the request data to delete the file
201+
data = {
202+
"message": commit_message,
203+
"sha": file_info["sha"],
204+
"branch": branch_name
205+
}
206+
207+
# Send a request to delete a file
208+
try:
209+
response = requests.delete(url, headers=headers, params=params, json=data)
210+
response.raise_for_status()
211+
print("The temp.md is deleted successfully!")
212+
except requests.exceptions.HTTPError as e:
213+
print(f"Failed to delete temp.md. Error message: {response.text}")
214+
raise e
215+
elif response.status_code == 404:
216+
print(f"The temp.md file does not exist in branch {branch_name}.")
217+
else:
218+
print(f"Failed to get file information. Error message: {response.text}")
219+
220+
# Get the information of the source language PR
221+
source_title, source_description, source_labels, base_repo, base_branch, head_repo, head_branch, source_pr_number = get_pr_info(source_pr_url)
222+
223+
# Get the repo info of my foked repository and the target repository, and the translation label
224+
my_repo_owner = my_github_id
225+
target_repo_owner = "pingcap"
226+
if "pingcap/docs-cn/pull" in source_pr_url:
227+
my_repo_name = "docs"
228+
target_repo_name = "docs"
229+
translation_label = "translation/from-docs-cn"
230+
elif "pingcap/docs/pull" in source_pr_url:
231+
target_repo_name = "docs-cn"
232+
my_repo_name = "docs-cn"
233+
translation_label = "translation/from-docs"
234+
else:
235+
print ("Error: The provided URL is not a pull request of pingcap/docs-cn or pingcap/docs.")
236+
print("Exiting the program...")
237+
exit(1)
238+
239+
source_labels.append(translation_label)
240+
#print ("The following labels will be reused for the translation PR.")
241+
#print (source_labels)
242+
243+
# Sync from upstream
244+
sync_my_repo_branch(target_repo_owner, target_repo_name,my_repo_owner, my_repo_name, base_branch)
245+
246+
# Create a new branch in the repository that I forked
247+
new_branch_name = head_branch + "-" + source_pr_number
248+
## print (my_repo_owner, my_repo_name, new_branch_name, base_branch )
249+
250+
create_branch(my_repo_owner, my_repo_name, new_branch_name, base_branch, access_token)
251+
252+
# Create a temporary temp.md file in the new branch
253+
file_path = "temp.md"
254+
file_content = "This is a test file."
255+
commit_message = "Add temp.md"
256+
create_file_in_branch(my_repo_owner, my_repo_name, new_branch_name, access_token, file_path, file_content, commit_message)
257+
258+
# Create the target PR
259+
title = source_title
260+
body = update_pr_description(source_description)
261+
labels = source_labels
262+
create_pull_request(target_repo_owner, target_repo_name, base_branch, my_repo_owner, my_repo_name, new_branch_name, access_token, title, body, labels)
263+
264+
# Delete the temporary temp.md file
265+
commit_message2 = "Delete temp.md"
266+
delete_file_in_branch(my_repo_owner, my_repo_name, new_branch_name, access_token, file_path, commit_message2)

0 commit comments

Comments
 (0)