Ejemplo n.º 1
0
def create_or_update_comment(ghrequest, comment,
                             ONLY_UPDATE_COMMENT_BUT_NOT_CREATE):
    query = f"/repos/{ghrequest.repository}/issues/{str(ghrequest.pr_number)}/comments"
    comments = utils.query_request(query).json()

    # Get the last comment id by the bot
    last_comment_id = None
    for old_comment in comments:
        if old_comment["user"]["login"] == os.environ["BOT_USERNAME"]:
            last_comment_id = old_comment["id"]
            break

    if last_comment_id is None and not ONLY_UPDATE_COMMENT_BUT_NOT_CREATE:  # Create a new comment
        response = utils.query_request(query=query,
                                       method='POST',
                                       json={"body": comment})
        ghrequest.comment_response = response.json()
    else:  # Update the last comment
        utc_time = datetime.datetime.utcnow()
        time_now = utc_time.strftime("%Y-%m-%d %H:%M:%S UTC")
        comment += f"\n\n##### Comment last updated at {time_now!s}"

        query = f"/repos/{ghrequest.repository}/issues/comments/{str(last_comment_id)}"
        response = utils.query_request(query,
                                       method='PATCH',
                                       json={"body": comment})

    return response
Ejemplo n.º 2
0
def create_or_update_comment(ghrequest, comment, ONLY_UPDATE_COMMENT_BUT_NOT_CREATE):
    query = "/repos/{}/issues/{}/comments"
    query = query.format(ghrequest.repository, str(ghrequest.pr_number))
    comments = utils.query_request(query).json()

    # Get the last comment id by the bot
    last_comment_id = None
    for old_comment in comments:
        if old_comment["user"]["id"] == 24736507:  # ID of @pep8speaks
            last_comment_id = old_comment["id"]
            break

    if last_comment_id is None and not ONLY_UPDATE_COMMENT_BUT_NOT_CREATE:  # Create a new comment
        response = utils.query_request(query=query, method='POST', json={"body": comment})
        ghrequest.comment_response = response.json()
    else:  # Update the last comment
        utc_time = datetime.datetime.utcnow()
        time_now = utc_time.strftime("%B %d, %Y at %H:%M Hours UTC")
        comment += "\n\n##### Comment last updated on {}"
        comment = comment.format(time_now)

        query = "/repos/{}/issues/comments/{}"
        query = query.format(ghrequest.repository, str(last_comment_id))
        response = utils.query_request(query, method='PATCH', json={"body": comment})

    return response
Ejemplo n.º 3
0
def delete_if_forked(ghrequest):
    FORKED = False
    query = "/user/repos"
    r = utils.query_request(query)
    for repo in r.json():
        if repo["description"]:
            if ghrequest.target_repo_fullname in repo["description"]:
                FORKED = True
                url = f"/repos/{repo['full_name']}"
                utils.query_request(url, method='DELETE')
    return FORKED
Ejemplo n.º 4
0
def autopep8(ghrequest, config):
    # Run pycodestyle

    r = utils.query_request(ghrequest.diff_url)
    ## All the python files with additions
    patch = unidiff.PatchSet(r.content.splitlines(), encoding=r.encoding)

    # A dictionary with filename paired with list of new line numbers
    py_files = {}

    for patchset in patch:
        if patchset.target_file[-3:] == '.py':
            py_file = patchset.target_file[1:]
            py_files[py_file] = []
            for hunk in patchset:
                for line in hunk.target_lines():
                    if line.is_added:
                        py_files[py_file].append(line.target_line_no)

    # Ignore errors and warnings specified in the config file
    to_ignore = ",".join(config["pycodestyle"]["ignore"])
    arg_to_ignore = ""
    if len(to_ignore) > 0:
        arg_to_ignore = "--ignore " + to_ignore

    for py_file in py_files:
        filename = py_file[1:]
        url = "https://raw.githubusercontent.com/{}/{}/{}"
        url = url.format(ghrequest.repository, ghrequest.sha, py_file)
        r = utils.query_request(url)
        with open("file_to_fix.py", 'w+', encoding=r.encoding) as file_to_fix:
            file_to_fix.write(r.text)

        cmd = 'autopep8 file_to_fix.py --diff {arg_to_ignore}'.format(
            arg_to_ignore=arg_to_ignore)
        proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
        stdout, _ = proc.communicate()
        ghrequest.diff[filename] = stdout.decode(r.encoding)

        # Fix the errors
        ghrequest.diff[filename] = ghrequest.diff[filename].replace(
            "file_to_check.py", filename)
        ghrequest.diff[filename] = ghrequest.diff[filename].replace(
            "\\", "\\\\")

        ## Store the link to the file
        url = "https://github.com/{}/blob/{}{}"
        ghrequest.links = {}
        ghrequest.links[filename + "_link"] = url.format(
            ghrequest.repository, ghrequest.sha, py_file)
        os.remove("file_to_fix.py")
