Skip to content

Commit a20bb60

Browse files
authored
[Bitbucket Cloud] Pull Request Builds (#667)
* - Add PR property for pipeline - add statuses() response for PR * Adds builds for pull requests * unittests, build properties refname + website
1 parent f7fe277 commit a20bb60

File tree

4 files changed

+167
-1
lines changed

4 files changed

+167
-1
lines changed

atlassian/bitbucket/cloud/repositories/pipelines.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# coding=utf-8
22

3+
from .pullRequests import PullRequest
34
from requests import HTTPError
45

56
from ..base import BitbucketCloudBase
@@ -126,6 +127,17 @@ def completed_on(self):
126127
""" The pipeline completion time """
127128
return self.get_time("completed_on")
128129

130+
@property
131+
def pullrequest(self):
132+
""" Returns a PullRequest object if the pipeline was triggered by a pull request, else None """
133+
target = self.get_data("target")
134+
if target["type"] == "pipeline_pullrequest_target":
135+
return PullRequest(
136+
target["pullrequest"]["links"]["self"]["href"], target["pullrequest"], **self._new_session_args
137+
)
138+
else:
139+
return None
140+
129141
def stop(self):
130142
"""
131143
Stop the pipeline

atlassian/bitbucket/cloud/repositories/pullRequests.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,14 @@ def author(self):
197197
""" User object of the author """
198198
return User(None, self.get_data("author"))
199199

200+
def statuses(self):
201+
"""
202+
Returns generator object of the statuses endpoint
203+
204+
API docs: https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Bworkspace%7D/%7Brepo_slug%7D/pullrequests/%7Bpull_request_id%7D/statuses
205+
"""
206+
return self._get_paged("{}/statuses".format(self.url), absolute=True)
207+
200208
def participants(self):
201209
""" Returns a generator object of participants """
202210
for participant in self.get_data("participants"):
@@ -211,6 +219,14 @@ def reviewers(self):
211219

212220
return
213221

222+
def builds(self):
223+
"""Returns the latest Build objects for the pull request."""
224+
builds = [b for b in self.statuses() if b["type"] == "build"]
225+
for build in builds:
226+
yield Build(build, **self._new_session_args)
227+
228+
return
229+
214230
def comment(self, raw_message):
215231
"""
216232
Commenting the pull request in raw format
@@ -321,6 +337,10 @@ def is_reviewer(self):
321337
""" True if the user is a pull request reviewer """
322338
return self.get_data("role") == self.ROLE_REVIEWER
323339

340+
@property
341+
def is_default_reviewer(self):
342+
""" True if the user is a default reviewer """
343+
324344
@property
325345
def has_changes_requested(self):
326346
""" True if user requested changes """
@@ -335,3 +355,75 @@ def has_approved(self):
335355
def participated_on(self):
336356
""" time of last participation """
337357
return self.get_time("participated_on")
358+
359+
360+
class Build(BitbucketCloudBase):
361+
STATE_FAILED = "FAILED"
362+
STATE_INPROGRESS = "INPROGRESS"
363+
STATE_STOPPED = "STOPPED"
364+
STATE_SUCCESSFUL = "SUCCESSFUL"
365+
366+
def __init__(self, data, *args, **kwargs) -> None:
367+
super(Build, self).__init__(None, None, *args, data=data, expected_type="build", **kwargs)
368+
369+
@property
370+
def key(self):
371+
"""Key of the build"""
372+
return self.get_data("key")
373+
374+
@property
375+
def name(self):
376+
"""Name of the build"""
377+
return self.get_data("name")
378+
379+
@property
380+
def description(self):
381+
"""Build description"""
382+
return self.get_data("description")
383+
384+
@property
385+
def failed(self):
386+
"""True if the build was stopped"""
387+
return self.get_data("state") == self.STATE_FAILED
388+
389+
@property
390+
def inprogress(self):
391+
"""True if the build is inprogress"""
392+
return self.get_data("state") == self.STATE_INPROGRESS
393+
394+
@property
395+
def successful(self):
396+
"""True if the build was successful"""
397+
return self.get_data("state") == self.STATE_SUCCESSFUL
398+
399+
@property
400+
def stopped(self):
401+
"""True if the build was stopped"""
402+
return self.get_data("state") == self.STATE_STOPPED
403+
404+
@property
405+
def created_on(self):
406+
""" time of creation """
407+
return self.get_time("created_on")
408+
409+
@property
410+
def updated_on(self):
411+
""" time of last update """
412+
return self.get_time("updated_on")
413+
414+
@property
415+
def commit(self):
416+
"""Returns the hash key of the commit"""
417+
return self.get_data("commit")["hash"]
418+
419+
@property
420+
def website(self):
421+
"""Returns the url to the builds webpage.
422+
This url points to the build's frontend website (Pipelines, Jenkins ...)
423+
"""
424+
return self.get_data("url")
425+
426+
@property
427+
def refname(self):
428+
"""Returns the refname"""
429+
return self.get_data("refname")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
responses[None] = {
2+
"pagelen": 30,
3+
"values": [
4+
{
5+
"key": "12345",
6+
"description": "This commit looks good.",
7+
"repository": {
8+
"links": {
9+
"self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1"},
10+
"html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1"},
11+
"avatar": {"href": "https://bytebucket.org/ravatar/%7BRepoUUID%7D?ts=default"},
12+
},
13+
"type": "repository",
14+
"name": "testrepository1",
15+
"full_name": "TestWorkspace1/testrepository1",
16+
"uuid": "{RepoUUID}",
17+
},
18+
"url": "http://urltothebui.ld/5",
19+
"links": {
20+
"commit": {
21+
"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/commit/1fbd047cd99a"
22+
},
23+
"self": {
24+
"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/commit/1fbd047cd99a/statuses/build/12345"
25+
},
26+
},
27+
"refname": "feature/abranch",
28+
"state": "SUCCESSFUL",
29+
"created_on": "2021-01-22T14:47:16.366063+00:00",
30+
"commit": {
31+
"hash": "1fbd047cd99a",
32+
"type": "commit",
33+
"links": {
34+
"self": {
35+
"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/commit/1fbd047cd99a"
36+
},
37+
"html": {"href": "https://bitbucket.org/TestWorkspace1/testrepository1/commits/1fbd047cd99a"},
38+
},
39+
},
40+
"updated_on": "2021-01-22T14:51:00.667312+00:00",
41+
"type": "build",
42+
"name": "Build #5",
43+
}
44+
],
45+
"page": 1,
46+
"size": 1,
47+
}

