Repository navigation
bundle catalog add rewrites Windows drive paths as POSIX paths #5204
Description
Summary
_is_local_path() explicitly recognizes a Windows drive path such as
C:\catalog.json, but _canonicalize_url() then passes it to the host-native
Path(...).resolve(). On Linux and macOS that turns the Windows absolute path
into a path relative to the current project, and add_source() persists the
rewritten value into the catalog configuration.
Environment
- Repository:
github/spec-kit - Target revision:
1c799bda6c136fcf42fb3475a922ccaac57e6c71 - Platform: macOS (POSIX), Python 3.13
- File:
src/specify_cli/bundles/catalog_config.py:96-116
(_is_local_path,_canonicalize_url), used byadd_source()at:184
Expected behavior
A Windows absolute path is either preserved verbatim in the configuration or
rejected as non-local on a POSIX host. It must not be silently rewritten into a
different path.
Actual behavior
Adding C:\catalog.json on macOS persists:
catalogs:
- id: c---catalog
url: /private/tmp/.../project/C:\catalog.json
priority: 50
install_policy: install-allowed
The stored URL points at a non-existent file whose name contains a backslash
and a colon; later catalog reads fail to resolve the intended source.
Root cause
_WINDOWS_DRIVE_RE = re.compile(r"^[A-Za-z]:[\\/]")
def _is_local_path(url: str) -> bool:
if _WINDOWS_DRIVE_RE.match(url):
return True
...
def _canonicalize_url(url: str) -> str:
if _is_local_path(url):
return str(Path(url).expanduser().resolve())
return url
The Windows-drive branch is platform-independent, while Path is host-native.
On POSIX, Path(r"C:\catalog.json").resolve() produces
<cwd>/C:\catalog.json; on Windows it would be absolute. The same function is
reached from the public bundle catalog add path through add_source().
Impact
- A catalog configuration produced on Windows and consumed on Linux/macOS (or a
Windows path passed on a POSIX host) is silently corrupted instead of being
rejected. - The stored catalog id (
c---catalog) and URL both diverge from the user's
input, and the source cannot be loaded.
Suggested fix
Preserve Windows drive paths on non-Windows hosts instead of resolving them
with the host-native Path:
def _canonicalize_url(url: str) -> str:
if _WINDOWS_DRIVE_RE.match(url):
return url # do not reinterpret on POSIX
if _is_local_path(url):
return str(Path(url).expanduser().resolve())
return url
or canonicalize with PureWindowsPath when the host is POSIX and the input
matches the Windows-drive form, and reject the value if it cannot be resolved.
Activity