Ejemplo n.º 5
0
def comment_permission_check(ghrequest):
    """
    Check for quite and resume status or duplicate comments
    """
    repository = ghrequest.repository

    # Check for duplicate comment
    url = "/repos/{}/issues/{}/comments"
    url = url.format(repository, str(ghrequest.pr_number))
    comments = utils.query_request(url).json()

    # # Get the last comment by the bot
    # last_comment = ""
    # for old_comment in reversed(comments):
    #     if old_comment["user"]["id"] == 24736507:  # ID of @pep8speaks
    #         last_comment = old_comment["body"]
    #         break

    # # Disabling this because only a single comment is made per PR
    # text1 = ''.join(BeautifulSoup(markdown(comment)).findAll(text=True))
    # text2 = ''.join(BeautifulSoup(markdown(last_comment)).findAll(text=True))
    # if text1 == text2.replace("submitting", "updating"):
    #     PERMITTED_TO_COMMENT = False

    # Check if the bot is asked to keep quiet
    for old_comment in reversed(comments):
        if '@pep8speaks' in old_comment['body']:
            if 'resume' in old_comment['body'].lower():
                break
            elif 'quiet' in old_comment['body'].lower():
                return False

    # Check for [skip pep8]
    ## In commits
    commits = utils.query_request(ghrequest.commits_url).json()
    for commit in commits:
        if any(m in commit["commit"]["message"].lower()
               for m in ["[skip pep8]", "[pep8 skip]"]):
            return False
    ## PR title
    if any(m in ghrequest.pr_title.lower()
           for m in ["[skip pep8]", "[pep8 skip]"]):
        return False
    ## PR description
    if any(m in ghrequest.pr_desc.lower()
           for m in ["[skip pep8]", "[pep8 skip]"]):
        return False

    return True
Ejemplo n.º 6
0
def _create_diff(ghrequest, config):
    # Dictionary with filename matched with a string of diff
    ghrequest.diff = {}

    # Process the files and prepare the diff for the gist
    helpers.autopep8(ghrequest, config)

    # Create the gist
    helpers.create_gist(ghrequest)

    comment = "Here you go with [the gist]({}) !\n\n" + \
              "> You can ask me to create a PR against this branch " + \
              "with those fixes. Simply comment " + \
              "`@pep8speaks pep8ify`.\n\n"
    if ghrequest.reviewer == ghrequest.author:  # Both are the same person
        comment += "@{} "
        comment = comment.format(ghrequest.gist_url, ghrequest.reviewer)
    else:
        comment += "@{} @{} "
        comment = comment.format(ghrequest.gist_url, ghrequest.reviewer,
                                 ghrequest.author)

    query = "/repos/{}/issues/{}/comments"
    query = query.format(ghrequest.repository, str(ghrequest.pr_number))
    response = utils.query_request(query,
                                   method='POST',
                                   json={"body": comment})
    ghrequest.comment_response = response.json()

    if ghrequest.error:
        return utils.Response(ghrequest, status=400)

    return utils.Response(ghrequest)
Ejemplo n.º 7
0
def update_users(repository):
    """Star the repository from the bot account"""
    headers = {
        "Content-Length": "0",
    }
    query = f"/user/starred/{repository}"
    return utils.query_request(query=query, method='PUT', headers=headers)
