# Copyright (C) 2016-2019 Codethink Limited # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import re import urllib.parse try: import gitlab except ImportError: gitlab = None class MissingGitlabModuleError(Exception): pass class Gitlab(object): '''Run commands on a GitLab instance. This uses the python wrapper around the GitLab API. Use of the API requires the private token of a user with master access to the targetted group. ''' def __init__(self, host, token): if gitlab: url = "http://" + host self.gl = gitlab.Gitlab(url, token) else: raise MissingGitlabModuleError('gitlab module missing\n' '\tpython-gitlab is required with GitLab as the git server') def find_project(self, repo_path): return self.gl.projects.get(repo_path) def has_project(self, repo_path): try: self.find_project(repo_path) return True except gitlab.GitlabGetError: return False def create_project(self, repo_path): path_comps = repo_path.split('/') if len(path_comps) < 2: raise ValueError('cannot create GitLab project outside a group') # Create hierarchy of groups as necessary parent_group = None for group_name in path_comps[:-1]: if parent_group is None: group_path = group_name else: group_path = parent_group.full_path + '/' + group_name try: group = self.gl.groups.get(group_path) except gitlab.GitlabGetError as e: if e.response_code != 404: raise data = {'name': group_name, 'path': group_name} if parent_group is not None: data['parent_id'] = parent_group.id group = self.gl.groups.create(data) parent_group = group project = { 'name': path_comps[-1], 'public': True, 'merge_requests_enabled': False, 'namespace_id': group.id, } self.gl.projects.create(project) def list_projects(self): '''List projects on a GitLab instance.''' return [x.path_with_namespace for x in self.gl.projects.list()] def get_project_url(self, protocol, project_path): '''Return the clone url for a GitLab project. Depending on the protocol specified, will return a suitable clone url. If 'ssh', a url in the format 'git@host:group/project.git' will be returned. If 'http' or 'https', the http_url_to_repo from the GitLab API is split with urlparse into its constituent parts: the protocol (http by default), the host, and the path ('group/project.git'). This is then rejoined, replacing the protocol with what is specified. The resulting format matching 'http(s)://host/group/project.git'. ''' project = self.find_project(project_path) if protocol == 'ssh': return project.ssh_url_to_repo elif protocol in ('http', 'https'): split = urllib.parse.urlsplit(project.http_url_to_repo) return urllib.parse.urlunsplit(( protocol, split.netloc, split.path, '', ''))