예제 #1
0
class TestRepoHandler:
    def setup_class(self):
        self.repo = RepoHandler('fakerepo/doesnotexist', branch='awesomebot')

    @patch('requests.get')
    def test_get_issues(self, mock_get):
        # http://engineroom.trackmaven.com/blog/real-life-mocking/
        mock_response = Mock()
        mock_response.json.return_value = [{
            'number': 42,
            'state': 'open'
        }, {
            'number': 55,
            'state': 'open',
            'pull_request': {
                'diff_url': 'blah'
            }
        }]
        mock_get.return_value = mock_response

        assert self.repo.get_issues('open', 'Close?') == [42]
        assert self.repo.get_issues('open', 'Close?',
                                    exclude_pr=False) == [42, 55]

    def test_urls(self):
        assert self.repo._url_contents == 'https://api.github.com/repos/fakerepo/doesnotexist/contents/'
        assert self.repo._url_pull_requests == 'https://api.github.com/repos/fakerepo/doesnotexist/pulls'
        assert self.repo._headers is None
예제 #2
0
def test_get_config(file_contents):
    file_contents.return_value = """[tool.stsci-bot]
changelog_check = false
autoclose_stale_pull_request = false
    """
    repo = RepoHandler('fake/fakebot')

    assert repo.get_config_value('changelog_check', True) is False
    assert repo.get_config_value('autoclose_stale_pull_request', True) is False
    assert repo.get_config_value('bizbaz', 42) == 42
예제 #3
0
class TestRealRepoHandler:
    def setup_class(self):
        # TODO: Use astropy/astropy-bot when #42 is merged.
        self.repo = RepoHandler('astropy/astropy-bot')

    def test_get_config(self):
        # These are set to False in YAML; defaults must not be used.
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter('always')
            do_changelog_check = self.repo.get_config_value(
                'changelog_check', True)
            do_autoclose_pr = self.repo.get_config_value(
                'autoclose_stale_pull_request', True)

        hit_api_limit = False
        other_warns = []
        for ww in w:
            s = str(ww.message)
            if 'API limit' in s:
                hit_api_limit = True
            else:
                other_warns.append(s)

        if hit_api_limit:
            pytest.xfail(s)
        elif len(other_warns) > 0:
            raise AssertionError(os.linesep.join(other_warns))
        else:
            assert not (do_changelog_check or do_autoclose_pr)

    @patch('requests.get')
    def test_get_file_contents(self, mock_get):
        content = b"I, for one, welcome our new robot overlords"

        mock_response = Mock()
        mock_response.ok = True
        mock_response.json.return_value = {
            'content': base64.b64encode(content)
        }
        mock_get.return_value = mock_response

        result = self.repo.get_file_contents('some/file/here.txt')
        assert result == content.decode('utf-8')

    @patch('requests.get')
    def test_missing_file_contents(self, mock_get):
        mock_response = Mock()
        mock_response.ok = False
        mock_response.json.return_value = {'message': 'Not Found'}
        mock_get.return_value = mock_response

        with pytest.raises(FileNotFoundError):
            self.repo.get_file_contents('some/file/here.txt')
예제 #4
0
def process_issues(repository, installation):

    now = time.time()

    # Get issues labeled as 'Close?'
    repo = RepoHandler(repository, 'master', installation)
    issuelist = repo.get_issues('open', 'Close?')

    for n in issuelist:

        print(f'Checking {n}')
        yield f'Checking {n}'

        issue = IssueHandler(repository, n, installation)
        labeled_time = issue.get_label_added_date('Close?')
        if labeled_time is None:
            continue

        dt = now - labeled_time

        if current_app.stale_issue_close and dt > current_app.stale_issue_close_seconds:
            comment_ids = issue.find_comments('astropy-bot[bot]',
                                              filter_keep=is_close_epilogue)
            if len(comment_ids) == 0:
                print(f'-> CLOSING issue {n}')
                yield f'-> CLOSING issue {n}'
                issue.set_labels(['closed-by-bot'])
                issue.submit_comment(ISSUE_CLOSE_EPILOGUE)
                issue.close()
            else:
                print(f'-> Skipping issue {n} (already closed)')
                yield f'-> Skipping issue {n} (already closed)'
        elif dt > current_app.stale_issue_warn_seconds:
            comment_ids = issue.find_comments('astropy-bot[bot]',
                                              filter_keep=is_close_warning)
            if len(comment_ids) == 0:
                print(f'-> WARNING issue {n}')
                yield f'-> WARNING issue {n}'
                issue.submit_comment(
                    ISSUE_CLOSE_WARNING.format(
                        pasttime=naturaltime(dt),
                        futuretime=naturaldelta(
                            current_app.stale_issue_close_seconds -
                            current_app.stale_issue_warn_seconds)))
            else:
                print(f'-> Skipping issue {n} (already warned)')
                yield f'-> Skipping issue {n} (already warned)'
        else:
            print(f'-> OK issue {n}')
            yield f'-> OK issue {n}'

    print('Finished checking for stale issues')
    yield 'Finished checking for stale issues'