Ejemplo n.º 8
0
def follow_user(user):
    """Follow the user of the service"""
    headers = {
        "Content-Length": "0",
    }
    query = f"/user/following/{user}"
    return utils.query_request(query=query, method='PUT', headers=headers)
Ejemplo n.º 9
0
def commit(ghrequest):
    fullname = ghrequest.fork_fullname

    for old_file, new_file in ghrequest.results.items():
        query = f"/repos/{fullname}/contents/{old_file}"
        params = {"ref": ghrequest.new_branch}
        r = utils.query_request(query, params=params)
        sha_blob = r.json().get("sha")
        params["path"] = old_file
        content_code = base64.b64encode(new_file.encode()).decode("utf-8")
        request_json = {
            "path": old_file,
            "message": f"Fix PEP 8 errors in {old_file}",
            "content": content_code,
            "sha": sha_blob,
            "branch": ghrequest.new_branch,
        }
        r = utils.query_request(query, method='PUT', json=request_json)
Ejemplo n.º 10
0
def create_new_branch(ghrequest):
    query = f"/repos/{ghrequest.fork_fullname}/git/refs/heads"
    sha = None
    r = utils.query_request(query)
    for ref in r.json():
        if ref["ref"].split("/")[-1] == ghrequest.target_repo_branch:
            sha = ref["object"]["sha"]

    query = f"/repos/{ghrequest.fork_fullname}/git/refs"
    ghrequest.new_branch = f"{ghrequest.target_repo_branch}-pep8-patch"
    request_json = {
        "ref": f"refs/heads/{ghrequest.new_branch}",
        "sha": sha,
    }
    r = utils.query_request(query, method='POST', json=request_json)

    if r.status_code > 299:
        ghrequest.error = "Could not create a new branch in the fork"
Ejemplo n.º 11
0
def run_pycodestyle(ghrequest, config):
    """
    Runs the linter cli tool on the files and update ghrequest
    """
    linter = config["scanner"]["linter"]  # Either pycodestyle or flake8
    repo = ghrequest.repository
    pr_number = ghrequest.pr_number
    commit = ghrequest.after_commit_hash

    r = repo_zip_url = utils.query_request(
        f'https://github.com/{repo}/archive/{commit}.zip')

    with open('/tmp/repo.zip', 'wb') as zf:
        zf.write(r.content)

    with ZipFile('/tmp/repo.zip') as zf:
        zf.extractall('/tmp')

    repo_dir = f'/tmp/{repo.split("/")[1]}-{commit}'
    diff = utils.query_request(
        # for some reason the `https:github.com/{repo}/pull/{pr_number}.diff` endpoint
        # doesn't work with private repos.
        f'https://api.github.com/repos/{repo}/pulls/{pr_number}',
        headers={
            'accept': 'application/vnd.github.v3.diff'
        }).text
    if config["scanner"]["linter"] == "flake8":
        cmd = f'flake8 {config["flake8_cmd_config"]} --diff'
    else:
        cmd = f'pycodestyle {config["pycodestyle_cmd_config"]} --diff'
    logging.debug(f'Running linter command: {cmd}')
    proc = Popen(cmd, shell=True, cwd=repo_dir, stdin=PIPE, stdout=PIPE)
    stdout, stderr = proc.communicate(input=diff.encode())

    output_lines = stdout.decode().splitlines()
    for line in output_lines:
        filename = line.split(':')[0]
        relevant_error_pattern = f"^{filename}:\d+:\d+:\s[WEF]\d+\s.*"
        # Other error codes are B C D T
        if re.search(relevant_error_pattern, line):
            ghrequest.results.setdefault(filename, []).append(line)

    shutil.rmtree(repo_dir)
    os.remove('/tmp/repo.zip')
