Skip to content

Commit

Permalink
adding experimental integrations support
Browse files Browse the repository at this point in the history
  • Loading branch information
jayfk committed Feb 1, 2017
1 parent 4cfade2 commit c8e09e8
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 11 deletions.
32 changes: 25 additions & 7 deletions pyup/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@

class Bot(object):
def __init__(self, repo, user_token, bot_token=None,
provider=GithubProvider, bundle=RequirementsBundle, config=Config):
provider=GithubProvider, bundle=RequirementsBundle, config=Config,
integration=False):
self.bot_token = bot_token
self.req_bundle = bundle()
self.provider = provider(self.req_bundle)
self.provider = provider(self.req_bundle, integration)
self.user_token = user_token
self.bot_token = bot_token
self.fetched_files = []
Expand All @@ -30,6 +31,8 @@ def __init__(self, repo, user_token, bot_token=None,

self._fetched_prs = False

self.integration = integration

@property
def user_repo(self):
if self._user_repo is None:
Expand Down Expand Up @@ -57,8 +60,13 @@ def bot_repo(self):
@property
def pull_requests(self):
if not self._fetched_prs:
self.req_bundle.pull_requests = [pr for pr in self.provider.iter_issues(
repo=self.user_repo, creator=self.bot if self.bot_token else self.user)]
self.req_bundle.pull_requests = [
pr for pr in self.provider.iter_issues(
repo=self.user_repo,
creator=self.bot if self.bot_token else self.user
)
if pr.is_valid
]
self._fetched_prs = True
return self.req_bundle.pull_requests

Expand Down Expand Up @@ -196,11 +204,10 @@ def close_stale_prs(self, update, pull_request, scheduled):
:param update:
:param pull_request:
"""
logger.info("Preparing to close stale PRs for {}".format(pull_request.title))
closed = []
if self.bot_token and not pull_request.is_initial:
for pr in self.pull_requests:
close_pr = False
logger.info("Checking PR {}".format(pr.title))
same_title = \
pr.canonical_title(self.config.pr_prefix) == \
pull_request.canonical_title(self.config.pr_prefix)
Expand All @@ -227,6 +234,7 @@ def close_stale_prs(self, update, pull_request, scheduled):
close_pr = True

if close_pr and self.is_bot_the_only_committer(pr=pr):
logger.info("Closing stale PR {} for {}".format(pr.title, pull_request.title))
self.provider.close_pull_request(
bot_repo=self.bot_repo,
user_repo=self.user_repo,
Expand All @@ -235,19 +243,29 @@ def close_stale_prs(self, update, pull_request, scheduled):
pull_request.number),
prefix=self.config.branch_prefix
)
pr.state = "closed"
closed.append(pr)
for closed_pr in closed:
self.pull_requests.remove(closed_pr)


def is_bot_the_only_committer(self, pr):
"""
Checks if the bot is the only committer for the given pull request.
:param update: Update to check
:return: bool - True if conflict found
"""
logger.info("check if bot is only committer")
committer = self.provider.get_pull_request_committer(
self.user_repo,
pr)
# flatten the list and remove duplicates
committer_set = set([c.login for c in committer])

# it's impossible to get the bots login if this is an integration, just check that
# there's only one commit in the commit history.
if self.integration:
return len(committer_set) == 1

# check that there's exactly one committer in this PRs commit history and
# that the committer is the bot
return len(committer_set) == 1 and \
Expand Down
27 changes: 23 additions & 4 deletions pyup/providers/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@


class Provider(object):
def __init__(self, bundle):
def __init__(self, bundle, integration=False):
self.bundle = bundle
self.integration = integration

@classmethod
def is_same_user(cls, this, that):
Expand Down Expand Up @@ -73,12 +74,18 @@ def get_file(self, repo, path, branch):
return None, None

def create_and_commit_file(self, repo, path, branch, content, commit_message, committer):
# integrations don't support committer data being set. Add this as extra kwarg
# if we're not dealing with an integration token
extra_kwargs = {}
if not self.integration:
extra_kwargs["committer"] = self.get_committer_data(committer)

return repo.create_file(
path=path,
message=commit_message,
content=content,
branch=branch,
committer=self.get_committer_data(committer),
**extra_kwargs
)

def get_requirement_file(self, repo, path, branch):
Expand Down Expand Up @@ -137,6 +144,12 @@ def create_commit(self, path, branch, commit_message, content, sha, repo, commit
if not path.startswith("/"):
path = "/" + path

# integrations don't support committer data being set. Add this as extra kwarg
# if we're not dealing with an integration token
extra_kwargs = {}
if not self.integration:
extra_kwargs["committer"] = self.get_committer_data(committer)

for i in range(1, 7):
try:
data = repo.update_file(
Expand All @@ -145,7 +158,7 @@ def create_commit(self, path, branch, commit_message, content, sha, repo, commit
content=content,
branch=branch,
sha=sha,
committer=self.get_committer_data(committer),
**extra_kwargs
)
return data["content"].sha
except GithubException as e:
Expand Down Expand Up @@ -257,7 +270,13 @@ def create_issue(self, repo, title, body):
raise

def iter_issues(self, repo, creator):
for issue in repo.get_issues(creator=creator.login):
# integrations don't support the creator param. Add this as extra kwarg
# if we're not dealing with an integration token
extra_kwargs = {}
if not self.integration:
extra_kwargs["creator"] = creator.login

for issue in repo.get_issues(**extra_kwargs):
yield self.bundle.get_pull_request_class()(
state=issue.state,
title=issue.title,
Expand Down
6 changes: 6 additions & 0 deletions pyup/pullrequest.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ def is_scheduled(self):
def is_open(self):
return self.state == "open"

@property
def is_valid(self):
return self.is_update or self.is_security \
or self.is_pin or self.is_initial \
or self.is_compile or self.is_scheduled

def get_requirement(self, prefix=""):
if self.type != "initial":
title = self.canonical_title(prefix)
Expand Down
18 changes: 18 additions & 0 deletions tests/test_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,24 @@ def test_close_success(self):
prefix="pyup-"
)

def test_close_integration(self):
bot = bot_factory(bot_token="foo", prs=[self.other_pr])
bot.integration = True
bot.provider.integration = True
commiter = Mock()
bot.provider.get_pull_request_committer.return_value = [commiter]

bot.close_stale_prs(self.update, self.pr, False)

bot.provider.get_pull_request_committer.assert_called_once_with(bot.user_repo, self.other_pr)
bot.provider.close_pull_request.assert_called_once_with(
bot_repo=bot.bot_repo,
user_repo=bot.user_repo,
pull_request=self.other_pr,
comment="Closing this in favor of #100",
prefix="pyup-"
)

def test_close_success_with_prefix(self):
bot = bot_factory(bot_token="foo", prs=[self.other_pr])
bot.config.pr_prefix = "Some Prefix"
Expand Down
15 changes: 15 additions & 0 deletions tests/test_pullrequest.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,21 @@ def test_scheduled(self):
self.assertEqual(pr.type, "scheduled")
self.assertTrue(pr.is_scheduled)

def test_is_valid(self):
pr = pullrequest_factory(title="Scheduled foo in bar")
self.assertTrue(pr.is_valid)

pr = pullrequest_factory(title="Compile foo.txt")
self.assertTrue(pr.is_valid)

pr = pullrequest_factory(title="Initial Update")
self.assertTrue(pr.is_valid)

pr = pullrequest_factory(title="Pin this on thta")
self.assertTrue(pr.is_valid)

pr = pullrequest_factory(title="Update this and that")
self.assertTrue(pr.is_valid)

class PullRequestEQTest(TestCase):
def test_is_eq(self):
Expand Down

0 comments on commit c8e09e8

Please sign in to comment.