summaryrefslogtreecommitdiff
path: root/lorrycontroller
diff options
context:
space:
mode:
authorBen Hutchings <ben.hutchings@codethink.co.uk>2020-05-04 17:36:58 +0100
committerBen Hutchings <ben.hutchings@codethink.co.uk>2020-06-01 15:28:07 +0100
commit85476070627f6478d236580fc3286f700e30c4ae (patch)
treef0ea1fb4a755bc1b019ae96cca6e542b83429cd5 /lorrycontroller
parentc85896d42da695fa27606a1f70106c5c4b053197 (diff)
downloadlorry-controller-85476070627f6478d236580fc3286f700e30c4ae.tar.gz
lorrycontroller.gitlab: Use and create subgroups as needed
The current code only uses top-level groups since GitLab did not originally support subgroups. For repositories with more than two path components, it joins multiple components together as the project name. However, subgroups were implemented in version 9.0, so we can use them and remove this mangling.
Diffstat (limited to 'lorrycontroller')
-rw-r--r--lorrycontroller/gitlab.py85
1 files changed, 29 insertions, 56 deletions
diff --git a/lorrycontroller/gitlab.py b/lorrycontroller/gitlab.py
index 48b161b..13becfe 100644
--- a/lorrycontroller/gitlab.py
+++ b/lorrycontroller/gitlab.py
@@ -16,7 +16,7 @@
import re
import urllib.parse
-import itertools
+
try:
import gitlab
except ImportError:
@@ -45,79 +45,52 @@ class Gitlab(object):
raise MissingGitlabModuleError('gitlab module missing\n'
'\tpython-gitlab is required with GitLab as the git server')
- def first(self, predicate, iterable):
- return next(filter(predicate, iterable))
-
- def split_and_unslashify_path(self, path):
- group, project = path.split('/', 1)
- return group, project.replace('/', '_')
-
def find_project(self, repo_path):
- group, project = self.split_and_unslashify_path(repo_path)
- predicate = lambda x: x.namespace['name'] == group and x.name == project
-
- return self.first(predicate, self.gl.projects.list(search=project))
+ return self.gl.projects.get(repo_path)
def has_project(self, repo_path):
try:
- return bool(self.find_project(repo_path))
- except StopIteration:
+ self.find_project(repo_path)
+ return True
+ except gitlab.GitlabGetError:
return False
def create_project(self, repo_path):
- # GitLab only supports one level of namespacing.
- group_name, project_name = self.split_and_unslashify_path(repo_path)
- group = None
- try:
- group = self.gl.groups.get(group_name)
- except gitlab.GitlabGetError as e:
- if e.response_code == 404:
- group = self.gl.groups.create(
- {'name': group_name, 'path': group_name})
+ 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:
- raise
+ 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': project_name,
+ 'name': path_comps[-1],
'public': True,
'merge_requests_enabled': False,
'namespace_id': group.id,
- # Set the original path in the description. We will use this to
- # work around lack of multi-level namespacing.
- 'description': 'original_path: %s' % repo_path
}
self.gl.projects.create(project)
- def try_get_original_path(self, project_description):
- match = re.search('original_path:\s(.*)', str(project_description))
- if match:
- return match.groups()[0]
-
- def suitable_path(self, project):
- '''Return a path for a downstream Lorry Controller instance to consume.
-
- Should the path that was lorried have contained more than one level of
- namespacing (more than one '/' within the repository path), then for
- GitLab to handle this, we replace any '/'s (remaining in the project
- name after extracting the group name) with underscores (_). To preserve
- the original path, we set the 'original_path' within the project
- description.
- This method will attempt to return 'original_path' if it was set,
- otherwise it will return the 'path_with_namespace', being of the format
- 'group_name/project_name', rather than 'group_name/project/name'.
- '''
- return (self.try_get_original_path(project.description) or
- project.path_with_namespace)
-
def list_projects(self):
- '''List projects on a GitLab instance.
-
- In attempt to handle GitLab's current lack of multi-level namespacing
- (see: https://gitlab.com/gitlab-org/gitlab-ce/issues/2772), return
- the 'original_path' stored in a project's description, if it exists.
- '''
+ '''List projects on a GitLab instance.'''
- return [self.suitable_path(x) for x in self.gl.projects.list()]
+ 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.