Skip to content

Commit cc961dc

Browse files
georgeh0claude
andcommitted
fix: use = delimiter for DB path mapping (avoid Windows colon conflict)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d3b2c29 commit cc961dc

4 files changed

Lines changed: 16 additions & 17 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -425,15 +425,15 @@ By default, index databases (`cocoindex.db` and `target_sqlite.db`) live alongsi
425425
Set `COCOINDEX_CODE_DB_PATH_MAPPING` to remap database locations by path prefix:
426426

427427
```bash
428-
COCOINDEX_CODE_DB_PATH_MAPPING=/workspace:/db-files
428+
COCOINDEX_CODE_DB_PATH_MAPPING=/workspace=/db-files
429429
```
430430

431431
With this mapping, a project at `/workspace/myrepo` stores its databases in `/db-files/myrepo/` instead of `/workspace/myrepo/.cocoindex_code/`. Settings files remain in the original location.
432432

433433
Multiple mappings are comma-separated and resolved in order (first match wins):
434434

435435
```bash
436-
COCOINDEX_CODE_DB_PATH_MAPPING=/workspace:/db-files,/workspace2:/db-files2
436+
COCOINDEX_CODE_DB_PATH_MAPPING=/workspace=/db-files,/workspace2=/db-files2
437437
```
438438

439439
Both source and target must be absolute paths. If no mapping matches, the default location is used.

src/cocoindex_code/settings.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ class DbPathMapping:
131131
def _parse_db_path_mapping() -> list[DbPathMapping]:
132132
"""Parse ``COCOINDEX_CODE_DB_PATH_MAPPING`` env var.
133133
134-
Format: ``/src1:/dst1,/src2:/dst2``
134+
Format: ``/src1=/dst1,/src2=/dst2``
135135
Both source and target must be absolute paths.
136136
"""
137137
raw = os.environ.get(_ENV_DB_PATH_MAPPING, "")
@@ -143,11 +143,10 @@ def _parse_db_path_mapping() -> list[DbPathMapping]:
143143
entry = entry.strip()
144144
if not entry:
145145
continue
146-
# Split on first colon only (to be robust with edge cases)
147-
parts = entry.split(":", 1)
146+
parts = entry.split("=", 1)
148147
if len(parts) != 2 or not parts[0] or not parts[1]:
149148
raise ValueError(
150-
f"{_ENV_DB_PATH_MAPPING}: invalid entry {entry!r}, expected format 'source:target'"
149+
f"{_ENV_DB_PATH_MAPPING}: invalid entry {entry!r}, expected format 'source=target'"
151150
)
152151
source = Path(parts[0])
153152
target = Path(parts[1])

tests/test_e2e.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,7 @@ def e2e_project_with_db_mapping() -> Iterator[tuple[Path, Path]]:
606606
}
607607
os.environ["COCOINDEX_CODE_DIR"] = str(base_dir)
608608
workspace = str(base_dir / "workspace")
609-
os.environ["COCOINDEX_CODE_DB_PATH_MAPPING"] = f"{workspace}:{db_base_dir}"
609+
os.environ["COCOINDEX_CODE_DB_PATH_MAPPING"] = f"{workspace}={db_base_dir}"
610610
_reset_db_path_mapping_cache()
611611
old_cwd = os.getcwd()
612612
os.chdir(project_dir)

tests/test_settings.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -220,45 +220,45 @@ def test_no_mapping(self) -> None:
220220
)
221221

222222
def test_single_mapping_match(self, monkeypatch: pytest.MonkeyPatch) -> None:
223-
monkeypatch.setenv("COCOINDEX_CODE_DB_PATH_MAPPING", "/workspace:/db-files")
223+
monkeypatch.setenv("COCOINDEX_CODE_DB_PATH_MAPPING", "/workspace=/db-files")
224224
assert resolve_db_dir(Path("/workspace/myproject")) == Path("/db-files/myproject")
225225

226226
def test_exact_root_match(self, monkeypatch: pytest.MonkeyPatch) -> None:
227-
monkeypatch.setenv("COCOINDEX_CODE_DB_PATH_MAPPING", "/workspace:/db-files")
227+
monkeypatch.setenv("COCOINDEX_CODE_DB_PATH_MAPPING", "/workspace=/db-files")
228228
assert resolve_db_dir(Path("/workspace")) == Path("/db-files")
229229

