Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 188 additions & 0 deletions atlassian/bitbucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

class Bitbucket(AtlassianRestAPI):

bulk_headers = {"Content-Type": "application/vnd.atl.bitbucket.bulk+json"}

def project_list(self, limit=None):
"""
Provide the project list
Expand Down Expand Up @@ -748,6 +750,89 @@ def create_pull_request(self, project_key, repository, data):
repository=repository)
return self.post(url, data=data)

def decline_pull_request(self, project_key, repository, pr_id, pr_version):
"""
Decline a pull request.
The authenticated user must have REPO_READ permission for the repository
that this pull request targets to call this resource.

:param project_key: PROJECT
:param repository: my_shiny_repo
:param pr_id: 2341
:param pr_version: 12
:return:
"""
url = 'rest/api/1.0/projects/{project_key}/repos/{repository}/pull-requests/{pr_id}/decline'.format(
project_key=project_key,
repository=repository,
pr_id=pr_id
)
params = {'version': pr_version}

return self.post(url, params=params)

def is_pull_request_can_be_merged(self, project_key, repository, pr_id):
"""
Test whether a pull request can be merged.
A pull request may not be merged if:
- there are conflicts that need to be manually resolved before merging; and/or
- one or more merge checks have vetoed the merge.
The authenticated user must have REPO_READ permission for the repository
that this pull request targets to call this resource.

:param project_key: PROJECT
:param repository: my_shiny_repo
:param pr_id: 2341
:return:
"""
url = 'rest/api/1.0/projects/{project_key}/repos/{repository}/pull-requests/{pr_id}/merge'.format(
project_key=project_key,
repository=repository,
pr_id=pr_id
)
return self.get(url)

def merge_pull_request(self, project_key, repository, pr_id, pr_version):
"""
Merge pull request
The authenticated user must have REPO_READ permission for the repository
that this pull request targets to call this resource.

:param project_key: PROJECT
:param repository: my_shiny_repo
:param pr_id: 2341
:return:
"""
url = 'rest/api/1.0/projects/{project_key}/repos/{repository}/pull-requests/{pr_id}/merge'.format(
project_key=project_key,
repository=repository,
pr_id=pr_id
)
params = {'version': pr_version}

return self.post(url, params=params)

def reopen_pull_request(self, project_key, repository, pr_id, pr_version):
"""
Re-open a declined pull request.
The authenticated user must have REPO_READ permission for the repository
that this pull request targets to call this resource.

:param project_key: PROJECT
:param repository: my_shiny_repo
:param pr_id: 2341
:param pr_version: 12
:return:
"""
url = 'rest/api/1.0/projects/{project_key}/repos/{repository}/pull-requests/{pr_id}/reopen'.format(
project_key=project_key,
repository=repository,
pr_id=pr_id
)
params = {'version': pr_version}

return self.post(url, params=params)

def check_inbox_pull_requests_count(self):
return self.get('rest/api/1.0/inbox/pull-requests/count')

Expand Down Expand Up @@ -1100,6 +1185,109 @@ def get_branches_permissions(self, project, repository=None, start=0, limit=25):
params['start'] = start
return self.get(url, params=params)

def set_branches_permissions(
self, project_key,
multiple_permissions=False,
matcher_type=None,
matcher_value=None,
permission_type=None,
repository=None,
except_users=[],
except_groups=[],
except_access_keys=[],
start=0,
limit=25
):
"""
Create a restriction for the supplied branch or set of branches to be applied to the given repository.
Allows creating multiple restrictions at once.
To use multiple restrictions you should format payload manually - see the bitbucket-branch-restrictions.py example.
Reference: https://docs.atlassian.com/bitbucket-server/rest/6.8.0/bitbucket-ref-restriction-rest.html