Ejemplo n.º 12
0
def run_pycodestyle(ghrequest, config):
    """
    Runs the linter cli tool on the files and update ghrequest
    """
    linter = config["scanner"]["linter"]  # Either pycodestyle or flake8
    repo = ghrequest.repository
    pr_number = ghrequest.pr_number
    commit = ghrequest.after_commit_hash

    # Run linter
    ## All the python files with additions
    # A dictionary with filename paired with list of new line numbers
    files_to_exclude = config[linter]["exclude"]
    py_files = get_py_files_in_pr(repo, pr_number, files_to_exclude)

    ghrequest.links = {}  # UI Link of each updated file in the PR
    for py_file in py_files:
        filename = py_file[1:]
        query = f"https://raw.githubusercontent.com/{repo}/{commit}/{py_file}"
        r = utils.query_request(query)
        with open("file_to_check.py", 'w+', encoding=r.encoding) as file_to_check:
            file_to_check.write(r.text)

        # Use the command line here
        if config["scanner"]["linter"] == "flake8":
            cmd = f'flake8 {config["flake8_cmd_config"]} file_to_check.py'
        else:
            cmd = f'pycodestyle {config["pycodestyle_cmd_config"]} file_to_check.py'
        proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
        stdout, _ = proc.communicate()
        ghrequest.extra_results[filename] = stdout.decode(r.encoding).splitlines()

        # Put only relevant errors in the ghrequest.results dictionary
        ghrequest.results[filename] = []
        for error in list(ghrequest.extra_results[filename]):
            relevant_error_pattern = r"^file_to_check.py:\d+:\d+:\s[WEF]\d+\s.*"
            # Other error codes are B C D T
            if re.search(relevant_error_pattern, error):
                ghrequest.results[filename].append(error.replace("file_to_check.py", filename))
                ghrequest.extra_results[filename].remove(error)

        # Replace file_to_check.py with filename in all additional errors
        extras = ghrequest.extra_results[filename]
        ghrequest.extra_results[filename] = [e.replace("file_to_check.py", filename) for e in extras]

        ## Remove errors in case of diff_only = True
        ## which are caused in the whole file
        for error in list(ghrequest.results[filename]):
            if config["scanner"]["diff_only"]:
                if not int(error.split(":")[1]) in py_files[py_file]:
                    ghrequest.results[filename].remove(error)

        ## Store the link to the file
        url = f"https://github.com/{repo}/blob/{commit}{py_file}"
        ghrequest.links[filename + "_link"] = url
        os.remove("file_to_check.py")
Ejemplo n.º 13
0
def autopep8ify(ghrequest, config):
    # Run pycodestyle
    r = utils.query_request(ghrequest.diff_url)

    ## All the python files with additions
    patch = unidiff.PatchSet(r.content.splitlines(), encoding=r.encoding)

    # A dictionary with filename paired with list of new line numbers
    py_files = {}

    linter = config["scanner"]["linter"]
    files_to_exclude = config[linter]["exclude"]

    for patchset in patch:
        if patchset.target_file[-3:] == '.py':
            py_file = patchset.target_file[1:]
            if utils.filename_match(py_file, files_to_exclude):
                continue
            py_files[py_file] = []
            for hunk in patchset:
                for line in hunk.target_lines():
                    if line.is_added:
                        py_files[py_file].append(line.target_line_no)

    # Ignore errors and warnings specified in the config file
    to_ignore = ",".join(config["pycodestyle"]["ignore"])
    arg_to_ignore = ""
    if len(to_ignore) > 0:
        arg_to_ignore = "--ignore " + to_ignore

    for py_file in py_files:
        filename = py_file[1:]
        query = f"https://raw.githubusercontent.com/{ghrequest.repository}/{ghrequest.sha}/{py_file}"
        r = utils.query_request(query)
        with open("file_to_fix.py", 'w+', encoding=r.encoding) as file_to_fix:
            file_to_fix.write(r.text)

        cmd = f'autopep8 file_to_fix.py {arg_to_ignore}'
        proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
        stdout, _ = proc.communicate()
        ghrequest.results[filename] = stdout.decode(r.encoding)

        os.remove("file_to_fix.py")
