diff --git a/src/apm_cli/models/dependency/reference.py b/src/apm_cli/models/dependency/reference.py index 55207a9e..84a99202 100644 --- a/src/apm_cli/models/dependency/reference.py +++ b/src/apm_cli/models/dependency/reference.py @@ -129,14 +129,26 @@ def get_virtual_package_name(self) -> str: def is_local_path(dep_str: str) -> bool: """Check if a dependency string looks like a local filesystem path. - Local paths start with './', '../', '/', or '~'. + Local paths start with './', '../', '/', '~/', '~\\', or a Windows drive + letter (e.g. 'C:\\' or 'C:/'). Protocol-relative URLs ('//...') are explicitly excluded. """ s = dep_str.strip() # Reject protocol-relative URLs ('//...') if s.startswith('//'): return False - return s.startswith(('./','../', '/', '~/', '~\\', '.\\', '..\\')) + if s.startswith(('./','../', '/', '~/', '~\\', '.\\', '..\\')): + return True + # Windows absolute paths: drive letter + colon + separator (C:\ or C:/). + # Only ASCII letters A-Z/a-z are valid drive letters. + if ( + len(s) >= 3 + and (('A' <= s[0] <= 'Z') or ('a' <= s[0] <= 'z')) + and s[1] == ':' + and s[2] in ('\\', '/') + ): + return True + return False def get_unique_key(self) -> str: """Get a unique key for this dependency for deduplication. diff --git a/tests/unit/test_local_deps.py b/tests/unit/test_local_deps.py index 64491adb..323d2300 100644 --- a/tests/unit/test_local_deps.py +++ b/tests/unit/test_local_deps.py @@ -37,6 +37,18 @@ def test_windows_parent(self): def test_windows_home(self): assert DependencyReference.is_local_path("~\\repos\\my-pkg") is True + def test_windows_absolute_backslash(self): + assert DependencyReference.is_local_path("C:\\Users\\runner\\my-pkg") is True + + def test_windows_absolute_forward_slash(self): + assert DependencyReference.is_local_path("D:/repos/my-pkg") is True + + def test_windows_absolute_uppercase(self): + assert DependencyReference.is_local_path("Z:\\some\\path") is True + + def test_windows_absolute_lowercase(self): + assert DependencyReference.is_local_path("c:\\users\\me\\pkg") is True + def test_remote_shorthand_not_local(self): assert DependencyReference.is_local_path("owner/repo") is False