Skip to content

Commit

Permalink
Fix backport script when PR references an issue from another repo (#6824
Browse files Browse the repository at this point in the history
)

We were always looking at the referenced issue number in the timescaledb
repo, which is incorrect.
  • Loading branch information
akuzm authored Apr 12, 2024
1 parent 4420561 commit 4bd73f5
Showing 1 changed file with 33 additions and 17 deletions.
50 changes: 33 additions & 17 deletions scripts/backport.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,19 @@ def get_referenced_issue(pr_number):
"""Get the number of issue fixed by the given pull request.
Returns None if no issue is fixed, or more than one issue"""

# We only need the first issue here. We also request only the first 30 labels,
# because GitHub requires some small restriction there that is counted
# towards the GraphQL API usage quota.
ref_result = run_query(
string.Template(
"""
query {
repository(owner: "timescale", name: "timescaledb") {
pullRequest(number: $pr_number) {
closingIssuesReferences(first: 1) {
edges {
node {
number
}
nodes {
number, title,
labels (first: 30) { nodes { name } }
}
}
}
Expand All @@ -61,16 +63,26 @@ def get_referenced_issue(pr_number):
).substitute({"pr_number": pr_number})
)

# The above returns {'data': {'repository': {'pullRequest': {'closingIssuesReferences': {'edges': [{'node': {'number': 4944}}]}}}}}
# The above returns:
# {'data': {'repository': {'pullRequest': {'closingIssuesReferences': {'nodes': [{'number': 6819,
# 'title': '[Bug]: Segfault when `ts_insert_blocker` function is called',
# 'labels': {'nodes': [{'name': 'bug'}]}}]}}}}}
#
# We can have {'nodes': [None]} in case it references an inaccessble repository,
# just ignore it.

ref_edges = ref_result["data"]["repository"]["pullRequest"][
ref_nodes = ref_result["data"]["repository"]["pullRequest"][
"closingIssuesReferences"
]["edges"]
]["nodes"]

if ref_edges and len(ref_edges) == 1:
return ref_edges[0]["node"]["number"]
if not ref_nodes or len(ref_nodes) != 1 or not ref_nodes[0]:
return None, None, None

return None
number = ref_nodes[0]["number"]
title = ref_nodes[0]["title"]
labels = {x["name"] for x in ref_nodes[0]["labels"]["nodes"]}

return number, title, labels


def set_auto_merge(pr_number):
Expand Down Expand Up @@ -242,26 +254,25 @@ def __init__(self, pygithub_pr_, issue_number_):
self.issue_number = issue_number_


def should_backport_by_labels(pygithub_object):
def should_backport_by_labels(number, title, labels):
"""Should we backport the given PR/issue, judging by the labels?
Note that this works in ternary logic:
True means we must,
False means we must not (tags to disable backport take precedence),
and None means weak no (no tags to either request or disable backport)"""
labels = {label.name for label in pygithub_object.labels}
stopper_labels = labels.intersection(
["disable-auto-backport", "auto-backport-not-done"]
)
if stopper_labels:
print(
f"#{pygithub_object.number} '{pygithub_object.title}' is labeled as '{list(stopper_labels)[0]}' which prevents automated backporting."
f"#{number} '{title}' is labeled as '{list(stopper_labels)[0]}' which prevents automated backporting."
)
return False

force_labels = labels.intersection(["bug", "force-auto-backport"])
if force_labels:
print(
f"#{pygithub_object.number} '{pygithub_object.title}' is labeled as '{list(force_labels)[0]}' which requests automated backporting."
f"#{number} '{title}' is labeled as '{list(force_labels)[0]}' which requests automated backporting."
)
return True

Expand Down Expand Up @@ -308,20 +319,25 @@ def should_backport_by_labels(pygithub_object):
# labels to request backport like "bug", and labels to prevent backport
# like "disable-auto-backport", on both issue and the PR. We're going to use
# the ternary False/None/True logic to combine them properly.
issue_number = get_referenced_issue(pull.number)
issue_number, issue_title, issue_labels = get_referenced_issue(pull.number)
if not issue_number:
should_backport_issue_ternary = None
print(
f"{commit_sha[:9]} belongs to the PR #{pull.number} '{pull.title}' that does not close an issue."
)
else:
issue = source_repo.get_issue(number=issue_number)
should_backport_issue_ternary = should_backport_by_labels(issue)
should_backport_issue_ternary = should_backport_by_labels(
issue_number, issue_title, issue_labels
)
print(
f"{commit_sha[:9]} belongs to the PR #{pull.number} '{pull.title}' "
f"that references the issue #{issue.number} '{issue.title}'."
)
should_backport_pr_ternary = should_backport_by_labels(pull)
pull_labels = {label.name for label in pull.labels}
should_backport_pr_ternary = should_backport_by_labels(
pull.number, pull.title, pull_labels
)

# We backport if either the PR or the issue labels request the backport, and
# none of them prevent it. I'm writing it with `is True` because I don't
Expand Down

0 comments on commit 4bd73f5

Please sign in to comment.