Ejemplo n.º 14
0
 def test_request(self, mocker, query, method, json, data, headers, params):
     mock_func = mock.MagicMock(return_value=True)
     mocker.patch('requests.request', mock_func)
     query_request(query,
                   method,
                   json=json,
                   data=data,
                   headers=headers,
                   params=params)
     assert mock_func.call_count == 1
     assert mock_func.call_args[0][0] == method
     assert mock_func.call_args[1]['headers'] == headers
     assert mock_func.call_args[1]['auth'] == ('', '')
     assert mock_func.call_args[1]['params'] == params
     assert mock_func.call_args[1]['json'] == json
     if query[0] == "/":
         assert mock_func.call_args[0][1] == BASE_URL + query
     else:
         assert mock_func.call_args[0][1] == query
Ejemplo n.º 15
0
def fork_for_pr(ghrequest):
    query = f"/repos/{ghrequest.target_repo_fullname}/forks"
    r = utils.query_request(query, method='POST')

    if r.status_code == 202:
        ghrequest.fork_fullname = r.json()["full_name"]
        return True

    ghrequest.error = "Unable to fork"
    return False
Ejemplo n.º 16
0
def update_fork_desc(ghrequest):
    # Check if forked (takes time)
    query = f"/repos/{ghrequest.fork_fullname}"
    r = utils.query_request(query)
    ATTEMPT = 0
    while(r.status_code != 200):
        time.sleep(5)
        r = utils.query_request(query)
        ATTEMPT += 1
        if ATTEMPT > 10:
            ghrequest.error = "Forking is taking longer than usual"
            break

    full_name = ghrequest.target_repo_fullname
    author, name = full_name.split("/")
    request_json = {
        "name": name,
        "description": f"Forked from @{author}'s {full_name}"
    }
    r = utils.query_request(query, method='PATCH', data=json.dumps(request_json))
    if r.status_code != 200:
        ghrequest.error = "Could not update description of the fork"
Ejemplo n.º 17
0
def create_pr(ghrequest):
    query = f"/repos/{ghrequest.target_repo_fullname}/pulls"
    request_json = {
        "title": "Fix PEP 8 errors",
        "head": f"pep8speaks:{ghrequest.new_branch}",
        "base": ghrequest.target_repo_branch,
        "body": "The changes are suggested by autopep8",
    }
    r = utils.query_request(query, method='POST', json=request_json)
    if r.status_code == 201:
        ghrequest.pr_url = r.json()["html_url"]
    else:
        ghrequest.error = "Pull request could not be created"
Ejemplo n.º 18
0
def run_pycodestyle(ghrequest, config):
    """
    Runs the pycodestyle cli tool on the files and update ghrequest
    """
    repo = ghrequest.repository
    pr_number = ghrequest.pr_number
    commit = ghrequest.after_commit_hash

    # Run pycodestyle
    ## All the python files with additions
    # A dictionary with filename paired with list of new line numbers
    files_to_exclude = config["pycodestyle"]["exclude"]
    py_files = get_py_files_in_pr(repo, pr_number, files_to_exclude)

    ghrequest.links = {}  # UI Link of each updated file in the PR
    for py_file in py_files:
        filename = py_file[1:]
        query = "https://raw.githubusercontent.com/{}/{}/{}"
        query = query.format(repo, commit, py_file)
        r = utils.query_request(query)
        with open("file_to_check.py", 'w+',
                  encoding=r.encoding) as file_to_check:
            file_to_check.write(r.text)

        # Use the command line here
        cmd = 'pycodestyle {config[pycodestyle_cmd_config]} file_to_check.py'.format(
            config=config)
        proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
        stdout, _ = proc.communicate()
        ghrequest.extra_results[filename] = stdout.decode(
            r.encoding).splitlines()

        # Put only relevant errors in the ghrequest.results dictionary
        ghrequest.results[filename] = []
        for error in list(ghrequest.extra_results[filename]):
            if re.search("^file_to_check.py:\d+:\d+:\s[WE]\d+\s.*", error):
                ghrequest.results[filename].append(
                    error.replace("file_to_check.py", filename))
                ghrequest.extra_results[filename].remove(error)

        ## Remove errors in case of diff_only = True
        ## which are caused in the whole file
        for error in list(ghrequest.results[filename]):
            if config["scanner"]["diff_only"]:
                if not int(error.split(":")[1]) in py_files[py_file]:
                    ghrequest.results[filename].remove(error)

        ## Store the link to the file
        url = "https://github.com/{}/blob/{}{}"
        ghrequest.links[filename + "_link"] = url.format(repo, commit, py_file)
        os.remove("file_to_check.py")
