|
1 | 1 | import logging |
2 | 2 | import re |
3 | 3 | from posixpath import join, normpath |
4 | | -from typing import List, Optional, Union |
| 4 | +from typing import Optional, Tuple |
5 | 5 |
|
6 | 6 | from gitlab.exceptions import GitlabGetError, GitlabHttpError |
7 | 7 | from gitlab.v4.objects import Project, ProjectManager |
8 | 8 | from giturlparse import GitUrlParsed, parse |
9 | 9 |
|
10 | 10 | from gitlab_submodule.objects import Submodule |
| 11 | +from gitlab_submodule.project_manager_utils import (OneOrManyClients, |
| 12 | + map_domain_to_clients) |
11 | 13 | from gitlab_submodule.string_utils import lstrip, rstrip |
12 | 14 |
|
13 | 15 | logger = logging.getLogger(__name__) |
14 | 16 |
|
15 | 17 |
|
16 | | -def submodule_to_project( |
17 | | - submodule: Submodule, |
18 | | - project_manager: ProjectManager, |
19 | | - self_managed_gitlab_host: Optional[Union[str, List[str]]] = None |
20 | | -) -> Optional[Project]: |
21 | | - submodule_project_path_with_namespace = \ |
22 | | - _submodule_url_to_path_with_namespace(submodule.url, |
23 | | - submodule.parent_project, |
24 | | - self_managed_gitlab_host) |
25 | | - if not submodule_project_path_with_namespace: |
26 | | - return None |
27 | | - try: |
28 | | - submodule_project = project_manager.get( |
29 | | - submodule_project_path_with_namespace) |
30 | | - except (GitlabGetError, GitlabHttpError): |
31 | | - # Repo doesn't actually exist (possible because you can modify |
32 | | - # .gitmodules without using `git submodule add`) |
33 | | - raise FileNotFoundError( |
34 | | - 'No repo found at url "{}" for submodule at path "{}" - Check if ' |
35 | | - 'the repo was deleted.'.format(submodule.url, submodule.path)) |
36 | | - return submodule_project |
| 18 | +def host_url_to_domain(url: str) -> str: |
| 19 | + return url.split("//")[1].rstrip("/") |
| 20 | + |
37 | 21 |
|
| 22 | +def match_submodule_to_client_and_format_project_path( |
| 23 | + submodule: Submodule, |
| 24 | + gls: OneOrManyClients |
| 25 | +) -> Optional[Tuple[ProjectManager, str]]: |
| 26 | + url = submodule.url |
38 | 27 |
|
39 | | -def _submodule_url_to_path_with_namespace( |
40 | | - url: str, |
41 | | - parent_project: Project, |
42 | | - self_managed_gitlab_host: Optional[Union[str, List[str]]] = None |
43 | | -) -> Optional[str]: |
44 | | - """Returns a path pointing to a Gitlab project, or None if the submodule |
45 | | - is hosted elsewhere |
46 | | - """ |
47 | 28 | # check if the submodule url is a relative path to the project path |
48 | 29 | if url.startswith('./') or url.startswith('../'): |
49 | 30 | # we build the path of the submodule project using the path of |
50 | 31 | # the current project |
51 | 32 | url = rstrip(url, '.git') |
52 | | - path_with_namespace = normpath( |
53 | | - join(parent_project.path_with_namespace, url)) |
54 | | - return path_with_namespace |
| 33 | + path_with_namespace = normpath(join( |
| 34 | + submodule.parent_project.path_with_namespace, |
| 35 | + url |
| 36 | + )) |
| 37 | + client: ProjectManager = submodule.parent_project.manager |
| 38 | + return client, path_with_namespace |
55 | 39 |
|
| 40 | + # If URL is not relative: try parsing it |
56 | 41 | parsed: GitUrlParsed = parse(url) |
57 | 42 | if not parsed.valid: |
58 | 43 | logger.warning(f'submodule git url does not seem to be valid: {url}') |
59 | 44 | return None |
60 | 45 |
|
61 | | - # even if the parent project is hosted on a self-managed gitlab host, |
62 | | - # it can still use submodules hosted on gitlab.com |
63 | | - gitlab_hosts = ['gitlab'] |
64 | | - if self_managed_gitlab_host: |
65 | | - if isinstance(self_managed_gitlab_host, str): |
66 | | - gitlab_hosts.append(self_managed_gitlab_host) |
67 | | - else: |
68 | | - gitlab_hosts.extend(self_managed_gitlab_host) |
| 46 | + url_to_client = map_domain_to_clients(gls) |
| 47 | + domain_to_client = { |
| 48 | + host_url_to_domain(_url): client |
| 49 | + for _url, client in url_to_client.items() |
| 50 | + } |
69 | 51 |
|
70 | | - # giturlparse.GitUrlParsed.platform is too permissive and will be set to |
71 | | - # 'gitlab' for some non-gitlab urls, for instance: |
72 | | - # https://opensource.ncsa.illinois.edu/bitbucket/scm/u3d/3dutilities.git |
73 | | - if (parsed.platform not in ('gitlab', 'base') |
74 | | - or not any([re.match(fr'^{host}(\.\w+)?$', parsed.host) |
75 | | - for host in gitlab_hosts])): |
| 52 | + matched_domain = [ |
| 53 | + domain for domain in domain_to_client |
| 54 | + if re.search("(^|[/@])" + domain, url) |
| 55 | + ] |
| 56 | + if len(matched_domain) == 0: |
76 | 57 | logger.warning(f'submodule git url is not hosted on gitlab: {url}') |
77 | 58 | return None |
| 59 | + elif len(matched_domain) > 1: |
| 60 | + raise ValueError(f"More than one of the provided Gitlab host domains " |
| 61 | + f"matches submodule url {url}") |
| 62 | + else: |
| 63 | + matched_domain = matched_domain[0] |
| 64 | + client = domain_to_client[matched_domain] |
78 | 65 |
|
79 | 66 | # Format to python-gitlab path_with_namespace: |
80 | 67 | # rewrite to https format then split by host and keep & cut the right part. |
81 | 68 | # I find it more robust than trying to rebuild the path from the different |
82 | 69 | # attributes of giturlparse.GitUrlParsed objects |
83 | 70 | https_url = parsed.url2https |
84 | | - path_with_namespace = https_url.split(parsed.host)[1] |
| 71 | + path_with_namespace = https_url.split(matched_domain)[1] |
85 | 72 | path_with_namespace = lstrip(path_with_namespace, '/') |
86 | 73 | path_with_namespace = rstrip(path_with_namespace, '.git') |
87 | | - return path_with_namespace |
| 74 | + return client, path_with_namespace |
| 75 | + |
| 76 | + |
| 77 | +def submodule_to_project( |
| 78 | + submodule: Submodule, |
| 79 | + gls: OneOrManyClients, |
| 80 | +) -> Optional[Project]: |
| 81 | + match = match_submodule_to_client_and_format_project_path( |
| 82 | + submodule=submodule, |
| 83 | + gls=gls |
| 84 | + ) |
| 85 | + if not match: |
| 86 | + return None |
| 87 | + try: |
| 88 | + client, submodule_project_path_with_namespace = match |
| 89 | + submodule_project = client.get(submodule_project_path_with_namespace) |
| 90 | + except (GitlabGetError, GitlabHttpError): |
| 91 | + # Repo doesn't actually exist (possible because you can modify |
| 92 | + # .gitmodules without using `git submodule add`) |
| 93 | + raise FileNotFoundError( |
| 94 | + 'No repo found at url "{}" for submodule at path "{}" - Check if ' |
| 95 | + 'the repo was deleted.'.format(submodule.url, submodule.path)) |
| 96 | + return submodule_project |
0 commit comments