diff --git a/atlassian/bamboo.py b/atlassian/bamboo.py index 39b4dfce6..602c99f7f 100755 --- a/atlassian/bamboo.py +++ b/atlassian/bamboo.py @@ -6,6 +6,9 @@ class Bamboo(AtlassianRestAPI): + + """ Private methods """ + def _get_generator(self, path, elements_key='results', element_key='result', data=None, flags=None, params=None, headers=None, max_results=None): """ @@ -60,26 +63,8 @@ def base_list_call(self, resource, expand, favourite, clover_enabled, max_result max_results=max_results) params['start-index'] = start_index return self.get(self.resource_url(resource), flags=flags, params=params) - - def get_custom_expiry(self, limit=25): - """ - Get list of all plans where user has admin permission and which override global expiry settings. - If global expiry is not enabled it returns empty response. - :param limit: - """ - url = "rest/api/latest/admin/expiry/custom/plan?limit={}".format(limit) - return self.get(url) - def plan_directory_info(self, plan_key): - """ - Returns information about the directories where artifacts, build logs, and build results will be stored. - Disabled by default. - See https://confluence.atlassian.com/display/BAMBOO/Plan+directory+information+REST+API for more information. - :param plan_key: - :return: - """ - resource = 'planDirectoryInfo/{}'.format(plan_key) - return self.get(self.resource_url(resource)) + """ Projects & Plans """ def projects(self, expand=None, favourite=False, clover_enabled=False, max_results=25): return self.base_list_call('project', expand, favourite, clover_enabled, max_results, @@ -102,6 +87,91 @@ def project_plans(self, project_key): def plans(self, expand=None, favourite=False, clover_enabled=False, start_index=0, max_results=25): return self.base_list_call("plan", expand, favourite, clover_enabled, start_index, max_results, elements_key='plans', element_key='plan') + + def plan_directory_info(self, plan_key): + """ + Returns information about the directories where artifacts, build logs, and build results will be stored. + Disabled by default. + See https://confluence.atlassian.com/display/BAMBOO/Plan+directory+information+REST+API for more information. + :param plan_key: + :return: + """ + resource = 'planDirectoryInfo/{}'.format(plan_key) + return self.get(self.resource_url(resource)) + + def delete_plan(self, plan_key): + """ + Marks plan for deletion. Plan will be deleted by a batch job. + :param plan_key: + :return: + """ + resource = 'rest/api/latest/plan/{}'.format(plan_key) + return self.delete(resource) + + def enable_plan(self, plan_key): + """ + Enable plan. + :param plan_key: str TST-BLD + :return: POST request + """ + resource = 'plan/{plan_key}/enable'.format(plan_key=plan_key) + return self.post(self.resource_url(resource)) + + """ Branches """ + + def search_branches(self, plan_key, include_default_branch=True, max_results=25): + params = { + 'max-result': max_results, + 'start-index': 0, + 'masterPlanKey': plan_key, + 'includeMasterBranch': include_default_branch + } + size = 1 + while params['start-index'] < size: + results = self.get(self.resource_url('search/branches'), params=params) + size = results['size'] + for r in results['searchResults']: + yield r + params['start-index'] += results['max-result'] + + def plan_branches(self, plan_key, expand=None, favourite=False, clover_enabled=False, max_results=25): + """api/1.0/plan/{projectKey}-{buildKey}/branch""" + resource = 'plan/{}/branch'.format(plan_key) + return self.base_list_call(resource, expand, favourite, clover_enabled, max_results, + elements_key='branches', element_key='branch') + + def get_branch_info(self, plan_key, branch_name): + """ + Get information about a plan branch + :param plan_key: + :param branch_name: + :return: + """ + resource = 'plan/{plan_key}/branch/{branch_name}'.format(plan_key=plan_key, branch_name=branch_name) + return self.get(self.resource_url(resource)) + + def create_branch(self, plan_key, branch_name, vcs_branch=None, enabled=False, cleanup_enabled=False): + """ + Method for creating branch for a specified plan. + You can use vcsBranch query param to define which vcsBranch should newly created branch use. + If not specified it will not override vcsBranch from the main plan. + + :param plan_key: str TST-BLD + :param branch_name: str new-shiny-branch + :param vcs_branch: str feature/new-shiny-branch, /refs/heads/new-shiny-branch + :param enabled: bool + :param cleanup_enabled: bool + :return: PUT request + """ + resource = 'plan/{plan_key}/branch/{branch_name}'.format(plan_key=plan_key, branch_name=branch_name) + params = {} + if vcs_branch: + params = dict(vcsBranch=vcs_branch, + enabled='true' if enabled else 'false', + cleanupEnabled='true' if cleanup_enabled else 'false') + return self.put(self.resource_url(resource), params=params) + + """ Build results """ def results(self, project_key=None, plan_key=None, job_key=None, build_number=None, expand=None, favourite=False, 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): params = {'buildKey': plan_key, 'buildNumber': build_number} return self.post(custom_resource, params=params, headers=self.form_token_headers) - def delete_plan(self, plan_key): + def execute_build(self, plan_key, stage=None, execute_all_stages=True, custom_revision=None, **bamboo_variables): """ - Marks plan for deletion. Plan will be deleted by a batch job. - :param plan_key: - :return: + Fire build execution for specified plan. + !IMPORTANT! NOTE: for some reason, this method always execute all stages + :param plan_key: str TST-BLD + :param stage: str stage-name + :param execute_all_stages: bool + :param custom_revision: str revisionName + :param bamboo_variables: dict {variable=value} + :return: POST request """ - resource = 'rest/api/latest/plan/{}'.format(plan_key) - return self.delete(resource) + headers = self.form_token_headers + resource = 'queue/{plan_key}'.format(plan_key=plan_key) + params = {} + if stage: + execute_all_stages = False + params['stage'] = stage + if custom_revision: + params['customRevision'] = custom_revision + params['executeAllStages'] = 'true' if execute_all_stages else 'false' + if bamboo_variables: + for key, value in bamboo_variables.items(): + params['bamboo.variable.{}'.format(key)] = value - def reports(self, max_results=25): - params = {'max-results': max_results} - return self._get_generator(self.resource_url('chart/reports'), elements_key='reports', element_key='report', - params=params) + return self.post(self.resource_url(resource), params=params, headers=headers) - def chart(self, report_key, build_keys, group_by_period, date_filter=None, date_from=None, date_to=None, - width=None, height=None, start_index=9, max_results=25): - params = {'reportKey': report_key, 'buildKeys': build_keys, 'groupByPeriod': group_by_period, - 'start-index': start_index, 'max-results': max_results} - if date_filter: - params['dateFilter'] = date_filter - if date_filter == 'RANGE': - params['dateFrom'] = date_from - params['dateTo'] = date_to - if width: - params['width'] = width - if height: - params['height'] = height - return self.get(self.resource_url('chart'), params=params) + """ Comments & Labels """ def comments(self, project_key, plan_key, build_number, start_index=0, max_results=25): 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): resource = "result/{}-{}-{}/label/{}".format(project_key, plan_key, build_number, label) return self.delete(self.resource_url(resource)) - def server_info(self): - return self.get(self.resource_url('info')) - - def agent_status(self): - return self.get(self.resource_url('agent')) - - def activity(self): - return self.get('build/admin/ajax/getDashboardSummary.action') - - def deployment_project(self, project_id): - resource = 'deploy/project/{}'.format(project_id) - return self.get(self.resource_url(resource)) + """ Deployments """ def deployment_projects(self): resource = 'deploy/project/all' for project in self.get(self.resource_url(resource)): yield project + def deployment_project(self, project_id): + resource = 'deploy/project/{}'.format(project_id) + return self.get(self.resource_url(resource)) + def deployment_environment_results(self, env_id, expand=None, max_results=25): resource = 'deploy/environment/{environmentId}/results'.format(environmentId=env_id) params = {'max-result': max_results, 'start-index': 0} @@ -330,105 +392,7 @@ def deployment_dashboard(self, project_id=None): resource = 'deploy/dashboard/{}'.format(project_id) if project_id else 'deploy/dashboard' return self.get(self.resource_url(resource)) - def search_branches(self, plan_key, include_default_branch=True, max_results=25): - params = { - 'max-result': max_results, - 'start-index': 0, - 'masterPlanKey': plan_key, - 'includeMasterBranch': include_default_branch - } - size = 1 - while params['start-index'] < size: - results = self.get(self.resource_url('search/branches'), params=params) - size = results['size'] - for r in results['searchResults']: - yield r - params['start-index'] += results['max-result'] - - def plan_branches(self, plan_key, expand=None, favourite=False, clover_enabled=False, max_results=25): - """api/1.0/plan/{projectKey}-{buildKey}/branch""" - resource = 'plan/{}/branch'.format(plan_key) - return self.base_list_call(resource, expand, favourite, clover_enabled, max_results, - elements_key='branches', element_key='branch') - - def create_branch(self, plan_key, branch_name, vcs_branch=None, enabled=False, cleanup_enabled=False): - """ - Method for creating branch for a specified plan. - You can use vcsBranch query param to define which vcsBranch should newly created branch use. - If not specified it will not override vcsBranch from the main plan. - - :param plan_key: str TST-BLD - :param branch_name: str new-shiny-branch - :param vcs_branch: str feature/new-shiny-branch, /refs/heads/new-shiny-branch - :param enabled: bool - :param cleanup_enabled: bool - :return: PUT request - """ - resource = 'plan/{plan_key}/branch/{branch_name}'.format(plan_key=plan_key, branch_name=branch_name) - params = {} - if vcs_branch: - params = dict(vcsBranch=vcs_branch, - enabled='true' if enabled else 'false', - cleanupEnabled='true' if cleanup_enabled else 'false') - return self.put(self.resource_url(resource), params=params) - - def get_branch_info(self, plan_key, branch_name): - """ - Get information about a plan branch - :param plan_key: - :param branch_name: - :return: - """ - resource = 'plan/{plan_key}/branch/{branch_name}'.format(plan_key=plan_key, branch_name=branch_name) - return self.get(self.resource_url(resource)) - - def enable_plan(self, plan_key): - """ - Enable plan. - :param plan_key: str TST-BLD - :return: POST request - """ - resource = 'plan/{plan_key}/enable'.format(plan_key=plan_key) - return self.post(self.resource_url(resource)) - - def execute_build(self, plan_key, stage=None, execute_all_stages=True, custom_revision=None, **bamboo_variables): - """ - Fire build execution for specified plan. - !IMPORTANT! NOTE: for some reason, this method always execute all stages - :param plan_key: str TST-BLD - :param stage: str stage-name - :param execute_all_stages: bool - :param custom_revision: str revisionName - :param bamboo_variables: dict {variable=value} - :return: POST request - """ - headers = self.form_token_headers - resource = 'queue/{plan_key}'.format(plan_key=plan_key) - params = {} - if stage: - execute_all_stages = False - params['stage'] = stage - if custom_revision: - params['customRevision'] = custom_revision - params['executeAllStages'] = 'true' if execute_all_stages else 'false' - if bamboo_variables: - for key, value in bamboo_variables.items(): - params['bamboo.variable.{}'.format(key)] = value - - return self.post(self.resource_url(resource), params=params, headers=headers) - - def health_check(self): - """ - Get health status - https://confluence.atlassian.com/jirakb/how-to-retrieve-health-check-results-using-rest-api-867195158.html - :return: - """ - # check as Troubleshooting & Support Tools Plugin - response = self.get('rest/troubleshooting/1.0/check/') - if not response: - # check as support tools - response = self.get('rest/supportHealthCheck/1.0/check/') - return response + """ Users & Groups """ def get_users_in_global_permissions(self, start=0, limit=25): """ @@ -486,7 +450,7 @@ def add_users_into_group(self, group_name, users): url = 'rest/api/latest/admin/groups/{}/add-users'.format(group_name) return self.post(url, data=users) - def remove_users_into_group(self, group_name, users): + def remove_users_from_group(self, group_name, users): """ Remove multiple users from a group. The list of usernames should be passed as request body. @@ -540,6 +504,59 @@ def get_build_queue(self, expand='queuedBuilds'): params = {'expand': expand} return self.get('rest/api/latest/queue', params=params) + """Other actions""" + + def server_info(self): + return self.get(self.resource_url('info')) + + def agent_status(self): + return self.get(self.resource_url('agent')) + + def activity(self): + return self.get('build/admin/ajax/getDashboardSummary.action') + + def get_custom_expiry(self, limit=25): + """ + Get list of all plans where user has admin permission and which override global expiry settings. + If global expiry is not enabled it returns empty response. + :param limit: + """ + url = "rest/api/latest/admin/expiry/custom/plan?limit={}".format(limit) + return self.get(url) + + def reports(self, max_results=25): + params = {'max-results': max_results} + return self._get_generator(self.resource_url('chart/reports'), elements_key='reports', element_key='report', + params=params) + + def chart(self, report_key, build_keys, group_by_period, date_filter=None, date_from=None, date_to=None, + width=None, height=None, start_index=9, max_results=25): + params = {'reportKey': report_key, 'buildKeys': build_keys, 'groupByPeriod': group_by_period, + 'start-index': start_index, 'max-results': max_results} + if date_filter: + params['dateFilter'] = date_filter + if date_filter == 'RANGE': + params['dateFrom'] = date_from + params['dateTo'] = date_to + if width: + params['width'] = width + if height: + params['height'] = height + return self.get(self.resource_url('chart'), params=params) + + def health_check(self): + """ + Get health status + https://confluence.atlassian.com/jirakb/how-to-retrieve-health-check-results-using-rest-api-867195158.html + :return: + """ + # check as Troubleshooting & Support Tools Plugin + response = self.get('rest/troubleshooting/1.0/check/') + if not response: + # check as support tools + response = self.get('rest/supportHealthCheck/1.0/check/') + return response + def upload_plugin(self, plugin_path): """ Provide plugin path for upload into Jira e.g. useful for auto deploy diff --git a/docs/bamboo.rst b/docs/bamboo.rst new file mode 100644 index 000000000..82c089b79 --- /dev/null +++ b/docs/bamboo.rst @@ -0,0 +1,180 @@ +Confluence module +================= + +Projects & Plans +------------- + +.. code-block:: python + + # Get all Projects + projects(expand=None, favourite=False, clover_enabled=False, max_results=25) + + # Get a single project by the key + project(project_key, expand=None, favourite=False, clover_enabled=False) + + # Get all build plans in a project + project_plans(project_key) + + # Get all build plans + plans(expand=None, favourite=False, clover_enabled=False, start_index=0, max_results=25) + + # Get information about plan build directory + plan_directory_info(plan_key) + + # Delete a plan (or a plan branch) + delete_plan(plan_key) + + # Enable plan + enable_plan(plan_key) + +Branches +------------- + +.. code-block:: python + + # Search Branches + search_branches(plan_key, include_default_branch=True, max_results=25) + + # Get all plan Branches + plan_branches(plan_key, expand=None, favourite=False, clover_enabled=False, max_results=25) + + # Get branch information + get_branch_info(plan_key, branch_name) + + # Create new branch (vcs or simple) + create_branch(plan_key, branch_name, vcs_branch=None, enabled=False, cleanup_enabled=False) + +Build results +------------- + +.. code-block:: python + + # Get build results (Scalable from a single result to all build results) + results(project_key=None, plan_key=None, job_key=None, build_number=None, expand=None, favourite=False, + clover_enabled=False, issue_key=None, label=None, start_index=0, max_results=25, include_all_states=False) + + # Get latest build results + latest_results(expand=None, favourite=False, clover_enabled=False, label=None, issue_key=None, + start_index=0, max_results=25, include_all_states=False) + + # Get latest build results for the project + project_latest_results(project_key, expand=None, favourite=False, clover_enabled=False, label=None, + issue_key=None, start_index=0, max_results=25, include_all_states=False) + + # Get build results for a single plan + plan_results(project_key, plan_key, expand=None, favourite=False, clover_enabled=False, label=None, + issue_key=None, start_index=0, max_results=25, include_all_states=False) + + # Get a single build result + build_result(build_key, expand=None, include_all_states=False) + + # Get latest results for a plan + build_latest_result(plan_key, expand=None, include_all_states=False) + + # Delete build result + delete_build_result(build_key) + + # Execute build + execute_build(plan_key, stage=None, execute_all_stages=True, custom_revision=None, **bamboo_variables) + +Comments & Labels +------------- + +.. code-block:: python + + # Get comments for the build + comments(project_key, plan_key, build_number, start_index=0, max_results=25) + + # Make a comment + create_comment(project_key, plan_key, build_number, comment, author=None) + + # Get labels for a build + labels(project_key, plan_key, build_number, start_index=0, max_results=25) + + # Create a label + create_label(project_key, plan_key, build_number, label) + + # Delete a label + delete_label(project_key, plan_key, build_number, label) + +Deployments +------------- + +.. code-block:: python + + # Get deployment projects + deployment_projects() + + # Get deployments for a single project + deployment_project(project_id) + + # Get deployment environment results + deployment_environment_results(env_id, expand=None, max_results=25) + + # Get deployment dashboard + deployment_dashboard(project_id=None) + +Users & Groups +------------- + +.. code-block:: python + + # Get users in global permissons + get_users_in_global_permissions(start=0, limit=25) + + # Get Groups + get_groups(start=0, limit=25) + + # Create Group + create_group(group_name) + + # Delete Group + delete_group(group_name) + + # Add users into Group + add_users_into_group(group_name, users) + + # Remove users from Group + remove_users_from_group(group_name, users) + + # Get users from Group + get_users_from_group(group_name, filter_users=None, start=0, limit=25) + + # Get users without Group + get_users_not_in_group(group_name, filter_users='', start=0, limit=25) + + + +Other actions +------------- + +.. code-block:: python + + # Get build queue + get_build_queue(expand='queuedBuilds') + + # Get server information + server_info() + + # Get agents statuses + agent_status() + + # Get activity + activity() + + # Get custom expiry + get_custom_expiry(limit=25) + + # Get reports + reports(max_results=25) + + # Get charts + hart(report_key, build_keys, group_by_period, date_filter=None, date_from=None, date_to=None, + width=None, height=None, start_index=9, max_results=25) + + # Health check + health_check() + + # Upload plugin + upload_plugin(plugin_path) + diff --git a/examples/bamboo-add-plan-branch.py b/examples/bamboo-add-plan-branch.py new file mode 100644 index 000000000..59e02e281 --- /dev/null +++ b/examples/bamboo-add-plan-branch.py @@ -0,0 +1,25 @@ +from atlassian import Bamboo +import argparse + +bamboo = Bamboo(url="https://", username="", password="") + + +def create_plan_branch(plan, vcs_branch): + bamboo_branch = vcs_branch.replace("/", "-") + return bamboo.create_branch( + plan, bamboo_branch, vcs_branch=vcs_branch, enabled=True + ) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--plan") + parser.add_argument("--vcs_branch") + args = parser.parse_args() + + branch = create_plan_branch(plan=args.plan, vcs_branch=args.vcs_branch) + print(branch.get("key") or branch) + + +if __name__ == "__main__": + main()