Ejemplo n.º 19
0
def create_pr(ghrequest):
    r = utils.query_request(
        f'/repos/{ghrequest.target_repo_fullname}/compare/{ghrequest.target_repo_branch}...pep8speaks:{ghrequest.new_branch}',
        headers={'Accept': 'application/vnd.github.v3.diff'},
    )
    if r.headers['content-length'] == '0':
        # no diff
        return False

    query = f"/repos/{ghrequest.target_repo_fullname}/pulls"
    request_json = {
        "title": "Fix PEP 8 errors",
        "head": f"pep8speaks:{ghrequest.new_branch}",
        "base": ghrequest.target_repo_branch,
        "body": "The changes are suggested by autopep8",
    }
    r = utils.query_request(query, method='POST', json=request_json)
    if r.status_code == 201:
        ghrequest.pr_url = r.json()["html_url"]
    else:
        ghrequest.error = "Pull request could not be created"

    return True
Ejemplo n.º 20
0
    def _get_pull_request(self, request, event):
        """
        Get data about the pull request created
        """
        if not self.OK:
            return None

        if event == "issue_comment":
            pr_url = request.json['issue']['pull_request']['url']
            pull_request = utils.query_request(pr_url).json()
        elif event == "pull_request":
            pull_request = request.json['pull_request']
        else:
            return None
        return pull_request
Ejemplo n.º 21
0
    def _is_request_valid(self, request, event):
        # The bot should be able to access the repository
        r = utils.query_request(request['repository']['url'])
        if not r.ok:
            return False

        # A valid pull request payload can only be created or updated with commits
        if event == 'pull_request':
            if request['action'] in ['synchronize', 'opened', 'reopened']:
                return True
        elif event == 'issue_comment':
            if request['action'] in ('created', 'edited'
                                     ) and 'pull_request' in request['issue']:
                return True

        return False
Ejemplo n.º 22
0
def get_config(repo, base_branch):
    """
    Get .pep8speaks.yml config file from the repository and return
    the config dictionary
    """

    # Default configuration parameters
    default_config_path = os.path.join(os.path.dirname(__file__), '..', 'data',
                                       'default_config.json')
    with open(default_config_path) as config_file:
        config = json.loads(config_file.read())

    # Configuration file
    query = "https://raw.githubusercontent.com/{}/{}/.pep8speaks.yml"
    query = query.format(repo, base_branch)

    r = utils.query_request(query)

    if r.status_code == 200:
        try:
            new_config = yaml.load(r.text)
            # overloading the default configuration with the one specified
            config = utils.update_dict(config, new_config)
        except yaml.YAMLError:  # Bad YAML file
            pass

    # Create pycodestyle command line arguments
    arguments = []
    confs = config["pycodestyle"]
    for key, value in confs.items():
        if value:  # Non empty
            if isinstance(value, int):
                if isinstance(value, bool):
                    arguments.append("--{}".format(key))
                else:
                    arguments.append("--{}={}".format(key, value))
            elif isinstance(value, list):
                arguments.append("--{}={}".format(key, ','.join(value)))
    config["pycodestyle_cmd_config"] = ' {arguments}'.format(
        arguments=' '.join(arguments))

    # pycodestyle is case-sensitive
    config["pycodestyle"]["ignore"] = [
        e.upper() for e in list(config["pycodestyle"]["ignore"])
    ]

    return config
Ejemplo n.º 23
0
def create_gist(ghrequest):
    """Create gists for diff files"""
    request_json = {}
    request_json["public"] = True
    request_json["files"] = {}
    request_json["description"] = f"In response to @{ghrequest.reviewer}'s comment: {ghrequest.review_url}"

    for diff_file, diffs in ghrequest.diff.items():
        if len(diffs) != 0:
            request_json["files"][diff_file.split("/")[-1] + ".diff"] = {
                "content": diffs
            }

    # Call github api to create the gist
    query = "/gists"
    response = utils.query_request(query, method='POST', json=request_json).json()
    ghrequest.gist_response = response
    ghrequest.gist_url = response["html_url"]
