1313
1414import kaptan
1515
16- from libvcs .projects .git import GitRemote
16+ from libvcs ._internal .types import StrPath
17+ from libvcs .sync .git import GitRemote
1718
1819from . import exc
20+ from .types import ConfigDict
1921from .util import get_config_dir , update_dict
2022
2123log = logging .getLogger (__name__ )
@@ -45,7 +47,7 @@ def expand_dir(
4547 return _dir
4648
4749
48- def extract_repos (config : dict , cwd = pathlib .Path .cwd ()) -> list [dict ]:
50+ def extract_repos (config : dict , cwd = pathlib .Path .cwd ()) -> list [ConfigDict ]:
4951 """Return expanded configuration.
5052
5153 end-user configuration permit inline configuration shortcuts, expand to
@@ -62,11 +64,11 @@ def extract_repos(config: dict, cwd=pathlib.Path.cwd()) -> list[dict]:
6264 -------
6365 list : List of normalized repository information
6466 """
65- configs = []
67+ configs : list [ ConfigDict ] = []
6668 for directory , repos in config .items ():
6769 for repo , repo_data in repos .items ():
6870
69- conf = {}
71+ conf : ConfigDict = {}
7072
7173 """
7274 repo_name: http://myrepo.com/repo.git
@@ -94,18 +96,26 @@ def extract_repos(config: dict, cwd=pathlib.Path.cwd()) -> list[dict]:
9496 if "parent_dir" not in conf :
9597 conf ["parent_dir" ] = expand_dir (directory , cwd = cwd )
9698
97- # repo_dir -> dir in libvcs 0.12.0b25
98- if "repo_dir" in conf and "dir" not in conf :
99- conf ["dir" ] = conf .pop ("repo_dir" )
100-
10199 if "dir" not in conf :
102- conf ["dir" ] = expand_dir (conf ["parent_dir" ] / conf ["name" ], cwd )
100+ conf ["dir" ] = expand_dir (
101+ pathlib .Path (conf ["parent_dir" ]) / conf ["name" ], cwd
102+ )
103103
104104 if "remotes" in conf :
105105 for remote_name , url in conf ["remotes" ].items ():
106- conf ["remotes" ][remote_name ] = GitRemote (
107- name = remote_name , fetch_url = url , push_url = url
108- )
106+ if isinstance (url , GitRemote ):
107+ continue
108+ if isinstance (url , str ):
109+ conf ["remotes" ][remote_name ] = GitRemote (
110+ name = remote_name , fetch_url = url , push_url = url
111+ )
112+ elif isinstance (url , dict ):
113+ assert "push_url" in url
114+ assert "fetch_url" in url
115+ conf ["remotes" ][remote_name ] = GitRemote (
116+ name = remote_name , ** url
117+ )
118+
109119 configs .append (conf )
110120
111121 return configs
@@ -192,12 +202,12 @@ def find_config_files(
192202 configs .extend (find_config_files (path , match , f ))
193203 else :
194204 match = f"{ match } .{ filetype } "
195- configs = path .glob (match )
205+ configs = list ( path .glob (match ) )
196206
197207 return configs
198208
199209
200- def load_configs (files : list [Union [ str , pathlib . Path ] ], cwd = pathlib .Path .cwd ()):
210+ def load_configs (files : list [StrPath ], cwd = pathlib .Path .cwd ()):
201211 """Return repos from a list of files.
202212
203213 Parameters
@@ -216,10 +226,11 @@ def load_configs(files: list[Union[str, pathlib.Path]], cwd=pathlib.Path.cwd()):
216226 ----
217227 Validate scheme, check for duplicate destinations, VCS urls
218228 """
219- repos = []
229+ repos : list [ ConfigDict ] = []
220230 for file in files :
221231 if isinstance (file , str ):
222232 file = pathlib .Path (file )
233+ assert isinstance (file , pathlib .Path )
223234 ext = file .suffix .lstrip ("." )
224235 conf = kaptan .Kaptan (handler = ext ).import_config (str (file ))
225236 newrepos = extract_repos (conf .export ("dict" ), cwd = cwd )
@@ -230,51 +241,49 @@ def load_configs(files: list[Union[str, pathlib.Path]], cwd=pathlib.Path.cwd()):
230241
231242 dupes = detect_duplicate_repos (repos , newrepos )
232243
233- if dupes :
244+ if len ( dupes ) > 0 :
234245 msg = ("repos with same path + different VCS detected!" , dupes )
235246 raise exc .VCSPullException (msg )
236247 repos .extend (newrepos )
237248
238249 return repos
239250
240251
241- def detect_duplicate_repos (repos1 : list [dict ], repos2 : list [dict ]):
252+ ConfigDictTuple = tuple [ConfigDict , ConfigDict ]
253+
254+
255+ def detect_duplicate_repos (
256+ config1 : list [ConfigDict ], config2 : list [ConfigDict ]
257+ ) -> list [ConfigDictTuple ]:
242258 """Return duplicate repos dict if repo_dir same and vcs different.
243259
244260 Parameters
245261 ----------
246- repos1 : dict
247- list of repo expanded dicts
262+ config1 : list[ConfigDict]
248263
249- repos2 : dict
250- list of repo expanded dicts
264+ config2 : list[ConfigDict]
251265
252266 Returns
253267 -------
254- list of dict, or None
255- Duplicate repos
268+ list[ConfigDictTuple]
269+ List of duplicate tuples
256270 """
257- dupes = []
258- path_dupe_repos = []
259-
260- curpaths = [r ["dir" ] for r in repos1 ]
261- newpaths = [r ["dir" ] for r in repos2 ]
262- path_duplicates = list (set (curpaths ).intersection (newpaths ))
271+ if not config1 :
272+ return []
263273
264- if not path_duplicates :
265- return None
274+ dupes : list [ConfigDictTuple ] = []
266275
267- path_dupe_repos .extend (
268- [r for r in repos2 if any (r ["dir" ] == p for p in path_duplicates )]
269- )
276+ repo_dirs = {
277+ pathlib .Path (repo ["parent_dir" ]) / repo ["name" ]: repo for repo in config1
278+ }
279+ repo_dirs_2 = {
280+ pathlib .Path (repo ["parent_dir" ]) / repo ["name" ]: repo for repo in config2
281+ }
270282
271- if not path_dupe_repos :
272- return None
283+ for repo_dir , repo in repo_dirs .items ():
284+ if repo_dir in repo_dirs_2 :
285+ dupes .append ((repo , repo_dirs_2 [repo_dir ]))
273286
274- for n in path_dupe_repos :
275- currepo = next ((r for r in repos1 if r ["dir" ] == n ["dir" ]), None )
276- if n ["url" ] != currepo ["url" ]:
277- dupes += (n , currepo )
278287 return dupes
279288
280289
@@ -304,11 +313,11 @@ def in_dir(config_dir=None, extensions: list[str] = [".yml", ".yaml", ".json"]):
304313
305314
306315def filter_repos (
307- config : dict ,
308- dir : Union [pathlib .Path , None ] = None ,
316+ config : list [ ConfigDict ] ,
317+ dir : Union [pathlib .Path , Literal [ "*" ], None ] = None ,
309318 vcs_url : Union [str , None ] = None ,
310319 name : Union [str , None ] = None ,
311- ):
320+ ) -> list [ ConfigDict ] :
312321 """Return a :py:obj:`list` list of repos from (expanded) config file.
313322
314323 dir, vcs_url and name all support fnmatch.
@@ -329,23 +338,31 @@ def filter_repos(
329338 list :
330339 Repos
331340 """
332- repo_list = []
341+ repo_list : list [ ConfigDict ] = []
333342
334343 if dir :
335- repo_list .extend ([r for r in config if fnmatch .fnmatch (r ["parent_dir" ], dir )])
344+ repo_list .extend (
345+ [r for r in config if fnmatch .fnmatch (str (r ["parent_dir" ]), str (dir ))]
346+ )
336347
337348 if vcs_url :
338349 repo_list .extend (
339- r for r in config if fnmatch .fnmatch (r .get ("url" , r .get ("repo" )), vcs_url )
350+ r
351+ for r in config
352+ if fnmatch .fnmatch (str (r .get ("url" , r .get ("repo" ))), vcs_url )
340353 )
341354
342355 if name :
343- repo_list .extend ([r for r in config if fnmatch .fnmatch (r .get ("name" ), name )])
356+ repo_list .extend (
357+ [r for r in config if fnmatch .fnmatch (str (r .get ("name" )), name )]
358+ )
344359
345360 return repo_list
346361
347362
348- def is_config_file (filename : str , extensions : list [str ] = [".yml" , ".yaml" , ".json" ]):
363+ def is_config_file (
364+ filename : str , extensions : Union [list [str ], str ] = [".yml" , ".yaml" , ".json" ]
365+ ):
349366 """Return True if file has a valid config file type.
350367
351368 Parameters
0 commit comments