Skip to content

Commit ca4f108

Browse files
authored
Merge pull request #357 from djachkov/master
Bamboo - new example, documentation, restructuring
2 parents 198e105 + 5b24fcd commit ca4f108

File tree

3 files changed

+377
-155
lines changed

3 files changed

+377
-155
lines changed

atlassian/bamboo.py

Lines changed: 172 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66

77

88
class Bamboo(AtlassianRestAPI):
9+
10+
""" Private methods """
11+
912
def _get_generator(self, path, elements_key='results', element_key='result', data=None, flags=None,
1013
params=None, headers=None, max_results=None):
1114
"""
@@ -60,26 +63,8 @@ def base_list_call(self, resource, expand, favourite, clover_enabled, max_result
6063
max_results=max_results)
6164
params['start-index'] = start_index
6265
return self.get(self.resource_url(resource), flags=flags, params=params)
63-
64-
def get_custom_expiry(self, limit=25):
65-
"""
66-
Get list of all plans where user has admin permission and which override global expiry settings.
67-
If global expiry is not enabled it returns empty response.
68-
:param limit:
69-
"""
70-
url = "rest/api/latest/admin/expiry/custom/plan?limit={}".format(limit)
71-
return self.get(url)
7266

73-
def plan_directory_info(self, plan_key):
74-
"""
75-
Returns information about the directories where artifacts, build logs, and build results will be stored.
76-
Disabled by default.
77-
See https://confluence.atlassian.com/display/BAMBOO/Plan+directory+information+REST+API for more information.
78-
:param plan_key:
79-
:return:
80-
"""
81-
resource = 'planDirectoryInfo/{}'.format(plan_key)
82-
return self.get(self.resource_url(resource))
67+
""" Projects & Plans """
8368

8469
def projects(self, expand=None, favourite=False, clover_enabled=False, max_results=25):
8570
return self.base_list_call('project', expand, favourite, clover_enabled, max_results,
@@ -102,6 +87,91 @@ def project_plans(self, project_key):
10287
def plans(self, expand=None, favourite=False, clover_enabled=False, start_index=0, max_results=25):
10388
return self.base_list_call("plan", expand, favourite, clover_enabled, start_index, max_results,
10489
elements_key='plans', element_key='plan')
90+
91+
def plan_directory_info(self, plan_key):
92+
"""
93+
Returns information about the directories where artifacts, build logs, and build results will be stored.
94+
Disabled by default.
95+
See https://confluence.atlassian.com/display/BAMBOO/Plan+directory+information+REST+API for more information.
96+
:param plan_key:
97+
:return:
98+
"""
99+
resource = 'planDirectoryInfo/{}'.format(plan_key)
100+
return self.get(self.resource_url(resource))
101+
102+
def delete_plan(self, plan_key):
103+
"""
104+
Marks plan for deletion. Plan will be deleted by a batch job.
105+
:param plan_key:
106+
:return:
107+
"""
108+
resource = 'rest/api/latest/plan/{}'.format(plan_key)
109+
return self.delete(resource)
110+
111+
def enable_plan(self, plan_key):
112+
"""
113+
Enable plan.
114+
:param plan_key: str TST-BLD
115+
:return: POST request
116+
"""
117+
resource = 'plan/{plan_key}/enable'.format(plan_key=plan_key)
118+
return self.post(self.resource_url(resource))
119+
120+
""" Branches """
121+
122+
def search_branches(self, plan_key, include_default_branch=True, max_results=25):
123+
params = {
124+
'max-result': max_results,
125+
'start-index': 0,
126+
'masterPlanKey': plan_key,
127+
'includeMasterBranch': include_default_branch
128+
}
129+
size = 1
130+
while params['start-index'] < size:
131+
results = self.get(self.resource_url('search/branches'), params=params)
132+
size = results['size']
133+
for r in results['searchResults']:
134+
yield r
135+
params['start-index'] += results['max-result']
136+
137+
def plan_branches(self, plan_key, expand=None, favourite=False, clover_enabled=False, max_results=25):
138+
"""api/1.0/plan/{projectKey}-{buildKey}/branch"""
139+
resource = 'plan/{}/branch'.format(plan_key)
140+
return self.base_list_call(resource, expand, favourite, clover_enabled, max_results,
141+
elements_key='branches', element_key='branch')
142+
143+
def get_branch_info(self, plan_key, branch_name):
144+
"""
145+
Get information about a plan branch
146+
:param plan_key:
147+
:param branch_name:
148+
:return:
149+
"""
150+
resource = 'plan/{plan_key}/branch/{branch_name}'.format(plan_key=plan_key, branch_name=branch_name)
151+
return self.get(self.resource_url(resource))
152+
153+
def create_branch(self, plan_key, branch_name, vcs_branch=None, enabled=False, cleanup_enabled=False):
154+
"""
155+
Method for creating branch for a specified plan.
156+
You can use vcsBranch query param to define which vcsBranch should newly created branch use.
157+
If not specified it will not override vcsBranch from the main plan.
158+
159+
:param plan_key: str TST-BLD
160+
:param branch_name: str new-shiny-branch
161+
:param vcs_branch: str feature/new-shiny-branch, /refs/heads/new-shiny-branch
162+
:param enabled: bool
163+
:param cleanup_enabled: bool
164+
:return: PUT request
165+
"""
166+
resource = 'plan/{plan_key}/branch/{branch_name}'.format(plan_key=plan_key, branch_name=branch_name)
167+
params = {}
168+
if vcs_branch:
169+
params = dict(vcsBranch=vcs_branch,
170+
enabled='true' if enabled else 'false',
171+
cleanupEnabled='true' if cleanup_enabled else 'false')
172+
return self.put(self.resource_url(resource), params=params)
173+
174+
""" Build results """
105175

106176
def results(self, project_key=None, plan_key=None, job_key=None, build_number=None, expand=None, favourite=False,
107177
clover_enabled=False, issue_key=None, label=None, start_index=0, max_results=25, include_all_states=False):
@@ -239,34 +309,33 @@ def delete_build_result(self, build_key):
239309
params = {'buildKey': plan_key, 'buildNumber': build_number}
240310
return self.post(custom_resource, params=params, headers=self.form_token_headers)
241311

242-
def delete_plan(self, plan_key):
312+
def execute_build(self, plan_key, stage=None, execute_all_stages=True, custom_revision=None, **bamboo_variables):
243313
"""
244-
Marks plan for deletion. Plan will be deleted by a batch job.
245-
:param plan_key:
246-
:return:
314+
Fire build execution for specified plan.
315+
!IMPORTANT! NOTE: for some reason, this method always execute all stages
316+
:param plan_key: str TST-BLD
317+
:param stage: str stage-name
318+
:param execute_all_stages: bool
319+
:param custom_revision: str revisionName
320+
:param bamboo_variables: dict {variable=value}
321+
:return: POST request
247322
"""
248-
resource = 'rest/api/latest/plan/{}'.format(plan_key)
249-
return self.delete(resource)
323+
headers = self.form_token_headers
324+
resource = 'queue/{plan_key}'.format(plan_key=plan_key)
325+
params = {}
326+
if stage:
327+
execute_all_stages = False
328+
params['stage'] = stage
329+
if custom_revision:
330+
params['customRevision'] = custom_revision
331+
params['executeAllStages'] = 'true' if execute_all_stages else 'false'
332+
if bamboo_variables:
333+
for key, value in bamboo_variables.items():
334+
params['bamboo.variable.{}'.format(key)] = value
250335

251-
def reports(self, max_results=25):
252-
params = {'max-results': max_results}
253-
return self._get_generator(self.resource_url('chart/reports'), elements_key='reports', element_key='report',
254-
params=params)
336+
return self.post(self.resource_url(resource), params=params, headers=headers)
255337

256-
def chart(self, report_key, build_keys, group_by_period, date_filter=None, date_from=None, date_to=None,
257-
width=None, height=None, start_index=9, max_results=25):
258-
params = {'reportKey': report_key, 'buildKeys': build_keys, 'groupByPeriod': group_by_period,
259-
'start-index': start_index, 'max-results': max_results}
260-
if date_filter:
261-
params['dateFilter'] = date_filter
262-
if date_filter == 'RANGE':
263-
params['dateFrom'] = date_from
264-
params['dateTo'] = date_to
265-
if width:
266-
params['width'] = width
267-
if height:
268-
params['height'] = height
269-
return self.get(self.resource_url('chart'), params=params)
338+
""" Comments & Labels """
270339

271340
def comments(self, project_key, plan_key, build_number, start_index=0, max_results=25):
272341
resource = "result/{}-{}-{}/comment".format(project_key, plan_key, build_number)
@@ -291,24 +360,17 @@ def delete_label(self, project_key, plan_key, build_number, label):
291360
resource = "result/{}-{}-{}/label/{}".format(project_key, plan_key, build_number, label)
292361
return self.delete(self.resource_url(resource))
293362

294-
def server_info(self):
295-
return self.get(self.resource_url('info'))
296-
297-
def agent_status(self):
298-
return self.get(self.resource_url('agent'))
299-
300-
def activity(self):
301-
return self.get('build/admin/ajax/getDashboardSummary.action')
302-
303-
def deployment_project(self, project_id):
304-
resource = 'deploy/project/{}'.format(project_id)
305-
return self.get(self.resource_url(resource))
363+
""" Deployments """
306364

307365
def deployment_projects(self):
308366
resource = 'deploy/project/all'
309367
for project in self.get(self.resource_url(resource)):
310368
yield project
311369

370+
def deployment_project(self, project_id):
371+
resource = 'deploy/project/{}'.format(project_id)
372+
return self.get(self.resource_url(resource))
373+
312374
def deployment_environment_results(self, env_id, expand=None, max_results=25):
313375
resource = 'deploy/environment/{environmentId}/results'.format(environmentId=env_id)
314376
params = {'max-result': max_results, 'start-index': 0}
@@ -330,105 +392,7 @@ def deployment_dashboard(self, project_id=None):
330392
resource = 'deploy/dashboard/{}'.format(project_id) if project_id else 'deploy/dashboard'
331393
return self.get(self.resource_url(resource))
332394

333-
def search_branches(self, plan_key, include_default_branch=True, max_results=25):
334-
params = {
335-
'max-result': max_results,
336-
'start-index': 0,
337-
'masterPlanKey': plan_key,
338-
'includeMasterBranch': include_default_branch
339-
}
340-
size = 1
341-
while params['start-index'] < size:
342-
results = self.get(self.resource_url('search/branches'), params=params)
343-
size = results['size']
344-
for r in results['searchResults']:
345-
yield r
346-
params['start-index'] += results['max-result']
347-
348-
def plan_branches(self, plan_key, expand=None, favourite=False, clover_enabled=False, max_results=25):
349-
"""api/1.0/plan/{projectKey}-{buildKey}/branch"""
350-
resource = 'plan/{}/branch'.format(plan_key)
351-
return self.base_list_call(resource, expand, favourite, clover_enabled, max_results,
352-
elements_key='branches', element_key='branch')
353-
354-
def create_branch(self, plan_key, branch_name, vcs_branch=None, enabled=False, cleanup_enabled=False):
355-
"""
356-
Method for creating branch for a specified plan.
357-
You can use vcsBranch query param to define which vcsBranch should newly created branch use.
358-
If not specified it will not override vcsBranch from the main plan.
359-
360-
:param plan_key: str TST-BLD
361-
:param branch_name: str new-shiny-branch
362-
:param vcs_branch: str feature/new-shiny-branch, /refs/heads/new-shiny-branch
363-
:param enabled: bool
364-
:param cleanup_enabled: bool
365-
:return: PUT request
366-
"""
367-
resource = 'plan/{plan_key}/branch/{branch_name}'.format(plan_key=plan_key, branch_name=branch_name)
368-
params = {}
369-
if vcs_branch:
370-
params = dict(vcsBranch=vcs_branch,
371-
enabled='true' if enabled else 'false',
372-
cleanupEnabled='true' if cleanup_enabled else 'false')
373-
return self.put(self.resource_url(resource), params=params)
374-
375-
def get_branch_info(self, plan_key, branch_name):
376-
"""
377-
Get information about a plan branch
378-
:param plan_key:
379-
:param branch_name:
380-
:return:
381-
"""
382-
resource = 'plan/{plan_key}/branch/{branch_name}'.format(plan_key=plan_key, branch_name=branch_name)
383-
return self.get(self.resource_url(resource))
384-
385-
def enable_plan(self, plan_key):
386-
"""
387-
Enable plan.
388-
:param plan_key: str TST-BLD
389-
:return: POST request
390-
"""
391-
resource = 'plan/{plan_key}/enable'.format(plan_key=plan_key)
392-
return self.post(self.resource_url(resource))
393-
394-
def execute_build(self, plan_key, stage=None, execute_all_stages=True, custom_revision=None, **bamboo_variables):
395-
"""
396-
Fire build execution for specified plan.
397-
!IMPORTANT! NOTE: for some reason, this method always execute all stages
398-
:param plan_key: str TST-BLD
399-
:param stage: str stage-name
400-
:param execute_all_stages: bool
401-
:param custom_revision: str revisionName
402-
:param bamboo_variables: dict {variable=value}
403-
:return: POST request
404-
"""
405-
headers = self.form_token_headers
406-
resource = 'queue/{plan_key}'.format(plan_key=plan_key)
407-
params = {}
408-
if stage:
409-
execute_all_stages = False
410-
params['stage'] = stage
411-
if custom_revision:
412-
params['customRevision'] = custom_revision
413-
params['executeAllStages'] = 'true' if execute_all_stages else 'false'
414-
if bamboo_variables:
415-
for key, value in bamboo_variables.items():
416-
params['bamboo.variable.{}'.format(key)] = value
417-
418-
return self.post(self.resource_url(resource), params=params, headers=headers)
419-
420-
def health_check(self):
421-
"""
422-
Get health status
423-
https://confluence.atlassian.com/jirakb/how-to-retrieve-health-check-results-using-rest-api-867195158.html
424-
:return:
425-
"""
426-
# check as Troubleshooting & Support Tools Plugin
427-
response = self.get('rest/troubleshooting/1.0/check/')
428-
if not response:
429-
# check as support tools
430-
response = self.get('rest/supportHealthCheck/1.0/check/')
431-
return response
395+
""" Users & Groups """
432396

433397
def get_users_in_global_permissions(self, start=0, limit=25):
434398
"""
@@ -486,7 +450,7 @@ def add_users_into_group(self, group_name, users):
486450
url = 'rest/api/latest/admin/groups/{}/add-users'.format(group_name)
487451
return self.post(url, data=users)
488452

