diff --git a/gitlab_submodule/objects.py b/gitlab_submodule/objects.py index bf0e459..7df66d9 100644 --- a/gitlab_submodule/objects.py +++ b/gitlab_submodule/objects.py @@ -12,15 +12,27 @@ def __init__(self, parent_ref: str, name: str, path: str, - url: str): + url: str, + branch: Optional[str]=None, + ignore: Optional[str]=None, + update: Optional[str]=None, + recurse: bool=False, + shallow: bool=False): + self.parent_project = parent_project self.parent_ref = parent_ref self.name = name self.path = path self.url = url + self.branch = branch + self.ignore = ignore + self.update = update + self.recurse = recurse + self.shallow = shallow def keys(self): - return {'parent_project', 'parent_ref', 'name', 'path', 'url'} + return {'parent_project', 'parent_ref', 'name', 'path', 'url', + 'update', 'branch', 'ignore', 'shallow', 'recurse'} def __getitem__(self, key): if key in self.keys(): @@ -31,24 +43,29 @@ def __getitem__(self, key): def __str__(self): keys = sorted(self.keys()) class_part = f"" - def to_str(key): if isinstance(self[key], str): return f"'{self[key]}'" else: return str(self[key]) - attributes = [f"'{key}': {to_str(key)}" for key in keys] return class_part + ' => {' + ', '.join(attributes) + '}' def __repr__(self): - return '{} ({}, {}, {}, {}, {})'.format( + return '{} ({})'.format( self.__class__.__name__, - repr(self.parent_project), - f"'{self.parent_ref}'", - f"'{self.name}'", - f"'{self.path}'", - f"'{self.url}'", + ", ".join(( + repr(self.parent_project), + repr(self.parent_ref), + repr(self.name), + repr(self.path), + repr(self.url), + repr(self.branch), + repr(self.ignore), + repr(self.update), + repr(self.recurse), + repr(self.shallow), + )) ) diff --git a/gitlab_submodule/read_gitmodules.py b/gitlab_submodule/read_gitmodules.py index e1548fd..a752435 100644 --- a/gitlab_submodule/read_gitmodules.py +++ b/gitlab_submodule/read_gitmodules.py @@ -1,5 +1,6 @@ +import configparser import re -from typing import Iterable, List, Optional, Tuple +from typing import Iterable, List, Optional, Union from gitlab.v4.objects import Project @@ -18,14 +19,12 @@ def iterate_project_submodules( gitmodules_file_content = _get_gitmodules_file_content(project, ref) if not gitmodules_file_content: return [] - for (name, url, path) in _read_gitmodules_file_content( + for kwargs in _read_gitmodules_file_content( gitmodules_file_content): yield Submodule( parent_project=project, parent_ref=ref if ref else project.default_branch, - name=name, - url=url, - path=path) + **kwargs) def _get_gitmodules_file_content(project: Project, @@ -38,17 +37,24 @@ def _get_gitmodules_file_content(project: Project, except Exception: return None - def _read_gitmodules_file_content( - gitmodules_file_content: str) -> Iterable[Tuple[str, str, str]]: - """Some basic regex extractions to parse content of .gitmodules file - """ - name_regex = r'\[submodule "([a-zA-Z0-9\.\-/_]+)"\]' - path_regex = r'path ?= ?([a-zA-Z0-9\.\-/_]+)' - url_regex = r'url ?= ?([a-zA-Z0-9\.\-/_:@]+)' - names = re.findall(name_regex, gitmodules_file_content) - paths = re.findall(path_regex, gitmodules_file_content) - urls = re.findall(url_regex, gitmodules_file_content) - if not (len(names) == len(paths) == len(urls)): - raise RuntimeError('Failed parsing the .gitmodules content') - return zip(names, urls, paths) + gitmodules_file_content: str) -> Iterable[dict[str, Union[None, bool, str]]]: + """Parses contents of .gitmodule file using configparser""" + config = configparser.ConfigParser() + config.optionxform = str + config.read_string(gitmodules_file_content) + stropts = ('branch', 'ignore', 'update') + boolopts = ('recurse', 'shallow') + name_regex = r'submodule "([a-zA-Z0-9\.\-/_]+)"' + for section in config.sections(): + try: + kwargs = { + 'name': re.match(name_regex, section).group(1), + 'path': config.get(section, 'path'), + 'url': config.get(section, 'url') + } + except (AttributeError, KeyError): + raise RuntimeError('Failed parsing the .gitmodules contnet') + kwargs.update((opt, config.get(section, opt, fallback=None)) for opt in stropts) + kwargs.update((opt, config.getboolean(section, opt, fallback=False)) for opt in boolopts) + yield kwargs diff --git a/tests/test_objects.py b/tests/test_objects.py index cf9188e..ae227da 100644 --- a/tests/test_objects.py +++ b/tests/test_objects.py @@ -38,12 +38,15 @@ def test_Submodule_as_dict(self): parent_ref='main', name='test_submodule', url='git@gitlab.com:test/submodule', - path='include/test_submodule' + path='include/test_submodule', + update='rebase' ) submodule_dict = dict(submodule) - self.assertEqual(len(submodule_dict.keys()), 5) + self.assertEqual(len(submodule_dict.keys()), 10) self.assertEqual(submodule_dict['parent_ref'], 'main') self.assertEqual(submodule_dict['name'], 'test_submodule') + self.assertEqual(submodule_dict['update'], 'rebase') + self.assertEqual(submodule_dict['branch'], None) def test_Submodule_str(self): mock_project = DictMock() @@ -57,11 +60,13 @@ def test_Submodule_str(self): ) self.assertEqual( " => {" + "'branch': None, 'ignore': None, " "'name': 'test_submodule', " "'parent_project': => {'id': 123456789}, " "'parent_ref': 'main', " "'path': 'include/test_submodule', " - "'url': 'git@gitlab.com:test/submodule'}", + "'recurse': False, 'shallow': False, " + "'update': None, 'url': 'git@gitlab.com:test/submodule'}", str(submodule) ) @@ -73,11 +78,13 @@ def test_Submodule_repr(self): parent_ref='main', name='test_submodule', url='git@gitlab.com:test/submodule', - path='include/test_submodule' + path='include/test_submodule', + branch='development' ) self.assertEqual( "Submodule ({'id': 123456789}, 'main', 'test_submodule'," - " 'include/test_submodule', 'git@gitlab.com:test/submodule')", + " 'include/test_submodule', 'git@gitlab.com:test/submodule'," + " 'development', None, None, False, False)", repr(submodule) ) @@ -157,7 +164,8 @@ def test_Subproject_str(self): parent_ref='main', name='test_submodule', url='git@gitlab.com:test/submodule', - path='include/test_submodule' + path='include/test_submodule', + branch = "development" ) mock_project = DictMock() mock_project.name = 'project' @@ -175,11 +183,13 @@ def test_Subproject_str(self): ) self.assertEqual( " 'submodule': => {" + "'branch': 'development', 'ignore': None, " "'name': 'test_submodule', " "'parent_project': => {'id': '123456789'}, " "'parent_ref': 'main', " "'path': 'include/test_submodule', " - "'url': 'git@gitlab.com:test/submodule'},", + "'recurse': False, 'shallow': False, " + "'update': None, 'url': 'git@gitlab.com:test/submodule'},", str_lines[1] ) self.assertEqual( @@ -201,7 +211,8 @@ def test_Subproject_repr(self): parent_ref='main', name='test_submodule', url='git@gitlab.com:test/submodule', - path='include/test_submodule' + path='include/test_submodule', + branch = "development" ) mock_project = DictMock() mock_project.name = 'project' @@ -218,9 +229,9 @@ def test_Subproject_repr(self): str_lines[0] ) self.assertEqual( - " Submodule ({'id': '123456789'}, 'main', " - "'test_submodule', 'include/test_submodule', " - "'git@gitlab.com:test/submodule'),", + " Submodule ({'id': '123456789'}, 'main', 'test_submodule'," + " 'include/test_submodule', 'git@gitlab.com:test/submodule'," + " 'development', None, None, False, False),", str_lines[1] ) self.assertEqual(