diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index beb126eaf9496..82d7da0a8d3ef 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -45,8 +45,8 @@ issues][3] for the Apache Arrow project. Comment on the issue and/or contact [dev@arrow.apache.org](https://lists.apache.org/list.html?dev@arrow.apache.org) with your questions and ideas. -If you’d like to report a bug but don’t have time to fix it, you can still post -it on JIRA, or email the mailing list +If you’d like to report a bug but don’t have time to fix it, you can still create +a GitHub issue, or email the mailing list [dev@arrow.apache.org](https://lists.apache.org/list.html?dev@arrow.apache.org) To contribute a patch: @@ -57,8 +57,8 @@ harder to merge in a large change with a lot of disjoint features. GitHub](https://github.com/apache/arrow/issues). 3. Submit the patch as a GitHub pull request against the main branch. For a tutorial, see the GitHub guides on [forking a repo](https://help.github.com/en/articles/fork-a-repo) -and [sending a pull request](https://help.github.com/en/articles/creating-a-pull-request-from-a-fork). So that your pull request syncs with the JIRA issue, prefix your pull request -name with the JIRA issue id (ex: [ARROW-767: [C++] Filesystem abstraction](https://github.com/apache/arrow/pull/4225)) +and [sending a pull request](https://help.github.com/en/articles/creating-a-pull-request-from-a-fork). Prefix your pull request +name with the GitHub issue id (ex: [GH-767: [C++] Filesystem abstraction](https://github.com/apache/arrow/pull/4225)) 4. Make sure that your code passes the unit tests. You can find instructions how to run the unit tests for each Arrow component in its respective README file. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index ddcdb5ebfd9d9..3839d3e2fc889 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -19,10 +19,6 @@ or MINOR: [${COMPONENT}] ${SUMMARY} -In the case of PARQUET issues on JIRA the title also supports: - - PARQUET-${JIRA_ISSUE_ID}: [${COMPONENT}] ${SUMMARY} - --> ### Rationale for this change diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index e4d3cae96a1e5..52eae68c4f498 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -124,7 +124,7 @@ jobs: shell: bash run: | gem install test-unit - pip install "cython>=0.29.31" setuptools pytest jira setuptools-scm + pip install "cython>=0.29.31" setuptools pytest requests setuptools-scm - name: Run Release Test env: ARROW_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/dev_pr.yml b/.github/workflows/dev_pr.yml index 4e37432afaad5..96bf3993f8242 100644 --- a/.github/workflows/dev_pr.yml +++ b/.github/workflows/dev_pr.yml @@ -49,7 +49,7 @@ jobs: ref: main persist-credentials: false - - name: Comment JIRA link + - name: Add Issue link if: | (github.event.action == 'opened' || github.event.action == 'edited') diff --git a/.github/workflows/dev_pr/helpers.js b/.github/workflows/dev_pr/helpers.js index e4da5050d5dd5..1a93b428e0a95 100644 --- a/.github/workflows/dev_pr/helpers.js +++ b/.github/workflows/dev_pr/helpers.js @@ -24,10 +24,8 @@ const https = require('https'); * @returns {Issue} or null if no issue detected. * * @typedef {Object} Issue - * @property {string} kind - The kind of issue: minor, jira or github - * @property {string} id - The id of the issue: - * PARQUET-XXXX for jira - * The numeric issue id for github + * @property {string} kind - The kind of issue: minor or github + * @property {string} id - The numeric issue id of the issue */ function detectIssue(title) { if (!title) { @@ -36,10 +34,6 @@ function detectIssue(title) { if (title.startsWith("MINOR: ")) { return {"kind": "minor"}; } - const matched_jira = /^(WIP:?\s*)?((PARQUET)-\d+)/.exec(title); - if (matched_jira) { - return {"kind": "jira", "id": matched_jira[2]}; - } const matched_gh = /^(WIP:?\s*)?GH-(\d+)/.exec(title); if (matched_gh) { return {"kind": "github", "id": matched_gh[2]}; @@ -47,27 +41,6 @@ function detectIssue(title) { return null; } -/** - * Retrieves information about a JIRA issue. - * @param {String} jiraID - * @returns {Object} the information about a JIRA issue. - */ -async function getJiraInfo(jiraID) { - const jiraURL = `https://issues.apache.org/jira/rest/api/2/issue/${jiraID}`; - - return new Promise((resolve) => { - https.get(jiraURL, res => { - let data = ''; - - res.on('data', chunk => { data += chunk }) - - res.on('end', () => { - resolve(JSON.parse(data)); - }) - }) - }); -} - /** * Retrieves information about a GitHub issue. * @param {String} issueID diff --git a/.github/workflows/dev_pr/issue_check.js b/.github/workflows/dev_pr/issue_check.js index fb5d986dff2f7..f778f70c0f192 100644 --- a/.github/workflows/dev_pr/issue_check.js +++ b/.github/workflows/dev_pr/issue_check.js @@ -17,91 +17,6 @@ const helpers = require("./helpers.js"); -/** - * Performs checks on the JIRA Issue: - * - The issue is started in JIRA. - * - The issue contains components. - * - * @param {Object} github - * @param {Object} context - * @param {String} pullRequestNumber - * @param {String} jiraID - */ -async function verifyJIRAIssue(github, context, pullRequestNumber, jiraID) { - const ticketInfo = await helpers.getJiraInfo(jiraID); - if(!ticketInfo["fields"]["components"].length) { - await commentMissingComponents(github, context, pullRequestNumber); - } - - if(ticketInfo["fields"]["status"]["id"] == 1) { - // "status": {"name":"Open","id":"1" - // "description":"The issue is open and ready for the assignee to start work on it.", - await commentNotStartedTicket(github, context, pullRequestNumber); - } -} - -/** - * Adds a comment to add components on the JIRA ticket. - * - * @param {Object} github - * @param {Object} context - * @param {String} pullRequestNumber - */ -async function commentMissingComponents(github, context, pullRequestNumber) { - const {data: comments} = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pullRequestNumber, - per_page: 100 - }); - - var found = false; - for(var i=0; i { const title = context.payload.pull_request.title; const issue = helpers.detectIssue(title) if (issue){ - if (issue.kind == "jira") { - await verifyJIRAIssue(github, context, pullRequestNumber, issue.id); - } else if(issue.kind == "github") { - await verifyGitHubIssue(github, context, pullRequestNumber, issue.id); - } + await verifyGitHubIssue(github, context, pullRequestNumber, issue.id); } }; diff --git a/.github/workflows/dev_pr/link.js b/.github/workflows/dev_pr/link.js index a70dbc604c377..f7877f41dd5a3 100644 --- a/.github/workflows/dev_pr/link.js +++ b/.github/workflows/dev_pr/link.js @@ -47,30 +47,6 @@ async function haveComment(github, context, pullRequestNumber, message) { return false; } -/** - * Adds a comment on the Pull Request linking the JIRA issue. - * - * @param {Object} github - * @param {Object} context - * @param {String} pullRequestNumber - * @param {String} jiraID - */ -async function commentJIRAURL(github, context, pullRequestNumber, jiraID) { - const issueInfo = await helpers.getJiraInfo(jiraID); - const jiraURL = `https://issues.apache.org/jira/browse/${jiraID}`; - if (await haveComment(github, context, pullRequestNumber, jiraURL)) { - return; - } - if (issueInfo){ - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pullRequestNumber, - body: jiraURL - }); - } -} - /** * Adds a comment on the Pull Request linking the GitHub issue. * @@ -102,10 +78,6 @@ module.exports = async ({github, context}) => { const title = context.payload.pull_request.title; const issue = helpers.detectIssue(title); if (issue){ - if (issue.kind == "jira") { - await commentJIRAURL(github, context, pullRequestNumber, issue.id); - } else if (issue.kind == "github") { - await commentGitHubURL(github, context, pullRequestNumber, issue.id); - } + await commentGitHubURL(github, context, pullRequestNumber, issue.id); } }; diff --git a/.github/workflows/dev_pr/title_check.md b/.github/workflows/dev_pr/title_check.md index c810d7477947c..8de10a2962e92 100644 --- a/.github/workflows/dev_pr/title_check.md +++ b/.github/workflows/dev_pr/title_check.md @@ -31,10 +31,6 @@ or MINOR: [${COMPONENT}] ${SUMMARY} -In the case of PARQUET issues on JIRA the title also supports: - - PARQUET-${JIRA_ISSUE_ID}: [${COMPONENT}] ${SUMMARY} - See also: * [Other pull requests](https://github.com/apache/arrow/pulls/) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 03ce556add2bd..dc7d7a2244a60 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,8 +29,7 @@ To be assigned to an issue, add a comment "take" to that issue. Before you create a new bug entry, we recommend you first search among existing Arrow issues in -[Jira](https://issues.apache.org/jira/issues/?jql=project%20%3D%20ARROW%20AND%20status%20%3D%20Open) -or [GitHub](https://github.com/apache/arrow/issues). +[GitHub](https://github.com/apache/arrow/issues). We conventionally prefix the issue title with the component name in brackets, such as "[C++][Python] Ensure no validity bitmap in diff --git a/dev/README.md b/dev/README.md index d35dd231bbc42..c813aa6417833 100644 --- a/dev/README.md +++ b/dev/README.md @@ -51,8 +51,7 @@ you'll have to install Python dependencies yourself and then run The merge script requires tokens for access control. There are two options for configuring your tokens: environment variables or a configuration file. -> Note: Arrow only requires a GitHub token. Parquet can use GitHub or -JIRA tokens. +> Note: Arrow and Parquet only requires a GitHub token. #### Pass tokens via Environment Variables @@ -61,12 +60,6 @@ The merge script uses the GitHub REST API. You must set a [Personal Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token). You need to add `workflow` scope to the Personal Access Token. -You can specify the -[Personal Access Token](https://confluence.atlassian.com/enterprise/using-personal-access-tokens-1026032365.html) -of your JIRA account in the -`APACHE_JIRA_TOKEN` environment variable. -If the variable is not set, the script will ask you for it. - #### Pass tokens via configuration file ``` diff --git a/dev/merge.conf.sample b/dev/merge.conf.sample index c50ef85d70f3e..db436f31eac48 100644 --- a/dev/merge.conf.sample +++ b/dev/merge.conf.sample @@ -18,10 +18,6 @@ # Configuration for the merge_arrow_pr.py tool # Install a copy of this file at ~/.config/arrow/merge.conf -[jira] -# issues.apache.org Jira personal access token -token=abc123 - [github] # GitHub's personal access token. "workflow" scope is needed. api_token=ghp_ABC diff --git a/dev/merge_arrow_pr.py b/dev/merge_arrow_pr.py index 6694d2373b8f1..fe1dc1e79290e 100755 --- a/dev/merge_arrow_pr.py +++ b/dev/merge_arrow_pr.py @@ -30,7 +30,6 @@ # variables. # # 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 # - ARROW_GITHUB_ORG: the GitHub organisation ('apache' by default) # - DEBUG: use for testing to avoid pushing to apache (0 by default) @@ -44,15 +43,6 @@ import requests import getpass -try: - import jira.client - import jira.exceptions -except ImportError: - print("Could not find jira library. " - "Run 'pip install jira' to install.") - print("Exiting without trying to close the associated JIRA.") - sys.exit(1) - # Remote name which points to the GitHub site ORG_NAME = ( os.environ.get("ARROW_GITHUB_ORG") or @@ -68,9 +58,6 @@ print("**************** DEBUGGING ****************") -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: @@ -135,92 +122,6 @@ def fix_version_from_branch(versions): ) -class JiraIssue(object): - - def __init__(self, jira_con, jira_id, project, cmd): - self.jira_con = jira_con - self.jira_id = jira_id - self.project = project - self.cmd = cmd - - try: - self.issue = jira_con.issue(jira_id) - except Exception as e: - self.cmd.fail("ASF JIRA could not find %s\n%s" % (jira_id, e)) - - @property - def current_fix_versions(self): - return self.issue.fields.fixVersions - - @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) - return mainline_versions - - def _filter_mainline_versions(self, versions): - if self.project == 'PARQUET': - mainline_regex = re.compile(r'cpp-\d.*') - else: - mainline_regex = re.compile(r'\d.*') - - return [x for x in versions if mainline_regex.match(x.name)] - - def resolve(self, fix_version, comment, *args): - fields = self.issue.fields - cur_status = fields.status.name - - if cur_status == "Resolved" or cur_status == "Closed": - self.cmd.fail("JIRA issue %s already has status '%s'" - % (self.jira_id, cur_status)) - - 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 = [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) - - 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_issue_output("jira", self.jira_id, fields.status.name, - fields.summary, fields.assignee, - fields.components)) - - def github_issue_id(self): - try: - last_jira_comment = self.issue.fields.comment.comments[-1].body - except Exception: - # If no comment found or other issues ignore - return None - matches = MIGRATION_COMMENT_REGEX.search(last_jira_comment) - if matches: - values = matches.groupdict() - return "GH-" + values['issue_id'] - - class GitHubIssue(object): def __init__(self, github_api, github_id, cmd): @@ -328,12 +229,11 @@ def format_issue_output(issue_type, issue_id, status, else: 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}' - ) + url_id = issue_id + if "GH" in issue_id: + url_id = issue_id.replace("GH-", "") + + url = f'https://github.com/{ORG_NAME}/{PROJECT_NAME}/issues/{url_id}' return """=== {} {} === Summary\t\t{} @@ -476,19 +376,11 @@ def continue_maybe(self, prompt): class PullRequest(object): GITHUB_PR_TITLE_PATTERN = re.compile(r'^GH-([0-9]+)\b.*$') - # We can merge PARQUET patches from JIRA or GH prefixed issues - JIRA_SUPPORTED_PROJECTS = ['PARQUET'] - JIRA_PR_TITLE_REGEXEN = [ - (project, re.compile(r'^(' + project + r'-[0-9]+)\b.*$')) - for project in JIRA_SUPPORTED_PROJECTS - ] - JIRA_UNSUPPORTED_ARROW = re.compile(r'^(ARROW-[0-9]+)\b.*$') - - def __init__(self, cmd, github_api, git_remote, jira_con, number): + + def __init__(self, cmd, github_api, git_remote, number): self.cmd = cmd self._github_api = github_api self.git_remote = git_remote - self.con = jira_con self.number = number self._pr_data = github_api.get_pr_data(number) try: @@ -536,28 +428,8 @@ def _get_issue(self): github_id = m.group(1) return GitHubIssue(self._github_api, github_id, self.cmd) - m = self.JIRA_UNSUPPORTED_ARROW.search(self.title) - if m: - old_jira_id = m.group(1) - jira_issue = JiraIssue(self.con, old_jira_id, 'ARROW', self.cmd) - self.cmd.fail("PR titles with ARROW- prefixed tickets on JIRA " - "are unsupported, update the PR title from " - f"{old_jira_id}. Possible GitHub id could be: " - f"{jira_issue.github_issue_id()}") - - for project, regex in self.JIRA_PR_TITLE_REGEXEN: - m = regex.search(self.title) - if m: - jira_id = m.group(1) - 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)) + self.cmd.fail("PR title should be prefixed by a GitHub ID, like: " + "GH-XXX, but found {0}".format(self.title)) def merge(self): """ @@ -706,31 +578,6 @@ def load_configuration(): return config -def get_credentials(cmd): - token = None - - config = load_configuration() - if "jira" in config.sections(): - token = config["jira"].get("token") - - # Fallback to environment variables - if not token: - token = os.environ.get("APACHE_JIRA_TOKEN") - - # Fallback to user tty prompt - if not token: - token = cmd.prompt("Env APACHE_JIRA_TOKEN not set, " - "please enter your Jira API token " - "(Jira personal access token):") - - return token - - -def connect_jira(cmd): - return jira.client.JIRA(options={'server': JIRA_API_BASE}, - token_auth=get_credentials(cmd)) - - def get_pr_num(): if len(sys.argv) == 2: return sys.argv[1] @@ -752,9 +599,7 @@ def cli(): os.chdir(ARROW_HOME) github_api = GitHubAPI(PROJECT_NAME, cmd) - - jira_con = connect_jira(cmd) - pr = PullRequest(cmd, github_api, ORG_NAME, jira_con, pr_num) + pr = PullRequest(cmd, github_api, ORG_NAME, pr_num) if pr.is_merged: print("Pull request %s has already been merged" % pr_num) diff --git a/dev/requirements_merge_arrow_pr.txt b/dev/requirements_merge_arrow_pr.txt index 99909e434a580..f2293605cf1b0 100644 --- a/dev/requirements_merge_arrow_pr.txt +++ b/dev/requirements_merge_arrow_pr.txt @@ -1,2 +1 @@ -jira requests diff --git a/dev/test_merge_arrow_pr.py b/dev/test_merge_arrow_pr.py index 0067c10414c65..1db07ca91a401 100755 --- a/dev/test_merge_arrow_pr.py +++ b/dev/test_merge_arrow_pr.py @@ -25,99 +25,77 @@ FakeIssue = namedtuple('issue', ['fields']) -FakeFields = namedtuple('fields', ['status', 'summary', 'assignee', - 'components', 'fixVersions', 'milestone']) -FakeAssignee = namedtuple('assignee', ['displayName']) -FakeStatus = namedtuple('status', ['name']) -FakeComponent = namedtuple('component', ['name']) -FakeVersion = namedtuple('version', ['name', 'raw']) -FakeMilestone = namedtuple('milestone', ['state']) +FakeFields = namedtuple( + 'fields', ['assignees', 'labels', 'milestone', 'state', 'title']) +FakeAssignee = namedtuple('assignees', ['login']) +FakeLabel = namedtuple('label', ['name']) +FakeMilestone = namedtuple('milestone', ['title', 'state']) RAW_VERSION_JSON = [ - {'name': 'JS-0.4.0', 'released': False}, - {'name': '1.0.0', 'released': False}, - {'name': '2.0.0', 'released': False}, - {'name': '0.9.0', 'released': False}, - {'name': '0.10.0', 'released': False}, - {'name': '0.8.0', 'released': True}, - {'name': '0.7.0', 'released': True} + {'title': 'JS-0.4.0', 'state': 'open'}, + {'title': '1.0.0', 'state': 'open'}, + {'title': '2.0.0', 'state': 'open'}, + {'title': '0.9.0', 'state': 'open'}, + {'title': '0.10.0', 'state': 'open'}, + {'title': '0.8.0', 'state': 'closed'}, + {'title': '0.7.0', 'state': 'closed'} ] - -SOURCE_VERSIONS = [FakeVersion(raw['name'], raw) +SOURCE_VERSIONS = [FakeMilestone(raw['title'], raw['state']) for raw in RAW_VERSION_JSON] - -TRANSITIONS = [{'name': 'Resolve Issue', 'id': 1}] - -jira_id = 'ARROW-1234' -status = FakeStatus('In Progress') -fields = FakeFields(status, 'issue summary', FakeAssignee('groundhog'), - [FakeComponent('C++'), FakeComponent('Format')], - [], FakeMilestone('closed')._asdict()) +fake_issue_id = 'GH-1234' +fields = FakeFields([FakeAssignee('groundhog')._asdict()], + [FakeLabel('Component: C++')._asdict(), + FakeLabel('Component: Format')._asdict()], + FakeMilestone('', 'open')._asdict(), + 'open', '[C++][Format] The issue Title') FAKE_ISSUE_1 = FakeIssue(fields) -class FakeJIRA: +class FakeGitHub: - def __init__(self, issue=None, project_versions=None, transitions=None, - current_fix_versions=None): - self._issue = issue + def __init__(self, issues=None, project_versions=None, state='open'): + self._issues = issues self._project_versions = project_versions - self._transitions = transitions - - def issue(self, jira_id): - return self._issue + self._state = state + self._transitions = [] - def transitions(self, jira_id): - return self._transitions - - def transition_issue(self, jira_id, transition_id, comment=None, - fixVersions=None): - self.captured_transition = { - 'jira_id': jira_id, - 'transition_id': transition_id, - 'comment': comment, - 'fixVersions': fixVersions - } + @property + def issue(self): + return self._issues[fake_issue_id].fields._asdict() @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'] + v.title for v in self._project_versions if not v.state == 'closed' + ] @property def current_fix_versions(self): return 'JS-0.4.0' - def project_versions(self, project): - return self._project_versions - + @property + def state(self): + return self._state -class FakeGitHub: + def get_issue_data(self, issue_id): + return self._issues.get(issue_id).fields._asdict() - def __init__(self, issue=None, project_versions=None): - self._issue = issue - self._project_versions = project_versions + def get_milestones(self): + return [v._asdict() for v in self._project_versions] - @property - def issue(self): - return self._issue.fields._asdict() + def assign_milestone(self, issue_id, milestone): + self._transitions.append( + {'action': 'assign_milestone', 'issue_id': issue_id, + 'milestone': milestone}) - @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 close_issue(self, issue_id, comment): + self._transitions.append( + {'action': 'close_issue', 'issue_id': issue_id, 'comment': comment}) @property - def current_fix_versions(self): - return 'JS-0.4.0' - - def project_versions(self, project): - return self._project_versions + def captured_transitions(self): + return self._transitions class FakeCLI: @@ -135,23 +113,23 @@ def fail(self, msg): raise Exception(msg) -def test_jira_fix_versions(): - jira = FakeJIRA(project_versions=SOURCE_VERSIONS, - transitions=TRANSITIONS) +def test_gh_fix_versions(): + gh = FakeGitHub(issues={fake_issue_id: FAKE_ISSUE_1}, + project_versions=SOURCE_VERSIONS) - issue = merge_arrow_pr.JiraIssue(jira, 'ARROW-1234', 'ARROW', FakeCLI()) + issue = merge_arrow_pr.GitHubIssue(gh, fake_issue_id, FakeCLI()) fix_version = merge_arrow_pr.get_candidate_fix_version( issue.current_versions ) assert fix_version == '1.0.0' -def test_jira_fix_versions_filters_maintenance(): +def test_gh_fix_versions_filters_maintenance(): maintenance_branches = ["maint-1.0.0"] - jira = FakeJIRA(project_versions=SOURCE_VERSIONS, - transitions=TRANSITIONS) + gh = FakeGitHub(issues={fake_issue_id: FAKE_ISSUE_1}, + project_versions=SOURCE_VERSIONS) - issue = merge_arrow_pr.JiraIssue(jira, 'ARROW-1234', 'ARROW', FakeCLI()) + issue = merge_arrow_pr.GitHubIssue(gh, fake_issue_id, FakeCLI()) fix_version = merge_arrow_pr.get_candidate_fix_version( issue.current_versions, maintenance_branches=maintenance_branches @@ -159,102 +137,72 @@ def test_jira_fix_versions_filters_maintenance(): assert fix_version == '2.0.0' -def test_jira_only_suggest_major_release(): +def test_gh_only_suggest_major_release(): versions_json = [ - {'name': '0.9.1', 'released': False}, - {'name': '0.10.0', 'released': False}, - {'name': '1.0.0', 'released': False}, + {'name': '0.9.1', 'state': "open"}, + {'name': '0.10.0', 'state': "open"}, + {'name': '1.0.0', 'state': "open"}, ] - versions = [FakeVersion(raw['name'], raw) for raw in versions_json] + versions = [FakeMilestone(raw['name'], raw['state']) for raw in versions_json] - jira = FakeJIRA(project_versions=versions, transitions=TRANSITIONS) - issue = merge_arrow_pr.JiraIssue(jira, 'ARROW-1234', 'ARROW', FakeCLI()) + gh = FakeGitHub(issues={fake_issue_id: FAKE_ISSUE_1}, project_versions=versions) + issue = merge_arrow_pr.GitHubIssue(gh, fake_issue_id, FakeCLI()) fix_version = merge_arrow_pr.get_candidate_fix_version( issue.current_versions ) assert fix_version == '1.0.0' -def test_jira_parquet_no_suggest_non_cpp(): - # ARROW-7351 - versions_json = [ - {'name': 'cpp-1.5.0', 'released': True}, - {'name': 'cpp-1.6.0', 'released': False}, - {'name': 'cpp-1.7.0', 'released': False}, - {'name': 'cpp-2.0.0', 'released': False}, - {'name': '1.11.0', 'released': False}, - {'name': '1.12.0', 'released': False}, - {'name': '2.0.0', 'released': False} - ] - - versions = [FakeVersion(raw['name'], raw) - for raw in versions_json] - - jira = FakeJIRA(project_versions=versions, transitions=TRANSITIONS) - issue = merge_arrow_pr.JiraIssue(jira, 'PARQUET-1713', 'PARQUET', - FakeCLI()) - fix_version = merge_arrow_pr.get_candidate_fix_version( - issue.current_versions - ) - assert fix_version == 'cpp-2.0.0' - - -def test_jira_invalid_issue(): +def test_gh_invalid_issue(): class Mock: - def issue(self, jira_id): + def issue(self, gh_id): raise Exception("not found") with pytest.raises(Exception): - merge_arrow_pr.JiraIssue(Mock(), 'ARROW-1234', 'ARROW', FakeCLI()) + merge_arrow_pr.GitHubIssue(Mock(), fake_issue_id, FakeCLI()) -def test_jira_resolve(): - jira = FakeJIRA(issue=FAKE_ISSUE_1, - project_versions=SOURCE_VERSIONS, - transitions=TRANSITIONS) +def test_gh_resolve(): + gh = FakeGitHub(issues={fake_issue_id: FAKE_ISSUE_1}, + project_versions=SOURCE_VERSIONS) my_comment = 'my comment' fix_version = "0.10.0" - issue = merge_arrow_pr.JiraIssue(jira, 'ARROW-1234', 'ARROW', FakeCLI()) - issue.resolve(fix_version, my_comment) + issue = merge_arrow_pr.GitHubIssue(gh, fake_issue_id, FakeCLI()) + issue.resolve(fix_version, my_comment, pr_body="") - assert jira.captured_transition == { - 'jira_id': 'ARROW-1234', - 'transition_id': 1, - 'comment': my_comment, - 'fixVersions': [{'name': '0.10.0', 'released': False}] - } + assert len(gh.captured_transitions) == 2 + assert gh.captured_transitions[0]['action'] == 'assign_milestone' + assert gh.captured_transitions[1]['action'] == 'close_issue' + assert gh.captured_transitions[1]['comment'] == my_comment + assert gh.captured_transitions[0]['milestone'] == fix_version -def test_jira_resolve_non_mainline(): - jira = FakeJIRA(issue=FAKE_ISSUE_1, - project_versions=SOURCE_VERSIONS, - transitions=TRANSITIONS) +def test_gh_resolve_non_mainline(): + gh = FakeGitHub(issues={fake_issue_id: FAKE_ISSUE_1}, + project_versions=SOURCE_VERSIONS) my_comment = 'my comment' fix_version = "JS-0.4.0" - issue = merge_arrow_pr.JiraIssue(jira, 'ARROW-1234', 'ARROW', FakeCLI()) - issue.resolve(fix_version, my_comment) + issue = merge_arrow_pr.GitHubIssue(gh, fake_issue_id, FakeCLI()) + issue.resolve(fix_version, my_comment, "") - assert jira.captured_transition == { - 'jira_id': 'ARROW-1234', - 'transition_id': 1, - 'comment': my_comment, - 'fixVersions': [{'name': 'JS-0.4.0', 'released': False}] - } + assert len(gh.captured_transitions) == 2 + assert gh.captured_transitions[1]['comment'] == my_comment + assert gh.captured_transitions[0]['milestone'] == fix_version -def test_jira_resolve_released_fix_version(): +def test_gh_resolve_released_fix_version(): # ARROW-5083 - jira = FakeGitHub(issue=FAKE_ISSUE_1, - project_versions=SOURCE_VERSIONS) + gh = FakeGitHub(issues={fake_issue_id: FAKE_ISSUE_1}, + project_versions=SOURCE_VERSIONS) cmd = FakeCLI(responses=['1.0.0']) - fix_versions_json = merge_arrow_pr.prompt_for_fix_version(cmd, jira) + fix_versions_json = merge_arrow_pr.prompt_for_fix_version(cmd, gh) assert fix_versions_json == "1.0.0" @@ -283,107 +231,58 @@ def test_multiple_authors_bad_input(): assert distinct_other_authors == [a0, a1] -def test_jira_already_resolved(): - status = FakeStatus('Resolved') - fields = FakeFields(status, 'issue summary', FakeAssignee('groundhog'), - [FakeComponent('Java')], [], None) +def test_gh_already_resolved(): + fields = FakeFields([FakeAssignee('groundhog')._asdict()], + [FakeLabel('Component: Java')._asdict()], + FakeMilestone('', 'open')._asdict(), + 'closed', '[Java] The issue Title') issue = FakeIssue(fields) - jira = FakeJIRA(issue=issue, - project_versions=SOURCE_VERSIONS, - transitions=TRANSITIONS) + gh = FakeGitHub(issues={fake_issue_id: issue}, + project_versions=SOURCE_VERSIONS) - fix_versions = [SOURCE_VERSIONS[0].raw] - issue = merge_arrow_pr.JiraIssue(jira, 'ARROW-1234', 'ARROW', FakeCLI()) + fix_versions = [SOURCE_VERSIONS[0]._asdict()] + issue = merge_arrow_pr.GitHubIssue(gh, fake_issue_id, FakeCLI()) with pytest.raises(Exception, - match="ARROW-1234 already has status 'Resolved'"): - issue.resolve(fix_versions, "") - - -def test_no_unset_point_release_fix_version(): - # ARROW-6915: We have had the problem of issues marked with a point release - # having their fix versions overwritten by the merge tool. This verifies - # that existing patch release versions are carried over - status = FakeStatus('In Progress') - - versions_json = { - '0.14.2': {'name': '0.14.2', 'id': 1}, - '0.15.1': {'name': '0.15.1', 'id': 2}, - '0.16.0': {'name': '0.16.0', 'id': 3}, - '0.17.0': {'name': '0.17.0', 'id': 4} - } - - fields = FakeFields(status, 'summary', FakeAssignee('someone'), - [FakeComponent('Java')], - [FakeVersion(v, versions_json[v]) - for v in ['0.17.0', '0.15.1', '0.14.2']], None) - issue = FakeIssue(fields) - - 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('0.16.0', "a comment") - - assert jira.captured_transition == { - 'jira_id': 'ARROW-1234', - 'transition_id': 1, - 'comment': 'a comment', - 'fixVersions': [versions_json[v] - for v in ['0.16.0', '0.15.1', '0.14.2']] - } - - issue.resolve([versions_json['0.15.1']], "a comment") - - assert jira.captured_transition == { - 'jira_id': 'ARROW-1234', - 'transition_id': 1, - 'comment': 'a comment', - 'fixVersions': [versions_json[v] for v in ['0.15.1', '0.14.2']] - } + match="GitHub issue GH-1234 already has status 'closed'"): + issue.resolve(fix_versions, "", "") -def test_jira_output_no_components(): +def test_gh_output_no_components(): # ARROW-5472 status = 'Interesting work' - components = [] output = merge_arrow_pr.format_issue_output( - "jira", 'ARROW-1234', 'Resolved', status, - FakeAssignee('Foo Bar'), components + 'github', 'GH-1234', 'Resolved', status, + 'username', [] ) - assert output == """=== JIRA ARROW-1234 === + assert output == """=== GITHUB GH-1234 === Summary\t\tInteresting work -Assignee\tFoo Bar +Assignee\tusername Components\tNO COMPONENTS!!! Status\t\tResolved -URL\t\thttps://issues.apache.org/jira/browse/ARROW-1234""" +URL\t\thttps://github.com/apache/arrow/issues/1234""" output = merge_arrow_pr.format_issue_output( - "jira", 'ARROW-1234', 'Resolved', status, FakeAssignee('Foo Bar'), - [FakeComponent('C++'), FakeComponent('Python')] + 'github', 'GH-1234', 'Resolved', status, 'username', + [FakeLabel('C++'), FakeLabel('Python')] ) - assert output == """=== JIRA ARROW-1234 === + assert output == """=== GITHUB GH-1234 === Summary\t\tInteresting work -Assignee\tFoo Bar +Assignee\tusername Components\tC++, Python Status\t\tResolved -URL\t\thttps://issues.apache.org/jira/browse/ARROW-1234""" +URL\t\thttps://github.com/apache/arrow/issues/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}, + {'title': '11.0.0', 'state': 'open'}, + {'title': '9.0.0', 'state': 'open'}, + {'title': '10.0.0', 'state': 'open'}, ] - versions = [FakeVersion(raw['name'], raw) for raw in versions_json] - fix_version = merge_arrow_pr.get_candidate_fix_version(versions) + versions = [FakeMilestone(raw['title'], raw['state']) for raw in versions_json] + fix_version = merge_arrow_pr.get_candidate_fix_version([x.title for x in versions]) assert fix_version == "9.0.0" diff --git a/r/vignettes/install.Rmd b/r/vignettes/install.Rmd index 07d858c6cd62a..69780bd64dfb2 100644 --- a/r/vignettes/install.Rmd +++ b/r/vignettes/install.Rmd @@ -433,7 +433,7 @@ compatible with the current version of the R package. Please check the "Known installation issues" below to see if any apply, and if none apply, set the environment variable `ARROW_R_DEV=TRUE` for more verbose output and try installing again. Then, -please [report an issue](https://issues.apache.org/jira/projects/ARROW/issues) +please [report an issue](https://github.com/apache/arrow/issues/new/choose) and include the full installation output. ### Using system libraries @@ -465,7 +465,7 @@ update the libarrow system packages. If the R package finds and downloads a prebuilt binary of libarrow, but then the arrow package can't be loaded, perhaps with "undefined symbols" errors, -please [report an issue](https://issues.apache.org/jira/projects/ARROW/issues). +please [report an issue](https://github.com/apache/arrow/issues/new/choose). This is likely a compiler mismatch and may be resolvable by setting some environment variables to instruct R to compile the packages to match libarrow. @@ -475,7 +475,7 @@ instead of downloading the prebuilt binary. That should guarantee that the compiler settings match. If a prebuilt libarrow binary wasn't found for your operating system but you think it should have been, -please [report an issue](https://issues.apache.org/jira/projects/ARROW/issues) and share the console output. +please [report an issue](https://github.com/apache/arrow/issues/new/choose) and share the console output. You may also set the environment variable `ARROW_R_DEV=TRUE` for additional debug messages. @@ -485,7 +485,7 @@ If building libarrow from source fails, check the error message. (If you don't see an error message, only the `----- NOTE -----`, set the environment variable `ARROW_R_DEV=TRUE` to increase verbosity and retry installation.) The install script should work everywhere, so if libarrow fails to compile, -please [report an issue](https://issues.apache.org/jira/projects/ARROW/issues) +please [report an issue](https://github.com/apache/arrow/issues/new/choose) so that we can improve the script. ## Contributing