Ejemplo n.º 24
0
def get_files_involved_in_pr(repo, pr_number):
    """
    Return a list of file names modified/added in the PR
    """
    headers = {"Accept": "application/vnd.github.VERSION.diff"}

    query = f"/repos/{repo}/pulls/{pr_number}"
    r = utils.query_request(query, headers=headers)

    patch = unidiff.PatchSet(r.content.splitlines(), encoding=r.encoding)

    files = {}

    for patchset in patch:
        diff_file = patchset.target_file[1:]
        files[diff_file] = []
        for hunk in patchset:
            for line in hunk.target_lines():
                if line.is_added:
                    files[diff_file].append(line.target_line_no)
    return files
Ejemplo n.º 25
0
def _pep8ify(ghrequest, config):
    ghrequest.target_repo_fullname = ghrequest.pull_request["head"]["repo"][
        "full_name"]
    ghrequest.target_repo_branch = ghrequest.pull_request["head"]["ref"]
    ghrequest.results = {}

    # Check if the fork of the target repo exists
    # If yes, then delete it
    helpers.delete_if_forked(ghrequest)
    # Fork the target repository
    helpers.fork_for_pr(ghrequest)
    # Update the fork description. This helps in fast deleting it
    helpers.update_fork_desc(ghrequest)
    # Create a new branch for the PR
    helpers.create_new_branch(ghrequest)
    # Fix the errors in the files
    helpers.autopep8ify(ghrequest, config)
    # Commit each change onto the branch
    helpers.commit(ghrequest)
    # Create a PR from the branch to the target repository
    helpers.create_pr(ghrequest)

    comment = "Here you go with [the Pull Request]({}) ! The fixes are " \
              "suggested by [autopep8](https://github.com/hhatto/autopep8).\n\n"
    if ghrequest.reviewer == ghrequest.author:  # Both are the same person
        comment += "@{} "
        comment = comment.format(ghrequest.pr_url, ghrequest.reviewer)
    else:
        comment += "@{} @{} "
        comment = comment.format(ghrequest.pr_url, ghrequest.reviewer,
                                 ghrequest.author)

    query = "/repos/{}/issues/{}/comments"
    query = query.format(ghrequest.repository, str(ghrequest.pr_number))
    response = utils.query_request(query,
                                   method='POST',
                                   json={"body": comment})
    ghrequest.comment_response = response.json()

    return utils.Response(ghrequest)
Ejemplo n.º 26
0
def get_config(repo, base_branch, after_commit_hash):
    """
    Get .pep8speaks.yml config file from the repository and return
    the config dictionary

    First look in the base branch of the Pull Request (general master branch).
    If no file is found, then look for a file in the head branch of the Pull Request.
    """

    # Default configuration parameters
    default_config = Path(__file__).absolute().parent.parent.joinpath("data", "default_pep8speaks.yml")
    with open(default_config, "r") as config_file:
        config = yaml.safe_load(config_file)

    linters = ["pycodestyle", "flake8"]

    # Read setup.cfg for [pycodestyle] or [flake8] section
    setup_config_file = ""
    query = f"https://raw.githubusercontent.com/{repo}/{base_branch}/setup.cfg"
    r = utils.query_request(query)
    if r.status_code == 200:
        setup_config_file = r.text
    else:  # Try to look for a config in the head branch of the Pull Request
        new_query = f"https://raw.githubusercontent.com/{repo}/{after_commit_hash}/setup.cfg"
        r_new = utils.query_request(new_query)
        if r_new.status_code == 200:
            setup_config_file = r_new.text

    if len(setup_config_file) > 0:
        linter_cfg_config = read_setup_cfg_file(setup_config_file)
        # Copy the setup.cfg config for all linters
        new_setup_config = {}
        for linter in linters:
            new_setup_config[linter] = linter_cfg_config
        config = utils.update_dict(config, new_setup_config)

    # Read .pep8speaks.yml
    new_config_text = ""

    # Configuration file
    query = f"https://raw.githubusercontent.com/{repo}/{base_branch}/.pep8speaks.yml"
    r = utils.query_request(query)

    if r.status_code == 200:
        new_config_text = r.text
    else:  # Try to look for a config in the head branch of the Pull Request
        new_query = f"https://raw.githubusercontent.com/{repo}/{after_commit_hash}/.pep8speaks.yml"
        r_new = utils.query_request(new_query)
        if r_new.status_code == 200:
            new_config_text = r_new.text

    if len(new_config_text) > 0:
        try:
            new_config = yaml.load(new_config_text)
            # overloading the default configuration with the one specified
            config = utils.update_dict(config, new_config)
        except yaml.YAMLError:  # Bad YAML file
            pass

    # Create pycodestyle and flake8 command line arguments
    for linter in linters:
        confs = config.get(linter, dict())
        arguments = []
        for key, value in confs.items():
            if value:  # Non empty
                if isinstance(value, int):
                    if isinstance(value, bool):
                        arguments.append(f"--{key}")
                    else:
                        arguments.append(f"--{key}={value}")
                elif isinstance(value, list):
                    arguments.append(f"--{key}={','.join(value)}")
        config[f"{linter}_cmd_config"] = f' {" ".join(arguments)}'

    # linters are case-sensitive with error codes
    for linter in linters:
        config[linter]["ignore"] = [e.upper() for e in list(config[linter]["ignore"])]

    return config