tests/test_bitbucket_cloud.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from atlassian import Bitbucket
77
from atlassian.bitbucket import Cloud
88
from atlassian.bitbucket.cloud.common.users import User
9-
from atlassian.bitbucket.cloud.repositories.pullRequests import Participant, PullRequest
9+
from atlassian.bitbucket.cloud.repositories.pullRequests import Participant, PullRequest, Build
1010

1111
BITBUCKET = None
1212
try:
@@ -289,3 +289,18 @@ def test_create(self, tc2):
289289
)
290290
assert pr.id == 1
291291
assert len(list(pr.reviewers())) == 3
292+
293+
def test_builds(self, tc1):
294+
builds = list(tc1.builds())
295+
assert len(builds) == 1
296+
297+
build = builds[0]
298+
assert isinstance(build, Build)
299+
assert build.successful
300+
assert build.name == "Build #5"
301+
assert build.description == "This commit looks good."
302+
assert build.key == "12345"
303+
assert _datetimetostr(build.created_on) == _datetimetostr(datetime(2021, 1, 22, 14, 47, 16, 366063))
304+
assert build.commit == "1fbd047cd99a"
305+
assert build.refname == "feature/abranch"
306+
assert build.website == "http://urltothebui.ld/5"

0 commit comments

Comments
 (0)