:param project_key:
:param repository:
:return:
"""
headers = self.default_headers
if repository:
url = "/rest/branch-permissions/2.0/projects/{project_key}/repos/{repository}/restrictions".format(
project_key=project_key,
repository=repository
)
else:
url = "/rest/branch-permissions/2.0/projects/{project_key}/restrictions".format(
project_key=project_key
)
if multiple_permissions:
headers = self.bulk_headers
restriction = multiple_permissions
else:
restriction = {
"type": permission_type,
"matcher": {
"id": matcher_value,
"displayId": matcher_value,
"type": {
"id": matcher_type.upper(),
"name": matcher_type.capitalize()
},
"active": True,
},
"users": except_users,
"groups": except_groups,
"accessKeys": except_access_keys,
}
params = {"start": start, "limit": limit}
return self.post(url, data=restriction, params=params, headers=headers)

def delete_branch_permission(self, project_key, permission_id, repository=None):
"""
Deletes a restriction as specified by a restriction id.
The authenticated user must have REPO_ADMIN permission or higher to call this resource.

:param project_key:
:param repository:
:param permission_id:
:return:
"""

if repository:
url = "/rest/branch-permissions/2.0/projects/{project_key}/repos/{repository}/restrictions/{id}".format(
project_key=project_key,
repository=repository,
id=permission_id
)
else:
url = "/rest/branch-permissions/2.0/projects/{project_key}/restrictions/{id}".format(
project_key=project_key,
id=permission_id
)
return self.delete(url)

def get_branch_permission(self, project_key, permission_id, repository=None):
"""
Returns a restriction as specified by a restriction id.
The authenticated user must have REPO_ADMIN permission or higher to call this resource.

:param project_key:
:param repository:
:param permission_id:
:return:
"""

if repository:
url = "/rest/branch-permissions/2.0/projects/{project_key}/repos/{repository}/restrictions/{id}".format(
project_key=project_key,
repository=repository,
id=permission_id
)
else:
url = "/rest/branch-permissions/2.0/projects/{project_key}/restrictions/{id}".format(
project_key=project_key,
id=permission_id
)
return self.get(url)

def all_branches_permissions(self, project, repository=None):
"""
Get branches permissions from a given repo
Expand Down
47 changes: 39 additions & 8 deletions docs/bitbucket.rst
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@ Manage repositories
'prefix': 'release/'}]}
bitbucket.set_branching_model(project_key, repo_key, data)

bitbucket.repo_users(self, project_key, repo_key, limit=99999, filter_str=None)
bitbucket.repo_users(project_key, repo_key, limit=99999, filter_str=None)

bitbucket.repo_groups(self, project_key, repo_key, limit=99999, filter_str=None)
bitbucket.repo_groups(project_key, repo_key, limit=99999, filter_str=None)

# Grant repository permission to an specific user
bitbucket.repo_grant_user_permissions(project_key, repo_key, username, permission)
Expand Down Expand Up @@ -112,7 +112,7 @@ Manage code
# Requires an existing project in which this repository will be created. The only parameters which will be used
# are name and scmId.
# The authenticated user must have PROJECT_ADMIN permission for the context project to call this resource.
bitbucket.create_repo(project_key, repository, forkable=False, is_private=True):
bitbucket.create_repo(project_key, repository, forkable=False, is_private=True)

# Get branches from repo
bitbucket.get_branches(project, repository, filter='', limit=99999, details=True)
Expand All @@ -128,16 +128,16 @@ Manage code
bitbucket.get_pull_requests(project, repository, state='OPEN', order='newest', limit=100, start=0)

# Get pull request activities
bitbucket.get_pull_requests_activities(self, project, repository, pull_request_id)
bitbucket.get_pull_requests_activities(project, repository, pull_request_id)

# Get pull request changes
bitbucket.get_pull_requests_changes(self, project, repository, pull_request_id)
bitbucket.get_pull_requests_changes(project, repository, pull_request_id)

# Get pull request commits
bitbucket.get_pull_requests_commits(self, project, repository, pull_request_id)
bitbucket.get_pull_requests_commits(project, repository, pull_request_id)

# Add comment into pull request
bitbucket.add_pull_request_comment(self, project, repository, pull_request_id, text)
bitbucket.add_pull_request_comment(project, repository, pull_request_id, text)