예제 #5
0
def process_pull_requests(repository, installation):

    now = time.time()

    # Get issues labeled as 'Close?'
    repo = RepoHandler(repository, 'master', installation)
    pull_requests = repo.open_pull_requests()

    # User config
    enable_autoclose = repo.get_config_value('autoclose_stale_pull_request',
                                             True)

    for n in pull_requests:

        print(f'Checking {n}')

        pr = PullRequestHandler(repository, n, installation)
        if 'keep-open' in pr.labels:
            print('-> PROTECTED by label, skipping')
            continue
        commit_time = pr.last_commit_date

        dt = now - commit_time

        if current_app.stale_pull_requests_close and dt > current_app.stale_pull_requests_close_seconds:
            comment_ids = pr.find_comments('astropy-bot[bot]',
                                           filter_keep=is_close_epilogue)
            if not enable_autoclose:
                print(f'-> Skipping issue {n} (auto-close disabled)')
            elif len(comment_ids) == 0:
                print(f'-> CLOSING issue {n}')
                pr.submit_comment(PULL_REQUESTS_CLOSE_EPILOGUE)
                pr.close()
            else:
                print(f'-> Skipping issue {n} (already closed)')
        elif dt > current_app.stale_pull_requests_warn_seconds:
            comment_ids = pr.find_comments('astropy-bot[bot]',
                                           filter_keep=is_close_warning)
            if len(comment_ids) == 0:
                print(f'-> WARNING issue {n}')
                pr.submit_comment(
                    PULL_REQUESTS_CLOSE_WARNING.format(
                        pasttime=naturaldelta(dt),
                        futuretime=naturaldelta(
                            current_app.stale_pull_requests_close_seconds -
                            current_app.stale_pull_requests_warn_seconds)))
            else:
                print(f'-> Skipping issue {n} (already warned)')
        else:
            print(f'-> OK issue {n}')
예제 #6
0
class TestRealRepoHandler:
    def setup_class(self):
        # TODO: Use astropy/astropy-bot when #42 is merged.
        self.repo = RepoHandler('pllim/astropy-bot', branch='changelog-onoff')

    def test_get_config(self):
        # These are set to False in YAML; defaults must not be used.
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter('always')
            do_changelog_check = self.repo.get_config_value(
                'changelog_check', True)
            do_autoclose_pr = self.repo.get_config_value(
                'autoclose_stale_pull_request', True)

        hit_api_limit = False
        if len(w) > 0:
            hit_api_limit = True

        if hit_api_limit:
            pytest.xfail(str(w[-1].message))
        else:
            assert not (do_changelog_check or do_autoclose_pr)
예제 #7
0
 def setup_class(self):
     # TODO: Use astropy/astropy-bot when #42 is merged.
     self.repo = RepoHandler('astropy/astropy-bot')
예제 #8
0
 def setup_class(self):
     self.repo = RepoHandler('fakerepo/doesnotexist', branch='awesomebot')
예제 #9
0
 def setup_class(self):
     # TODO: Use astropy/astropy-bot when #42 is merged.
     self.repo = RepoHandler('pllim/astropy-bot', branch='changelog-onoff')
def process_changelog_consistency(repository, number, installation):

    # TODO: cache handlers and invalidate the internal cache of the handlers on
    # certain events.
    pr_handler = PullRequestHandler(repository, number, installation)

    # Don't comment on closed PR
    if pr_handler.is_closed:
        return "Pull request already close, no need to check"

    repo_handler = RepoHandler(pr_handler.head_repo_name,
                               pr_handler.head_branch, installation)

    # No-op if user so desires
    if not repo_handler.get_config_value('changelog_check', True):
        return "Repo owner does not want to check change log"

    # Find previous comments by this app
    comment_ids = pr_handler.find_comments(
        'stsci-bot[bot]', filter_keep=is_changelog_message)

    if len(comment_ids) == 0:
        comment_id = None
    else:
        comment_id = comment_ids[-1]

    # Construct message

    message = CHANGELOG_PROLOGUE.format(user=pr_handler.user)
    approve = False  # This is so that WIP and EXP shall not pass

    if 'Work in progress' in pr_handler.labels:
        message += CHANGELOG_NOT_DONE.format(
            status='a work in progress', is_done='is ready for review')

    elif 'Experimental' in pr_handler.labels:
        message += CHANGELOG_NOT_DONE.format(
            status='an experimental', is_done='discussion in settled')

    else:
        # Run checks
        check_milestone = repo_handler.get_config_value('check_milestone', True)
        issues = check_changelog_consistency(repo_handler, pr_handler,
                                             check_milestone=check_milestone)

        if len(issues) > 0:

            message += CHANGELOG_BAD_LIST
            for issue in issues:
                message += "* {0}\n".format(issue)

            message += CHANGELOG_BAD_EPILOGUE

            if len(issues) == 1:
                message = (message.replace('issues with', 'issue with')
                           .replace('fix these', 'fix this'))

        else:

            message += CHANGELOG_GOOD
            approve = True

    message += CHANGELOG_EPILOGUE

    comment_url = pr_handler.submit_comment(message, comment_id=comment_id,
                                            return_url=True)

    if approve:
        pr_handler.set_status('success', 'All checks passed', 'stsci-bot',
                              target_url=comment_url)
    else:
        pr_handler.set_status('failure', 'There were failures in checks - see '
                              'comments by @stsci-bot above', 'stsci-bot',
                              target_url=comment_url)

    return message