Ejemplo n.º 27
0
def create_a_new_pr(repo, expected_comment, head, sha, base="master"):
    """Create a new Pull Request on a test repository and verify the comment by pep8speaks."""

    print(f"Testing https://github.com/{repo}/tree/{head}")
    print("Waiting is required to to avoid triggering GitHub API abuse")

    responses_ok = [
    ]  # Store all GitHub requests r.ok variable and assert at the end. This
    # will enable debugging the requests

    # Create a new branch from the head branch for a new PR
    new_branch = f"{uuid.uuid4().hex}-{head}"
    request_json = {
        "ref": f"refs/heads/{new_branch}",
        "sha": sha,
    }
    query = f"/repos/{repo}/git/refs"
    r = query_request(query, method="POST", json=request_json)
    responses_ok.append(r.ok)
    if r.ok:
        print("New branch created!")

    # Create a pull request with the newly created branch and base branch on repo
    try:
        TRAVIS_PR_NUMBER = os.environ["TRAVIS_PR_NUMBER"]
        pr_title = f"Testing PR #{TRAVIS_PR_NUMBER} on OrkoHunter/PEP8Speaks"
        pr_body = f"Testing https://github.com/OrkoHunter/pep8speaks/pull/{TRAVIS_PR_NUMBER}\n\n"
    except KeyError:
        pr_title = "Testing new changes"
        pr_body = "Not triggered by Travis.\n\n"

    pr_body += f"---\n*Expected Result -*\n\n---\n{expected_comment}\n---"

    request_body = {
        'title': pr_title,
        'body': pr_body,
        'base': base,
        'head': new_branch
    }

    query = f"/repos/{repo}/pulls"
    r = query_request(query=query, method="POST", json=request_body)
    responses_ok.append(r.ok)
    response_data = r.json()
    # print(response_data)
    test_pr_number = response_data['number']
    print(f"Test PR Number #{test_pr_number}")

    # Wait for @pep8speaks to comment
    time.sleep(20)

    # Get the comment by @pep8speaks
    query = f"/repos/{repo}/issues/{test_pr_number}/comments"
    r = query_request(query=query)
    responses_ok.append(r.ok)
    response_data = r.json()
    # print("response_data for comments check ", response_data)
    comment = response_data[0]["body"]

    # Close the pull request
    query = f"/repos/{repo}/pulls/{test_pr_number}"
    r = query_request(query=query, method="PATCH", json={'state': 'closed'})
    responses_ok.append(r.ok)
    print("For closing the PR")
    # print(r.content.decode("utf-8"))

    # Delete the branch
    query = f"/repos/{repo}/git/refs/heads/{new_branch}"
    r = query_request(query=query, method="DELETE")
    responses_ok.append(r.ok)

    return responses_ok, comment