Skip to content

Commit 3fcbde8

Browse files
committed
cli/discover(feat[config-scope]): honor user vs project workspace labels
1 parent bbab554 commit 3fcbde8

File tree

1 file changed

+81
-4
lines changed

1 file changed

+81
-4
lines changed

src/vcspull/cli/discover.py

Lines changed: 81 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,65 @@
2828

2929
log = logging.getLogger(__name__)
3030

31+
ConfigScope = t.Literal["system", "user", "project", "external"]
32+
33+
34+
def _classify_config_scope(
35+
config_path: pathlib.Path,
36+
*,
37+
cwd: pathlib.Path,
38+
home: pathlib.Path,
39+
) -> ConfigScope:
40+
"""Determine whether a config lives in user, system, project, or external scope."""
41+
resolved = config_path.expanduser().resolve()
42+
home = home.expanduser().resolve()
43+
cwd = cwd.expanduser().resolve()
44+
45+
default_user_configs = {
46+
(home / ".vcspull.yaml").resolve(),
47+
(home / ".vcspull.json").resolve(),
48+
}
49+
if resolved in default_user_configs:
50+
return "user"
51+
52+
xdg_config_home = (
53+
pathlib.Path(os.environ.get("XDG_CONFIG_HOME", home / ".config"))
54+
.expanduser()
55+
.resolve()
56+
)
57+
user_config_root = (xdg_config_home / "vcspull").resolve()
58+
try:
59+
resolved.relative_to(user_config_root)
60+
except ValueError:
61+
pass
62+
else:
63+
return "user"
64+
65+
xdg_config_dirs_value = os.environ.get("XDG_CONFIG_DIRS")
66+
if xdg_config_dirs_value:
67+
config_dir_bases = [
68+
pathlib.Path(entry).expanduser().resolve()
69+
for entry in xdg_config_dirs_value.split(os.pathsep)
70+
if entry
71+
]
72+
else:
73+
config_dir_bases = [pathlib.Path("/etc/xdg").resolve()]
74+
75+
for base in config_dir_bases:
76+
candidate = (base / "vcspull").resolve()
77+
try:
78+
resolved.relative_to(candidate)
79+
except ValueError:
80+
continue
81+
else:
82+
return "system"
83+
84+
try:
85+
resolved.relative_to(cwd)
86+
except ValueError:
87+
return "external"
88+
return "project"
89+
3190

3291
def get_git_origin_url(repo_path: pathlib.Path) -> str | None:
3392
"""Get the origin URL from a git repository.
@@ -199,6 +258,11 @@ def discover_repos(
199258

200259
display_config_path = str(PrivatePath(config_file_path))
201260

261+
cwd = pathlib.Path.cwd()
262+
home = pathlib.Path.home()
263+
config_scope = _classify_config_scope(config_file_path, cwd=cwd, home=home)
264+
allow_relative_workspace = config_scope == "project"
265+
202266
raw_config: dict[str, t.Any]
203267
duplicate_root_occurrences: dict[str, list[t.Any]]
204268
if config_file_path.exists() and config_file_path.is_file():
@@ -288,8 +352,8 @@ def discover_repos(
288352
"" if occurrence_count == 1 else "s",
289353
)
290354

291-
cwd = pathlib.Path.cwd()
292-
home = pathlib.Path.home()
355+
explicit_relative_override = workspace_root_override in {".", "./"}
356+
preserve_cwd_label = explicit_relative_override or allow_relative_workspace
293357

294358
if merge_duplicates:
295359
(
@@ -301,6 +365,7 @@ def discover_repos(
301365
raw_config,
302366
cwd=cwd,
303367
home=home,
368+
preserve_cwd_label=preserve_cwd_label,
304369
)
305370
else:
306371
(
@@ -312,6 +377,7 @@ def discover_repos(
312377
raw_config,
313378
cwd=cwd,
314379
home=home,
380+
preserve_cwd_label=preserve_cwd_label,
315381
)
316382

317383
for message in merge_conflicts:
@@ -378,7 +444,12 @@ def discover_repos(
378444
for name, url, workspace_path in found_repos:
379445
workspace_label = workspace_map.get(workspace_path)
380446
if workspace_label is None:
381-
workspace_label = workspace_root_label(workspace_path, cwd=cwd, home=home)
447+
workspace_label = workspace_root_label(
448+
workspace_path,
449+
cwd=cwd,
450+
home=home,
451+
preserve_cwd_label=preserve_cwd_label,
452+
)
382453
workspace_map[workspace_path] = workspace_label
383454
raw_config.setdefault(workspace_label, {})
384455

@@ -416,6 +487,7 @@ def discover_repos(
416487
workspace_path,
417488
cwd=cwd,
418489
home=home,
490+
preserve_cwd_label=preserve_cwd_label,
419491
)
420492
workspace_map[workspace_path] = workspace_label
421493
raw_config.setdefault(workspace_label, {})
@@ -517,7 +589,12 @@ def discover_repos(
517589
for repo_name, repo_url, workspace_path in repos_to_add:
518590
workspace_label = workspace_map.get(workspace_path)
519591
if workspace_label is None:
520-
workspace_label = workspace_root_label(workspace_path, cwd=cwd, home=home)
592+
workspace_label = workspace_root_label(
593+
workspace_path,
594+
cwd=cwd,
595+
home=home,
596+
preserve_cwd_label=preserve_cwd_label,
597+
)
521598
workspace_map[workspace_path] = workspace_label
522599

523600
if workspace_label not in raw_config:

0 commit comments

Comments
 (0)