# Get tags for related repo
bitbucket.get_tags(project, repository, filter='', limit=99999)
Expand Down Expand Up @@ -168,4 +168,35 @@ Manage code
"""
Retrieve the raw content for a file path at a specified revision.
The authenticated user must have REPO_READ permission for the specified repository to call this resource.
"""
"""

Branch permissions
------------------

.. code-block:: python

# Set branches permissions
bitbucket.set_branches_permissions(project_key, multiple_permissions=False, matcher_type=None, matcher_value=None, permission_type=None, repository=None, except_users=[], except_groups=[], except_access_keys=[], start=0, limit=25)

# Delete a single branch permission by premission id
bitbucket.delete_branch_permission(project_key, permission_id, repository=None)

# Get a single branch permission by permission id
bitbucket.get_branch_permission(project_key, permission_id, repository=None)

Pull Request management
-----------------------

.. code-block:: python

# Decline pull request
bitbucket.decline_pull_request(project_key, repository, pr_id, pr_version)

# Check if pull request can be merged
bitbucket.is_pull_request_can_be_merged(project_key, repository, pr_id)

# Merge pull request
bitbucket.merge_pull_request(project_key, repository, pr_id, pr_version)

# Reopen pull request
bitbucket.reopen_pull_request(project_key, repository, pr_id, pr_version)
80 changes: 80 additions & 0 deletions examples/bitbucket-branch-restrictions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# coding=utf-8
from atlassian import Bitbucket

"""
For all possible arguments and values please visit:
https://docs.atlassian.com/bitbucket-server/rest/latest/bitbucket-ref-restriction-rest.html
"""
bitbucket = Bitbucket(
url='http://localhost:7990',
username='admin',
password='admin',
advanced_mode=True) # For more simple response handling

single_permission = bitbucket.set_branches_permissions(
"PROJECT_KEY",
matcher_type="branch", # lowercase matcher type
matcher_value="master",
permission_type="no-deletes",
repository="repository_name",
except_users=["user1", "user2"]
)
print(single_permission)
pid = single_permission.json().get("id")

single_permission = bitbucket.get_branch_permission("PROJECT_KEY", pid)
print(single_permission)

deleted_permission = bitbucket.delete_branch_permission("PROJECT_KEY", pid)
print(deleted_permission)

multiplpe_permissions_payload = [
{
"type": "read-only",
"matcher": {
"id": "master",
"displayId": "master",
"type": {
"id": "BRANCH",
"name": "Branch"
},
"active": True
},
"users": [
"user1",
],
"groups": [

],
"accessKeys": []
},
{
"type": "pull-request-only",
"matcher": {
"id": "refs/tags/**",
"displayId": "refs/tags/**",
"type": {
"id": "PATTERN",
"name": "Pattern"
},
"active": True
},
"users": [
"user2"
],
"groups": [

],
"accessKeys": []
}
]
multiple_permissions = bitbucket.set_branches_permissions(
"PROJECT_KEY",
multiple_permissions=multiplpe_permissions_payload,
matcher_type="branch",
matcher_value="master",
permission_type="no-deletes",
repository="repository_name",
users=["user1", "user2"]
)
print(multiple_permissions)
29 changes: 29 additions & 0 deletions examples/bitbucket-manage-pull-request.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# coding=utf-8
from atlassian import Bitbucket

bitbucket = Bitbucket(
url='http://localhost:7990',
username='admin',
password='admin')
pr_id = 12345

pr = bitbucket.get_pullrequest("project_name", "repository_name", pr_id)
ver = pr.json().get("version")
print(f"PR version: {ver}")

response = bitbucket.decline_pull_request("project_name", "repository_name", pr_id, ver)
print(f"Declined: {response}")
ver = response.json().get("version")
print(f"PR version: {ver}")

response = bitbucket.reopen_pull_request("project_name", "repository_name", pr_id, ver)
print(f"Reopen: {response}")
ver = response.json().get("version")
print(f"PR version: {ver}")

response = bitbucket.is_pull_request_can_be_merged("project_name", "repository_name", pr_id)
print(f"Reopen: {response}")
print(f"PR version: {ver}")

response = bitbucket.merge_pull_request("project_name", "repository_name", pr_id, ver)
print(f"Merged: {response}")