230230
def test_no_match_falls_back(self, monkeypatch: pytest.MonkeyPatch) -> None:
231-
monkeypatch.setenv("COCOINDEX_CODE_DB_PATH_MAPPING", "/workspace:/db-files")
231+
monkeypatch.setenv("COCOINDEX_CODE_DB_PATH_MAPPING", "/workspace=/db-files")
232232
assert resolve_db_dir(Path("/other/myproject")) == Path("/other/myproject/.cocoindex_code")
233233

234234
def test_multiple_mappings_first_wins(self, monkeypatch: pytest.MonkeyPatch) -> None:
235-
monkeypatch.setenv("COCOINDEX_CODE_DB_PATH_MAPPING", "/workspace:/db1,/workspace/sub:/db2")
235+
monkeypatch.setenv("COCOINDEX_CODE_DB_PATH_MAPPING", "/workspace=/db1,/workspace/sub=/db2")
236236
assert resolve_db_dir(Path("/workspace/sub/proj")) == Path("/db1/sub/proj")
237237

238238
def test_multiple_mappings_second_matches(self, monkeypatch: pytest.MonkeyPatch) -> None:
239-
monkeypatch.setenv("COCOINDEX_CODE_DB_PATH_MAPPING", "/workspace:/db1,/other:/db2")
239+
monkeypatch.setenv("COCOINDEX_CODE_DB_PATH_MAPPING", "/workspace=/db1,/other=/db2")
240240
assert resolve_db_dir(Path("/other/proj")) == Path("/db2/proj")
241241

242242
def test_no_partial_component_match(self, monkeypatch: pytest.MonkeyPatch) -> None:
243-
monkeypatch.setenv("COCOINDEX_CODE_DB_PATH_MAPPING", "/workspace:/db-files")
243+
monkeypatch.setenv("COCOINDEX_CODE_DB_PATH_MAPPING", "/workspace=/db-files")
244244
assert resolve_db_dir(Path("/workspace2/proj")) == Path("/workspace2/proj/.cocoindex_code")
245245

246246
def test_rejects_relative_source(self, monkeypatch: pytest.MonkeyPatch) -> None:
247-
monkeypatch.setenv("COCOINDEX_CODE_DB_PATH_MAPPING", "relative/path:/db-files")
247+
monkeypatch.setenv("COCOINDEX_CODE_DB_PATH_MAPPING", "relative/path=/db-files")
248248
with pytest.raises(ValueError, match="source path must be absolute"):
249249
resolve_db_dir(Path("/anything"))
250250

251251
def test_rejects_relative_target(self, monkeypatch: pytest.MonkeyPatch) -> None:
252-
monkeypatch.setenv("COCOINDEX_CODE_DB_PATH_MAPPING", "/workspace:relative/path")
252+
monkeypatch.setenv("COCOINDEX_CODE_DB_PATH_MAPPING", "/workspace=relative/path")
253253
with pytest.raises(ValueError, match="target path must be absolute"):
254254
resolve_db_dir(Path("/anything"))
255255

256256
def test_skips_empty_entries(self, monkeypatch: pytest.MonkeyPatch) -> None:
257-
monkeypatch.setenv("COCOINDEX_CODE_DB_PATH_MAPPING", "/workspace:/db-files,,/other:/db2,")
257+
monkeypatch.setenv("COCOINDEX_CODE_DB_PATH_MAPPING", "/workspace=/db-files,,/other=/db2,")
258258
assert resolve_db_dir(Path("/other/proj")) == Path("/db2/proj")
259259

260260
def test_nested_project(self, monkeypatch: pytest.MonkeyPatch) -> None:
261-
monkeypatch.setenv("COCOINDEX_CODE_DB_PATH_MAPPING", "/workspace:/db-files")
261+
monkeypatch.setenv("COCOINDEX_CODE_DB_PATH_MAPPING", "/workspace=/db-files")
262262
assert resolve_db_dir(Path("/workspace/org/repo/subdir")) == Path(
263263
"/db-files/org/repo/subdir"
264264
)

0 commit comments

Comments
 (0)