diff --git a/src/specify_cli/bundles/catalog_config.py b/src/specify_cli/bundles/catalog_config.py index db1833a4ca..7a8bcd7174 100644 --- a/src/specify_cli/bundles/catalog_config.py +++ b/src/specify_cli/bundles/catalog_config.py @@ -110,7 +110,10 @@ def _canonicalize_url(url: str) -> str: Remote URLs (``http(s)://``, ``file://``, ``builtin://``) are returned unchanged; only bare/relative local paths are resolved to an absolute path. + Foreign Windows drive paths stay unchanged rather than becoming POSIX paths. """ + if _WINDOWS_DRIVE_RE.match(url) and not Path(url).is_absolute(): + return url if _is_local_path(url): return str(Path(url).expanduser().resolve()) return url diff --git a/tests/specify_cli/bundles/test_catalog_config.py b/tests/specify_cli/bundles/test_catalog_config.py index 19fc79b37a..2df4cc921b 100644 --- a/tests/specify_cli/bundles/test_catalog_config.py +++ b/tests/specify_cli/bundles/test_catalog_config.py @@ -1,6 +1,7 @@ """Unit tests for project catalog-config id derivation and url canonicalization.""" from __future__ import annotations +import os from pathlib import Path import pytest @@ -72,6 +73,28 @@ def test_add_source_persists_absolute_local_path(tmp_path: Path, monkeypatch): assert Path(source.url) == catalog.resolve() +@pytest.mark.skipif(os.name == "nt", reason="POSIX path rewriting regression") +@pytest.mark.parametrize("url", [r"C:\catalog.json", "C:/catalog.json"]) +def test_add_source_preserves_foreign_windows_drive_path(tmp_path: Path, url: str): + project = tmp_path / "proj" + project.mkdir() + + source, status = cc.add_source( + project, url, policy="install-allowed", priority=50 + ) + + assert status == "added" + assert source.url == url + assert cc._read(project)[0]["url"] == url + repeated, status = cc.add_source( + project, url, policy="install-allowed", priority=50 + ) + assert status == "unchanged" + assert repeated.url == url + cc.remove_source(project, url) + assert cc._read(project) == [] + + def test_add_source_normalizes_existing_entry_for_idempotency(tmp_path: Path): project = tmp_path / "proj" (project / ".specify").mkdir(parents=True)