489-
def remove_users_into_group(self, group_name, users):
453+
def remove_users_from_group(self, group_name, users):
490454
"""
491455
Remove multiple users from a group.
492456
The list of usernames should be passed as request body.
@@ -540,6 +504,59 @@ def get_build_queue(self, expand='queuedBuilds'):
540504
params = {'expand': expand}
541505
return self.get('rest/api/latest/queue', params=params)
542506

507+
"""Other actions"""
508+
509+
def server_info(self):
510+
return self.get(self.resource_url('info'))
511+
512+
def agent_status(self):
513+
return self.get(self.resource_url('agent'))
514+
515+
def activity(self):
516+
return self.get('build/admin/ajax/getDashboardSummary.action')
517+
518+
def get_custom_expiry(self, limit=25):
519+
"""
520+
Get list of all plans where user has admin permission and which override global expiry settings.
521+
If global expiry is not enabled it returns empty response.
522+
:param limit:
523+
"""
524+
url = "rest/api/latest/admin/expiry/custom/plan?limit={}".format(limit)
525+
return self.get(url)
526+
527+
def reports(self, max_results=25):
528+
params = {'max-results': max_results}
529+
return self._get_generator(self.resource_url('chart/reports'), elements_key='reports', element_key='report',
530+
params=params)
531+
532+
def chart(self, report_key, build_keys, group_by_period, date_filter=None, date_from=None, date_to=None,
533+
width=None, height=None, start_index=9, max_results=25):
534+
params = {'reportKey': report_key, 'buildKeys': build_keys, 'groupByPeriod': group_by_period,
535+
'start-index': start_index, 'max-results': max_results}
536+
if date_filter:
537+
params['dateFilter'] = date_filter
538+
if date_filter == 'RANGE':
539+
params['dateFrom'] = date_from
540+
params['dateTo'] = date_to
541+
if width:
542+
params['width'] = width
543+
if height:
544+
params['height'] = height
545+
return self.get(self.resource_url('chart'), params=params)
546+
547+
def health_check(self):
548+
"""
549+
Get health status
550+
https://confluence.atlassian.com/jirakb/how-to-retrieve-health-check-results-using-rest-api-867195158.html
551+
:return:
552+
"""
553+
# check as Troubleshooting & Support Tools Plugin
554+
response = self.get('rest/troubleshooting/1.0/check/')
555+
if not response:
556+
# check as support tools
557+
response = self.get('rest/supportHealthCheck/1.0/check/')
558+
return response
559+
543560
def upload_plugin(self, plugin_path):
544561
"""
545562
Provide plugin path for upload into Jira e.g. useful for auto deploy

0 commit comments

Comments
 (0)