Skip to content

Commit

Permalink
Automerge PR labeled with "automerge".
Browse files Browse the repository at this point in the history
Merge the PR if:
- PR has "awaiting merge" and "automerge" labels, and
- all CI status checks have passed, and
- replace # with GH-
- get the commit message from PR title and description

Closes python/bedevere#14
  • Loading branch information
Mariatta committed Sep 10, 2018
1 parent efbcc9f commit a76a1ab
Show file tree
Hide file tree
Showing 4 changed files with 433 additions and 51 deletions.
92 changes: 55 additions & 37 deletions miss_islington/status_change.py
Expand Up @@ -14,23 +14,34 @@ async def check_status(event, gh, *args, **kwargs):
"""
Check the state change
"""
sha = event.data["sha"]

if (
event.data["commit"].get("committer")
and event.data["commit"]["committer"]["login"] == "miss-islington"
):
sha = event.data["sha"]
await check_ci_status_and_approval(gh, sha, leave_comment=True)
else:
pr_for_commit = await util.get_pr_for_commit(gh, sha)
if pr_for_commit:
pr_labels = pr_for_commit["labels"]
if util.pr_is_automerge(pr_labels) and util.pr_is_awaiting_merge(pr_labels):
await check_ci_status_and_approval(gh, sha, leave_comment=True, is_automerge=True)


@router.register("pull_request", action="labeled")
async def pr_reviewed(event, gh, *args, **kwargs):
if event.data["pull_request"]["user"]["login"] == "miss-islington":
if util.pr_is_awaiting_merge(event.data["pull_request"]["labels"]):

pr_labels = event.data["pull_request"]["labels"]
if util.pr_is_automerge(pr_labels) and util.pr_is_awaiting_merge(pr_labels):
sha = event.data["pull_request"]["head"]["sha"]
await check_ci_status_and_approval(gh, sha, is_automerge=True)
elif event.data["pull_request"]["user"]["login"] == "miss-islington" and util.pr_is_awaiting_merge(event.data["pull_request"]["labels"]):
sha = event.data["pull_request"]["head"]["sha"]
await check_ci_status_and_approval(gh, sha)


async def check_ci_status_and_approval(gh, sha, leave_comment=False):
async def check_ci_status_and_approval(gh, sha, leave_comment=False, is_automerge=False):

result = await gh.getitem(f"/repos/python/cpython/commits/{sha}/status")
all_ci_status = [status["state"] for status in result["statuses"]]
Expand All @@ -40,56 +51,63 @@ async def check_ci_status_and_approval(gh, sha, leave_comment=False):
"pending" not in all_ci_status
and "continuous-integration/travis-ci/pr" in all_ci_context
):

prs_for_commit = await gh.getitem(f'/search/issues?q=type:pr+repo:python/cpython+sha:{sha}')
if prs_for_commit["total_count"] > 0: # there should only be one
pr_for_commit = prs_for_commit["items"][0]
pr_for_commit = await util.get_pr_for_commit(gh, sha)
if pr_for_commit:
pr_number = pr_for_commit["number"]
normalized_pr_title = util.normalize_title(
pr_for_commit["title"], pr_for_commit["body"]
)

title_match = TITLE_RE.match(normalized_pr_title)
if title_match:
if title_match or is_automerge:
if leave_comment:
original_pr_number = title_match.group("pr")
original_pr_url = (
f"/repos/python/cpython/pulls/{original_pr_number}"
)
original_pr_result = await gh.getitem(original_pr_url)
pr_author = original_pr_result["user"]["login"]
committer = original_pr_result["merged_by"]["login"]
if is_automerge:
participants = await util.get_participants(gh, pr_number)
else:
original_pr_number = title_match.group("pr")
participants = await util.get_participants(gh, original_pr_number)

participants = util.get_participants(pr_author, committer)
emoji = "✅" if result["state"] == "success" else "❌"

await util.leave_comment(
gh,
pr_number=pr_number,
message=f"{participants}: Backport status check is done, and it's a {result['state']} {emoji} .",
message=f"{participants}: Status check is done, and it's a {result['state']} {emoji} .",
)

if result["state"] == "success":
pr = await gh.getitem(
f"/repos/python/cpython/pulls/{pr_number}"
)
if util.pr_is_awaiting_merge(pr["labels"]):
await merge_pr(gh, pr_number, sha)

if util.pr_is_awaiting_merge(pr_for_commit["labels"]):
print("awaiting merge")
await merge_pr(gh, pr_for_commit, sha, is_automerge=is_automerge)

async def merge_pr(gh, pr_number, sha):

async def merge_pr(gh, pr, sha, is_automerge=False):
pr_number = pr["number"]
async for commit in gh.getiter(f"/repos/python/cpython/pulls/{pr_number}/commits"):
if commit["sha"] == sha:
pr_commit_msg = commit["commit"]["message"].split("\n")

cleaned_up_title = f"{pr_commit_msg[0]}"
await gh.put(
f"/repos/python/cpython/pulls/{pr_number}/merge",
data={
"commit_title": cleaned_up_title,
"commit_message": "\n".join(pr_commit_msg[1:]),
"sha": sha,
"merge_method": "squash",
},
)
if is_automerge:
pr_commit_msg = pr["body"]
pr_title = f"{pr['title']} (GH-{pr_number})"
await gh.put(
f"/repos/python/cpython/pulls/{pr_number}/merge",
data={
"commit_title": pr_title,
"commit_message": pr_commit_msg,
"sha": sha,
"merge_method": "squash",
},
)
else:
pr_commit_msg = commit["commit"]["message"].split("\n")

cleaned_up_title = f"{pr_commit_msg[0]}"
await gh.put(
f"/repos/python/cpython/pulls/{pr_number}/merge",
data={
"commit_title": cleaned_up_title,
"commit_message": "\n".join(pr_commit_msg[1:]),
"sha": sha,
"merge_method": "squash",
},
)
break
33 changes: 30 additions & 3 deletions miss_islington/util.py
Expand Up @@ -66,12 +66,23 @@ def is_cpython_repo():
return True


def get_participants(created_by, merged_by):
async def get_participants(gh, pr_number):
pr_url = (
f"/repos/python/cpython/pulls/{pr_number}"
)
pr_result = await gh.getitem(pr_url)
created_by = pr_result["user"]["login"]

merged_by = None
if pr_result["merged_by"]:
merged_by = pr_result["merged_by"]["login"]

participants = ""
if created_by == merged_by:
if created_by == merged_by or merged_by is None:
participants = f"@{created_by}"
else:
elif merged_by is not None:
participants = f"@{created_by} and @{merged_by}"

return participants


Expand Down Expand Up @@ -113,3 +124,19 @@ def pr_is_awaiting_merge(pr_labels):
if label["name"] == "awaiting merge":
return True
return False


def pr_is_automerge(pr_labels):
for label in pr_labels:
if label["name"] == "automerge":
return True
return False


async def get_pr_for_commit(gh, sha):
prs_for_commit = await gh.getitem(
f'/search/issues?q=type:pr+repo:python/cpython+sha:{sha}')
if prs_for_commit["total_count"] > 0: # there should only be one
pr_for_commit = prs_for_commit["items"][0]
return pr_for_commit
return None

0 comments on commit a76a1ab

Please sign in to comment.