Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/specify_cli/bundles/catalog_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 23 additions & 0 deletions tests/specify_cli/bundles/test_catalog_config.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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)
Expand Down