This is an automated email from the ASF dual-hosted git repository.
jorisvandenbossche pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow.git
The following commit(s) were added to refs/heads/master by this push:
new 1ba97a6294 GH-14720: [Dev] Update merge_arrow_pr script to accept
GitHub issues (#14750)
1ba97a6294 is described below
commit 1ba97a62944accfd756491438dd6325ff26c9660
Author: Alessandro Molina <[email protected]>
AuthorDate: Thu Dec 1 13:57:45 2022 +0100
GH-14720: [Dev] Update merge_arrow_pr script to accept GitHub issues
(#14750)
* Closes: #14720
Lead-authored-by: Alessandro Molina <[email protected]>
Co-authored-by: Raúl Cumplido <[email protected]>
Signed-off-by: Joris Van den Bossche <[email protected]>
---
dev/merge_arrow_pr.py | 347 ++++++++++++++++++++++++++++++---------------
dev/test_merge_arrow_pr.py | 79 ++++++-----
2 files changed, 281 insertions(+), 145 deletions(-)
diff --git a/dev/merge_arrow_pr.py b/dev/merge_arrow_pr.py
index 490ee787e3..6824f6f436 100755
--- a/dev/merge_arrow_pr.py
+++ b/dev/merge_arrow_pr.py
@@ -32,8 +32,7 @@
# Configuration environment variables:
# - APACHE_JIRA_TOKEN: your Apache JIRA Personal Access Token
# - ARROW_GITHUB_API_TOKEN: a GitHub API token to use for API requests
-# - PR_REMOTE_NAME: the name of the remote to the Apache git repo (set to
-# 'apache' by default)
+# - ARROW_GITHUB_ORG: the GitHub organisation ('apache' by default)
# - DEBUG: use for testing to avoid pushing to apache (0 by default)
import configparser
@@ -58,12 +57,16 @@ except ImportError:
sys.exit(1)
# Remote name which points to the GitHub site
-PR_REMOTE_NAME = os.environ.get("PR_REMOTE_NAME", "apache")
+ORG_NAME = (
+ os.environ.get("ARROW_GITHUB_ORG") or
+ os.environ.get("PR_REMOTE_NAME") or # backward compatibility
+ "apache"
+)
+PROJECT_NAME = os.environ.get('ARROW_PROJECT_NAME') or "arrow"
# For testing to avoid accidentally pushing to apache
DEBUG = bool(int(os.environ.get("DEBUG", 0)))
-
if DEBUG:
print("**************** DEBUGGING ****************")
@@ -73,6 +76,8 @@ JIRA_API_BASE = "https://issues.apache.org/jira"
def get_json(url, headers=None):
response = requests.get(url, headers=headers)
+ if response.status_code != 200:
+ raise ValueError(response.json())
return response.json()
@@ -112,13 +117,7 @@ def fix_version_from_branch(branch, versions):
return versions[-1]
else:
branch_ver = branch.replace("branch-", "")
- return [x for x in versions if x.name.startswith(branch_ver)][-1]
-
-
-# We can merge both ARROW and PARQUET patches
-SUPPORTED_PROJECTS = ['ARROW', 'PARQUET']
-PR_TITLE_REGEXEN = [(project, re.compile(r'^(' + project + r'-[0-9]+)\b.*$'))
- for project in SUPPORTED_PROJECTS]
+ return [v for v in versions if v.startswith(branch_ver)][-1]
class JiraIssue(object):
@@ -138,46 +137,15 @@ class JiraIssue(object):
def current_fix_versions(self):
return self.issue.fields.fixVersions
- @classmethod
- def sort_versions(cls, versions):
- def version_tuple(x):
- # Parquet versions are something like cpp-1.2.0
- numeric_version = x.name.split("-", 1)[-1]
- return tuple(int(_) for _ in numeric_version.split("."))
- return sorted(versions, key=version_tuple, reverse=True)
-
- def get_candidate_fix_versions(self, merge_branches=('master',),
- maintenance_branches=()):
+ @property
+ def current_versions(self):
# Only suggest versions starting with a number, like 0.x but not JS-0.x
all_versions = self.jira_con.project_versions(self.project)
unreleased_versions = [x for x in all_versions
if not x.raw['released']]
mainline_versions = self._filter_mainline_versions(unreleased_versions)
- mainline_versions = self.sort_versions(mainline_versions)
-
- mainline_non_patch_versions = []
- for v in mainline_versions:
- (major, minor, patch) = v.name.split(".")
- if patch == "0":
- mainline_non_patch_versions.append(v)
-
- if len(mainline_versions) > len(mainline_non_patch_versions):
- # If there is a non-patch release, suggest that instead
- mainline_versions = mainline_non_patch_versions
-
- mainline_versions = self._filter_maintenance_versions(
- mainline_versions, maintenance_branches
- )
- default_fix_versions = [
- fix_version_from_branch(x, mainline_versions).name
- for x in merge_branches]
-
- return all_versions, default_fix_versions
-
- def _filter_maintenance_versions(self, versions, maintenance_branches):
- return [v for v in versions
- if f"maint-{v.name}" not in maintenance_branches]
+ return mainline_versions
def _filter_mainline_versions(self, versions):
if self.project == 'PARQUET':
@@ -187,7 +155,7 @@ class JiraIssue(object):
return [x for x in versions if mainline_regex.match(x.name)]
- def resolve(self, fix_versions, comment):
+ def resolve(self, fix_version, comment, *args):
fields = self.issue.fields
cur_status = fields.status.name
@@ -195,63 +163,170 @@ class JiraIssue(object):
self.cmd.fail("JIRA issue %s already has status '%s'"
% (self.jira_id, cur_status))
- if DEBUG:
- print("JIRA issue %s untouched" % (self.jira_id))
- return
-
resolve = [x for x in self.jira_con.transitions(self.jira_id)
if x['name'] == "Resolve Issue"][0]
# ARROW-6915: do not overwrite existing fix versions corresponding to
# point releases
- fix_versions = list(fix_versions)
+ fix_versions = [v.raw for v in self.jira_con.project_versions(
+ self.project) if v.name == fix_version]
fix_version_names = set(x['name'] for x in fix_versions)
for version in self.current_fix_versions:
major, minor, patch = version.name.split('.')
if patch != '0' and version.name not in fix_version_names:
fix_versions.append(version.raw)
- self.jira_con.transition_issue(self.jira_id, resolve["id"],
- comment=comment,
- fixVersions=fix_versions)
-
- print("Successfully resolved %s!" % (self.jira_id))
+ if DEBUG:
+ print("JIRA issue %s untouched -> %s" %
+ (self.jira_id, [v["name"] for v in fix_versions]))
+ else:
+ self.jira_con.transition_issue(self.jira_id, resolve["id"],
+ comment=comment,
+ fixVersions=fix_versions)
+ print("Successfully resolved %s!" % (self.jira_id))
self.issue = self.jira_con.issue(self.jira_id)
self.show()
def show(self):
fields = self.issue.fields
- print(format_jira_output(self.jira_id, fields.status.name,
- fields.summary, fields.assignee,
- fields.components))
+ print(format_issue_output("jira", self.jira_id, fields.status.name,
+ fields.summary, fields.assignee,
+ fields.components))
+
+
+class GitHubIssue(object):
+
+ def __init__(self, github_api, github_id, cmd):
+ self.github_api = github_api
+ self.github_id = github_id
+ self.cmd = cmd
+
+ try:
+ self.issue = self.github_api.get_issue_data(github_id)
+ except Exception as e:
+ self.cmd.fail("GitHub could not find %s\n%s" % (github_id, e))
+
+ def get_label(self, prefix):
+ prefix = f"{prefix}:"
+ return [
+ lbl["name"][len(prefix):].strip()
+ for lbl in self.issue["labels"] if lbl["name"].startswith(prefix)
+ ]
+
+ @property
+ def components(self):
+ return self.get_label("Component")
+
+ @property
+ def assignees(self):
+ return [a["login"] for a in self.issue["assignees"]]
+
+ @property
+ def current_fix_versions(self):
+ return self.issue.get("milestone", {}).get("title")
+ @property
+ def current_versions(self):
+ all_versions = self.github_api.get_milestones()
+
+ unreleased_versions = [x for x in all_versions if x["state"] == "open"]
+ unreleased_versions = [x["title"] for x in unreleased_versions]
+
+ return unreleased_versions
+
+ def resolve(self, fix_version, comment, pr_body):
+ cur_status = self.issue["state"]
+
+ if cur_status == "closed":
+ self.cmd.fail("GitHub issue %s already has status '%s'"
+ % (self.github_id, cur_status))
+
+ if DEBUG:
+ print("GitHub issue %s untouched -> %s" %
+ (self.github_id, fix_version))
+ else:
+ self.github_api.assign_milestone(self.github_id, fix_version)
+ if f"Closes: #{self.github_id}" not in pr_body:
+ self.github_api.close_issue(self.github_id, comment)
+ print("Successfully resolved %s!" % (self.github_id))
-def format_jira_output(jira_id, status, summary, assignee, components):
- if assignee is None:
+ self.issue = self.github_api.get_issue_data(self.github_id)
+ self.show()
+
+ def show(self):
+ issue = self.issue
+ print(format_issue_output("github", self.github_id, issue["state"],
+ issue["title"], ', '.join(self.assignees),
+ self.components))
+
+
+def get_candidate_fix_version(mainline_versions,
+ merge_branches=('master',),
+ maintenance_branches=()):
+ all_versions = [getattr(v, "name", v) for v in mainline_versions]
+
+ def version_tuple(x):
+ # Parquet versions are something like cpp-1.2.0
+ numeric_version = getattr(x, "name", x).split("-", 1)[-1]
+ return tuple(int(_) for _ in numeric_version.split("."))
+ all_versions = sorted(all_versions, key=version_tuple, reverse=True)
+
+ # Only suggest versions starting with a number, like 0.x but not JS-0.x
+ mainline_versions = all_versions
+ mainline_non_patch_versions = []
+ for v in mainline_versions:
+ (major, minor, patch) = v.split(".")
+ if patch == "0":
+ mainline_non_patch_versions.append(v)
+
+ if len(mainline_versions) > len(mainline_non_patch_versions):
+ # If there is a non-patch release, suggest that instead
+ mainline_versions = mainline_non_patch_versions
+
+ mainline_versions = [v for v in mainline_versions
+ if f"maint-{v}" not in maintenance_branches]
+ default_fix_versions = [
+ fix_version_from_branch(x, mainline_versions)
+ for x in merge_branches]
+
+ return default_fix_versions[0]
+
+
+def format_issue_output(issue_type, issue_id, status,
+ summary, assignee, components):
+ if not assignee:
assignee = "NOT ASSIGNED!!!"
else:
- assignee = assignee.displayName
+ assignee = getattr(assignee, "displayName", assignee)
if len(components) == 0:
components = 'NO COMPONENTS!!!'
else:
- components = ', '.join((x.name for x in components))
+ components = ', '.join((getattr(x, "name", x) for x in components))
+
+ if issue_type == "jira":
+ url = '/'.join((JIRA_API_BASE, 'browse', issue_id))
+ else:
+ url = (
+ f'https://github.com/{ORG_NAME}/{PROJECT_NAME}/issues/{issue_id}'
+ )
- return """=== JIRA {} ===
+ return """=== {} {} ===
Summary\t\t{}
Assignee\t{}
Components\t{}
Status\t\t{}
-URL\t\t{}/{}""".format(jira_id, summary, assignee, components, status,
- '/'.join((JIRA_API_BASE, 'browse')), jira_id)
+URL\t\t{}""".format(issue_type.upper(), issue_id, summary, assignee,
+ components, status, url)
class GitHubAPI(object):
def __init__(self, project_name, cmd):
- self.github_api = ("https://api.github.com/repos/apache/{0}"
- .format(project_name))
+ self.github_api = (
+ f"https://api.github.com/repos/{ORG_NAME}/{project_name}"
+ )
token = None
config = load_configuration()
@@ -269,6 +344,19 @@ class GitHubAPI(object):
}
self.headers = headers
+ def get_milestones(self):
+ return get_json("%s/milestones" % (self.github_api, ),
+ headers=self.headers)
+
+ def get_milestone_number(self, version):
+ return next((
+ m["number"] for m in self.get_milestones() if m["title"] == version
+ ), None)
+
+ def get_issue_data(self, number):
+ return get_json("%s/issues/%s" % (self.github_api, number),
+ headers=self.headers)
+
def get_pr_data(self, number):
return get_json("%s/pulls/%s" % (self.github_api, number),
headers=self.headers)
@@ -281,6 +369,36 @@ class GitHubAPI(object):
return get_json("%s/branches" % (self.github_api),
headers=self.headers)
+ def close_issue(self, number, comment):
+ issue_url = f'{self.github_api}/issues/{number}'
+ comment_url = f'{self.github_api}/issues/{number}/comments'
+
+ r = requests.post(comment_url, json={
+ "body": comment}, headers=self.headers)
+ if not r.ok:
+ raise ValueError(
+ f"Failed request: {comment_url}:{r.status_code} -> {r.json()}")
+
+ r = requests.patch(
+ issue_url, json={"state": "closed"}, headers=self.headers)
+ if not r.ok:
+ raise ValueError(
+ f"Failed request: {issue_url}:{r.status_code} -> {r.json()}")
+
+ def assign_milestone(self, number, version):
+ url = f'{self.github_api}/issues/{number}'
+ milestone_number = self.get_milestone_number(version)
+ if not milestone_number:
+ raise ValueError(f"Invalid version {version}, milestone not found")
+ payload = {
+ 'milestone': milestone_number
+ }
+ r = requests.patch(url, headers=self.headers, json=payload)
+ if not r.ok:
+ raise ValueError(
+ f"Failed request: {url}:{r.status_code} -> {r.json()}")
+ return r.json()
+
def merge_pr(self, number, commit_title, commit_message):
url = f'{self.github_api}/pulls/{number}/merge'
payload = {
@@ -322,6 +440,13 @@ class CommandInput(object):
class PullRequest(object):
+ GITHUB_PR_TITLE_PATTERN = re.compile(r'^GH-([0-9]+)\b.*$')
+ # We can merge both ARROW and PARQUET patches
+ JIRA_SUPPORTED_PROJECTS = ['ARROW', 'PARQUET']
+ JIRA_PR_TITLE_REGEXEN = [
+ (project, re.compile(r'^(' + project + r'-[0-9]+)\b.*$'))
+ for project in JIRA_SUPPORTED_PROJECTS
+ ]
def __init__(self, cmd, github_api, git_remote, jira_con, number):
self.cmd = cmd
@@ -342,14 +467,14 @@ class PullRequest(object):
raise
self.description = "%s/%s" % (self.user_login, self.base_ref)
- self.jira_issue = self._get_jira()
+ self.issue = self._get_issue()
def show(self):
print("\n=== Pull Request #%s ===" % self.number)
print("title\t%s\nsource\t%s\ntarget\t%s\nurl\t%s"
% (self.title, self.description, self.target_ref, self.url))
- if self.jira_issue is not None:
- self.jira_issue.show()
+ if self.issue is not None:
+ self.issue.show()
else:
print("Minor PR. Please ensure it meets guidelines for minor.\n")
@@ -366,24 +491,28 @@ class PullRequest(object):
return [x["name"] for x in self._github_api.get_branches()
if x["name"].startswith("maint-")]
- def _get_jira(self):
+ def _get_issue(self):
if self.title.startswith("MINOR:"):
return None
- jira_id = None
- for project, regex in PR_TITLE_REGEXEN:
+ m = self.GITHUB_PR_TITLE_PATTERN.search(self.title)
+ if m:
+ github_id = m.group(1)
+ return GitHubIssue(self._github_api, github_id, self.cmd)
+
+ for project, regex in self.JIRA_PR_TITLE_REGEXEN:
m = regex.search(self.title)
if m:
jira_id = m.group(1)
- break
+ return JiraIssue(self.con, jira_id, project, self.cmd)
- if jira_id is None:
- options = ' or '.join('{0}-XXX'.format(project)
- for project in SUPPORTED_PROJECTS)
- self.cmd.fail("PR title should be prefixed by a jira id "
- "{0}, but found {1}".format(options, self.title))
-
- return JiraIssue(self.con, jira_id, project, self.cmd)
+ options = ' or '.join(
+ '{0}-XXX'.format(project)
+ for project in self.JIRA_SUPPORTED_PROJECTS + ["GH"]
+ )
+ self.cmd.fail("PR title should be prefixed by a GitHub ID or a "
+ "Jira ID, like: {0}, but found {1}".format(
+ options, self.title))
def merge(self):
"""
@@ -487,25 +616,18 @@ def get_primary_author(cmd, distinct_authors):
return primary_author, distinct_other_authors
-def prompt_for_fix_version(cmd, jira_issue, maintenance_branches=()):
- (all_versions,
- default_fix_versions) = jira_issue.get_candidate_fix_versions(
+def prompt_for_fix_version(cmd, issue, maintenance_branches=()):
+ default_fix_version = get_candidate_fix_version(
+ issue.current_versions,
maintenance_branches=maintenance_branches
)
- default_fix_versions = ",".join(default_fix_versions)
-
- issue_fix_versions = cmd.prompt("Enter comma-separated "
- "fix version(s) [%s]: "
- % default_fix_versions)
- if issue_fix_versions == "":
- issue_fix_versions = default_fix_versions
- issue_fix_versions = issue_fix_versions.replace(" ", "").split(",")
-
- def get_version_json(version_str):
- return [x for x in all_versions if x.name == version_str][0].raw
-
- return [get_version_json(v) for v in issue_fix_versions]
+ issue_fix_version = cmd.prompt("Enter fix version [%s]: "
+ % default_fix_version)
+ if issue_fix_version == "":
+ issue_fix_version = default_fix_version
+ issue_fix_version = issue_fix_version.strip()
+ return issue_fix_version
CONFIG_FILE = "~/.config/arrow/merge.conf"
@@ -552,9 +674,9 @@ def get_pr_num():
def cli():
# Location of your Arrow git clone
ARROW_HOME = os.path.abspath(os.path.dirname(__file__))
- PROJECT_NAME = os.environ.get('ARROW_PROJECT_NAME') or 'arrow'
- print("ARROW_HOME = " + ARROW_HOME)
- print("PROJECT_NAME = " + PROJECT_NAME)
+ print(f"ARROW_HOME = {ARROW_HOME}")
+ print(f"ORG_NAME = {ORG_NAME}")
+ print(f"PROJECT_NAME = {PROJECT_NAME}")
cmd = CommandInput()
@@ -565,7 +687,7 @@ def cli():
github_api = GitHubAPI(PROJECT_NAME, cmd)
jira_con = connect_jira(cmd)
- pr = PullRequest(cmd, github_api, PR_REMOTE_NAME, jira_con, pr_num)
+ pr = PullRequest(cmd, github_api, ORG_NAME, jira_con, pr_num)
if pr.is_merged:
print("Pull request %s has already been merged" % pr_num)
@@ -581,20 +703,19 @@ def cli():
pr.merge()
- if pr.jira_issue is None:
- print("Minor PR. No JIRA issue to update.\n")
+ if pr.issue is None:
+ print("Minor PR. No issue to update.\n")
return
- cmd.continue_maybe("Would you like to update the associated JIRA?")
- jira_comment = (
- "Issue resolved by pull request %s\n[%s/%s]"
+ cmd.continue_maybe("Would you like to update the associated issue?")
+ issue_comment = (
+ "Issue resolved by pull request %s\n%s"
% (pr_num,
- "https://github.com/apache/" + PROJECT_NAME + "/pull",
- pr_num))
-
- fix_versions_json = prompt_for_fix_version(cmd, pr.jira_issue,
- pr.maintenance_branches)
- pr.jira_issue.resolve(fix_versions_json, jira_comment)
+ f"https://github.com/{ORG_NAME}/{PROJECT_NAME}/pull/{pr_num}")
+ )
+ fix_version = prompt_for_fix_version(cmd, pr.issue,
+ pr.maintenance_branches)
+ pr.issue.resolve(fix_version, issue_comment, pr.body)
if __name__ == '__main__':
diff --git a/dev/test_merge_arrow_pr.py b/dev/test_merge_arrow_pr.py
index 2367c50b6b..39576876d5 100755
--- a/dev/test_merge_arrow_pr.py
+++ b/dev/test_merge_arrow_pr.py
@@ -77,8 +77,12 @@ class FakeJIRA:
'fixVersions': fixVersions
}
- def get_candidate_fix_versions(self, maintenance_branches):
- return SOURCE_VERSIONS, ['0.11.0']
+ @property
+ def current_versions(self):
+ all_versions = self._project_versions or SOURCE_VERSIONS
+ return [
+ v for v in all_versions if not v.raw.get("released")
+ ] + ['0.11.0']
def project_versions(self, project):
return self._project_versions
@@ -104,9 +108,10 @@ def test_jira_fix_versions():
transitions=TRANSITIONS)
issue = merge_arrow_pr.JiraIssue(jira, 'ARROW-1234', 'ARROW', FakeCLI())
- all_versions, default_versions = issue.get_candidate_fix_versions()
- assert all_versions == SOURCE_VERSIONS
- assert default_versions == ['0.9.0']
+ fix_version = merge_arrow_pr.get_candidate_fix_version(
+ issue.current_versions
+ )
+ assert fix_version == '0.9.0'
def test_jira_fix_versions_filters_maintenance():
@@ -115,11 +120,11 @@ def test_jira_fix_versions_filters_maintenance():
transitions=TRANSITIONS)
issue = merge_arrow_pr.JiraIssue(jira, 'ARROW-1234', 'ARROW', FakeCLI())
- all_versions, default_versions = issue.get_candidate_fix_versions(
+ fix_version = merge_arrow_pr.get_candidate_fix_version(
+ issue.current_versions,
maintenance_branches=maintenance_branches
)
- assert all_versions == SOURCE_VERSIONS
- assert default_versions == ['0.10.0']
+ assert fix_version == '0.10.0'
def test_jira_no_suggest_patch_release():
@@ -132,9 +137,10 @@ def test_jira_no_suggest_patch_release():
jira = FakeJIRA(project_versions=versions, transitions=TRANSITIONS)
issue = merge_arrow_pr.JiraIssue(jira, 'ARROW-1234', 'ARROW', FakeCLI())
- all_versions, default_versions = issue.get_candidate_fix_versions()
- assert all_versions == versions
- assert default_versions == ['0.10.0']
+ fix_version = merge_arrow_pr.get_candidate_fix_version(
+ issue.current_versions
+ )
+ assert fix_version == '0.10.0'
def test_jira_parquet_no_suggest_non_cpp():
@@ -153,9 +159,10 @@ def test_jira_parquet_no_suggest_non_cpp():
jira = FakeJIRA(project_versions=versions, transitions=TRANSITIONS)
issue = merge_arrow_pr.JiraIssue(jira, 'PARQUET-1713', 'PARQUET',
FakeCLI())
- all_versions, default_versions = issue.get_candidate_fix_versions()
- assert all_versions == versions
- assert default_versions == ['cpp-1.6.0']
+ fix_version = merge_arrow_pr.get_candidate_fix_version(
+ issue.current_versions
+ )
+ assert fix_version == 'cpp-1.6.0'
def test_jira_invalid_issue():
@@ -174,16 +181,16 @@ def test_jira_resolve():
transitions=TRANSITIONS)
my_comment = 'my comment'
- fix_versions = [SOURCE_VERSIONS[1].raw]
+ fix_version = "0.10.0"
issue = merge_arrow_pr.JiraIssue(jira, 'ARROW-1234', 'ARROW', FakeCLI())
- issue.resolve(fix_versions, my_comment)
+ issue.resolve(fix_version, my_comment)
assert jira.captured_transition == {
'jira_id': 'ARROW-1234',
'transition_id': 1,
'comment': my_comment,
- 'fixVersions': fix_versions
+ 'fixVersions': [{'name': '0.10.0', 'released': False}]
}
@@ -193,16 +200,16 @@ def test_jira_resolve_non_mainline():
transitions=TRANSITIONS)
my_comment = 'my comment'
- fix_versions = [SOURCE_VERSIONS[0].raw]
+ fix_version = "JS-0.4.0"
issue = merge_arrow_pr.JiraIssue(jira, 'ARROW-1234', 'ARROW', FakeCLI())
- issue.resolve(fix_versions, my_comment)
+ issue.resolve(fix_version, my_comment)
assert jira.captured_transition == {
'jira_id': 'ARROW-1234',
'transition_id': 1,
'comment': my_comment,
- 'fixVersions': fix_versions
+ 'fixVersions': [{'name': 'JS-0.4.0', 'released': False}]
}
@@ -214,7 +221,7 @@ def test_jira_resolve_released_fix_version():
cmd = FakeCLI(responses=['0.7.0'])
fix_versions_json = merge_arrow_pr.prompt_for_fix_version(cmd, jira)
- assert fix_versions_json == [RAW_VERSION_JSON[-1]]
+ assert fix_versions_json == "0.7.0"
def test_multiple_authors_bad_input():
@@ -279,11 +286,16 @@ def test_no_unset_point_release_fix_version():
for v in ['0.17.0', '0.15.1', '0.14.2']])
issue = FakeIssue(fields)
- jira = FakeJIRA(issue=issue, project_versions=SOURCE_VERSIONS,
- transitions=TRANSITIONS)
+ jira = FakeJIRA(
+ issue=issue,
+ project_versions=[
+ FakeVersion(v, vdata) for v, vdata in versions_json.items()
+ ],
+ transitions=TRANSITIONS
+ )
issue = merge_arrow_pr.JiraIssue(jira, 'ARROW-1234', 'ARROW', FakeCLI())
- issue.resolve([versions_json['0.16.0']], "a comment")
+ issue.resolve('0.16.0', "a comment")
assert jira.captured_transition == {
'jira_id': 'ARROW-1234',
@@ -307,9 +319,10 @@ def test_jira_output_no_components():
# ARROW-5472
status = 'Interesting work'
components = []
- output = merge_arrow_pr.format_jira_output(
- 'ARROW-1234', 'Resolved', status, FakeAssignee('Foo Bar'),
- components)
+ output = merge_arrow_pr.format_issue_output(
+ "jira", 'ARROW-1234', 'Resolved', status,
+ FakeAssignee('Foo Bar'), components
+ )
assert output == """=== JIRA ARROW-1234 ===
Summary\t\tInteresting work
@@ -318,9 +331,10 @@ Components\tNO COMPONENTS!!!
Status\t\tResolved
URL\t\thttps://issues.apache.org/jira/browse/ARROW-1234"""
- output = merge_arrow_pr.format_jira_output(
- 'ARROW-1234', 'Resolved', status, FakeAssignee('Foo Bar'),
- [FakeComponent('C++'), FakeComponent('Python')])
+ output = merge_arrow_pr.format_issue_output(
+ "jira", 'ARROW-1234', 'Resolved', status, FakeAssignee('Foo Bar'),
+ [FakeComponent('C++'), FakeComponent('Python')]
+ )
assert output == """=== JIRA ARROW-1234 ===
Summary\t\tInteresting work
@@ -332,9 +346,10 @@ URL\t\thttps://issues.apache.org/jira/browse/ARROW-1234"""
def test_sorting_versions():
versions_json = [
+ {'name': '11.0.0', 'released': False},
{'name': '9.0.0', 'released': False},
{'name': '10.0.0', 'released': False},
]
versions = [FakeVersion(raw['name'], raw) for raw in versions_json]
- ordered_versions = merge_arrow_pr.JiraIssue.sort_versions(versions)
- assert ordered_versions[0].name == "10.0.0"
+ fix_version = merge_arrow_pr.get_candidate_fix_version(versions)
+ assert fix_version == "9.0.0"