From c69e4f4ac9a407f8b0f45c963e4096b76123a4a7 Mon Sep 17 00:00:00 2001 From: paytroff Date: Thu, 9 Dec 2021 21:02:58 +0100 Subject: [PATCH] add module gitlab_branch (#3795) * add module gitlab_branch * Update plugins/modules/source_control/gitlab/gitlab_branch.py Co-authored-by: Felix Fontein * Update plugins/modules/source_control/gitlab/gitlab_branch.py Co-authored-by: Felix Fontein * Update plugins/modules/source_control/gitlab/gitlab_branch.py Co-authored-by: Felix Fontein * Update gitlab_branch.py * Update gitlab_branch.py * Update gitlab_branch.py * add integration tests * Update BOTMETA.yml * Update gitlab_branch.py * Update tests/integration/targets/gitlab_branch/aliases Co-authored-by: Felix Fontein * Update main.yml Co-authored-by: paitrault Co-authored-by: Felix Fontein --- .github/BOTMETA.yml | 2 + plugins/modules/gitlab_branch.py | 1 + .../source_control/gitlab/gitlab_branch.py | 185 ++++++++++++++++++ .../integration/targets/gitlab_branch/aliases | 2 + .../targets/gitlab_branch/defaults/main.yml | 2 + .../targets/gitlab_branch/tasks/main.yml | 64 ++++++ 6 files changed, 256 insertions(+) create mode 120000 plugins/modules/gitlab_branch.py create mode 100644 plugins/modules/source_control/gitlab/gitlab_branch.py create mode 100644 tests/integration/targets/gitlab_branch/aliases create mode 100644 tests/integration/targets/gitlab_branch/defaults/main.yml create mode 100644 tests/integration/targets/gitlab_branch/tasks/main.yml diff --git a/.github/BOTMETA.yml b/.github/BOTMETA.yml index eb2548c351..4ae85b214d 100644 --- a/.github/BOTMETA.yml +++ b/.github/BOTMETA.yml @@ -951,6 +951,8 @@ files: maintainers: SamyCoenen $modules/source_control/gitlab/gitlab_user.py: maintainers: LennertMertens stgrace + $modules/source_control/gitlab/gitlab_branch.py: + maintainers: paytroff $modules/source_control/hg.py: maintainers: yeukhon $modules/storage/emc/emc_vnx_sg_member.py: diff --git a/plugins/modules/gitlab_branch.py b/plugins/modules/gitlab_branch.py new file mode 120000 index 0000000000..3049ddb6fc --- /dev/null +++ b/plugins/modules/gitlab_branch.py @@ -0,0 +1 @@ +source_control/gitlab/gitlab_branch.py \ No newline at end of file diff --git a/plugins/modules/source_control/gitlab/gitlab_branch.py b/plugins/modules/source_control/gitlab/gitlab_branch.py new file mode 100644 index 0000000000..4e7eea6bc7 --- /dev/null +++ b/plugins/modules/source_control/gitlab/gitlab_branch.py @@ -0,0 +1,185 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2021, Werner Dijkerman (ikben@werner-dijkerman.nl) +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = ''' +module: gitlab_branch +short_description: Create or delete a branch +version_added: 4.2.0 +description: + - This module allows to create or delete branches. +author: + - paytroff (@paytroff) +requirements: + - python >= 2.7 + - python-gitlab >= 2.3.0 +extends_documentation_fragment: +- community.general.auth_basic + +options: + state: + description: + - Create or delete branch. + default: present + type: str + choices: ["present", "absent"] + api_token: + description: + - GitLab access token with API permissions. + required: true + type: str + project: + description: + - The path or name of the project. + required: true + type: str + branch: + description: + - The name of the branch that needs to be created. + required: true + type: str + ref_branch: + description: + - Reference branch to create from. + - This must be specified if I(state=present). + type: str +''' + + +EXAMPLES = ''' +- name: Create branch branch2 from main + community.general.gitlab_branch: + api_url: https://gitlab.com + api_token: secret_access_token + project: "group1/project1" + branch: branch2 + ref_branch: main + state: present + +- name: Delete branch branch2 + community.general.gitlab_branch: + api_url: https://gitlab.com + api_token: secret_access_token + project: "group1/project1" + branch: branch2 + state: absent + +''' + +RETURN = ''' +''' + +import traceback + +from ansible.module_utils.basic import AnsibleModule, missing_required_lib +from ansible.module_utils.api import basic_auth_argument_spec +from distutils.version import LooseVersion + +GITLAB_IMP_ERR = None +try: + import gitlab + HAS_GITLAB_PACKAGE = True +except Exception: + GITLAB_IMP_ERR = traceback.format_exc() + HAS_GITLAB_PACKAGE = False + +from ansible_collections.community.general.plugins.module_utils.gitlab import gitlab_authentication + + +class GitlabBranch(object): + + def __init__(self, module, project, gitlab_instance): + self.repo = gitlab_instance + self._module = module + self.project = self.get_project(project) + + def get_project(self, project): + try: + return self.repo.projects.get(project) + except Exception as e: + return False + + def get_branch(self, branch): + try: + return self.project.branches.get(branch) + except Exception as e: + return False + + def create_branch(self, branch, ref_branch): + return self.project.branches.create({'branch': branch, 'ref': ref_branch}) + + def delete_branch(self, branch): + branch.unprotect() + return branch.delete() + + +def main(): + argument_spec = basic_auth_argument_spec() + argument_spec.update( + api_token=dict(type='str', required=True, no_log=True), + project=dict(type='str', required=True), + branch=dict(type='str', required=True), + ref_branch=dict(type='str', required=False), + state=dict(type='str', default="present", choices=["absent", "present"]), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + mutually_exclusive=[ + ['api_username', 'api_token'], + ['api_password', 'api_token'], + ], + required_together=[ + ['api_username', 'api_password'], + ], + required_one_of=[ + ['api_username', 'api_token'] + ], + required_if=[ + ['state', 'present', ['ref_branch'], True], + ], + supports_check_mode=False + ) + + project = module.params['project'] + branch = module.params['branch'] + ref_branch = module.params['ref_branch'] + state = module.params['state'] + + if not HAS_GITLAB_PACKAGE: + module.fail_json(msg=missing_required_lib("python-gitlab"), exception=GITLAB_IMP_ERR) + + gitlab_version = gitlab.__version__ + if LooseVersion(gitlab_version) < LooseVersion('2.3.0'): + module.fail_json(msg="community.general.gitlab_proteched_branch requires python-gitlab Python module >= 2.3.0 (installed version: [%s])." + " Please upgrade python-gitlab to version 2.3.0 or above." % gitlab_version) + + gitlab_instance = gitlab_authentication(module) + this_gitlab = GitlabBranch(module=module, project=project, gitlab_instance=gitlab_instance) + + this_branch = this_gitlab.get_branch(branch) + + if not this_branch and state == "present": + r_branch = this_gitlab.get_branch(ref_branch) + if not r_branch: + module.fail_json(msg="Ref branch {b} not exist.".format(b=ref_branch)) + this_gitlab.create_branch(branch, ref_branch) + module.exit_json(changed=True, msg="Created the branch {b}.".format(b=branch)) + elif this_branch and state == "present": + module.exit_json(changed=False, msg="Branch {b} already exist".format(b=branch)) + elif this_branch and state == "absent": + try: + this_gitlab.delete_branch(this_branch) + module.exit_json(changed=True, msg="Branch {b} deleted.".format(b=branch)) + except Exception as e: + module.fail_json(msg="Error delete branch.", exception=traceback.format_exc()) + else: + module.exit_json(changed=False, msg="No changes are needed.") + + +if __name__ == '__main__': + main() diff --git a/tests/integration/targets/gitlab_branch/aliases b/tests/integration/targets/gitlab_branch/aliases new file mode 100644 index 0000000000..268da2c702 --- /dev/null +++ b/tests/integration/targets/gitlab_branch/aliases @@ -0,0 +1,2 @@ +shippable/posix/group1 +disabled diff --git a/tests/integration/targets/gitlab_branch/defaults/main.yml b/tests/integration/targets/gitlab_branch/defaults/main.yml new file mode 100644 index 0000000000..ffe0d8dc47 --- /dev/null +++ b/tests/integration/targets/gitlab_branch/defaults/main.yml @@ -0,0 +1,2 @@ +gitlab_branch: ansible_test_branch +gitlab_project_name: ansible_test_project \ No newline at end of file diff --git a/tests/integration/targets/gitlab_branch/tasks/main.yml b/tests/integration/targets/gitlab_branch/tasks/main.yml new file mode 100644 index 0000000000..78fe772012 --- /dev/null +++ b/tests/integration/targets/gitlab_branch/tasks/main.yml @@ -0,0 +1,64 @@ +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + +- name: Install required libs + pip: + name: python-gitlab + state: present + +- name: Create {{ gitlab_project_name }} + gitlab_project: + server_url: "{{ gitlab_host }}" + validate_certs: False + login_token: "{{ gitlab_login_token }}" + name: "{{ gitlab_project_name }}" + initialize_with_readme: True + state: present + +- name: Create branch {{ gitlab_branch }} + community.general.gitlab_branch: + api_url: https://gitlab.com + api_token: secret_access_token + project: "{{ gitlab_project_name }}" + branch: "{{ gitlab_branch }}" + ref_branch: main + state: present + +- name: Create branch {{ gitlab_branch }} ( Idempotency test ) + community.general.gitlab_branch: + api_url: https://gitlab.com + api_token: secret_access_token + project: "{{ gitlab_project_name }}" + branch: "{{ gitlab_branch }}" + ref_branch: main + state: present + register: create_branch + +- name: Test module is idempotent + assert: + that: + - create_branch is not changed + +- name: Cleanup branch {{ gitlab_branch }} + community.general.gitlab_branch: + api_url: https://gitlab.com + api_token: secret_access_token + project: "{{ gitlab_project_name }}" + branch: "{{ gitlab_branch }}" + state: absent + register: delete_branch + +- name: Test module is idempotent + assert: + that: + - delete_branch is changed + +- name: Clean up {{ gitlab_project_name }} + gitlab_project: + server_url: "{{ gitlab_host }}" + validate_certs: False + login_token: "{{ gitlab_login_token }}" + name: "{{ gitlab_project_name }}" + state: absent