Skip to content
Merged
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
20 changes: 13 additions & 7 deletions java_codebase_rag/jrag.py
Original file line number Diff line number Diff line change
Expand Up @@ -1096,20 +1096,26 @@ def _core_parser() -> argparse.ArgumentParser:
def _resolve_cfg(args: argparse.Namespace): # type: ignore[no-untyped-def]
"""Resolve operator config (reuses the operator's cocoindex-free resolver).

Same pattern as ``java_codebase_rag.cli._resolved_from_ns``: walks up from
cwd to find a project root (config file or ``.java-codebase-rag/`` index),
applies CLI ``--index-dir`` if given, and calls ``apply_to_os_environ`` so
downstream modules see a consistent env (critically: SBERT_MODEL for
``jrag search`` in PR-JRAG-4).
Mirrors ``java_codebase_rag.cli._resolved_from_ns``: pass ``source_root=None``
so ``resolve_operator_config`` honors ``JAVA_CODEBASE_RAG_SOURCE_ROOT`` first,
then a YAML ``source_root`` field, then walks up from cwd to find a project
root. Passing a discovered root explicitly here would OVERRIDE a set env var
whenever any ancestor dir has a ``.java-codebase-rag`` marker — silently
ignoring the documented subprocess source-root mechanism that
``pipeline.subprocess_env`` sets for the cocoindex child (and that operators
set directly).

When the anchor is an index dir with no YAML beside it, resolution follows
that index's ``config_source`` pointer (see ``config._effective_config_dir``)
so a config living in a sibling dir is still found from inside a microservice.
Applies CLI ``--index-dir`` if given and calls ``apply_to_os_environ`` so
downstream modules see a consistent env (critically SBERT_MODEL for ``jrag
search``).
"""
from java_codebase_rag.config import discover_project_root, resolve_operator_config
from java_codebase_rag.config import resolve_operator_config

cfg = resolve_operator_config(
source_root=discover_project_root(Path.cwd()),
source_root=None,
cli_index_dir=getattr(args, "index_dir", None),
)
cfg.apply_to_os_environ()
Expand Down
44 changes: 44 additions & 0 deletions tests/test_jrag_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import sys
from pathlib import Path

import pytest


def _jrag_exe() -> str:
"""Locate the installed ``jrag`` entry point next to the venv interpreter."""
Expand Down Expand Up @@ -172,3 +174,45 @@ def test_jrag_help_lists_status_subcommand() -> None:
assert "status" in proc.stdout
# The --offset flag must NOT appear in the top-level help.
assert "--offset" not in proc.stdout


# ----- Config resolution: env var beats a stray ancestor marker -----


def test_resolve_cfg_honors_source_root_env_over_stray_marker(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""JAVA_CODEBASE_RAG_SOURCE_ROOT must win over a stray ``.java-codebase-rag``
marker in an ancestor of cwd.

Regression: ``jrag._resolve_cfg`` used to pass ``discover_project_root(cwd)``
as the explicit ``source_root``, which silently overrode the env var whenever
any ancestor dir had a non-empty ``.java-codebase-rag/`` index — the
documented subprocess source-root mechanism ``pipeline.subprocess_env`` sets
for the cocoindex child (and that the traversal tests rely on). It now passes
``source_root=None`` so ``resolve_operator_config`` honors the env var first
(mirroring ``cli._resolved_from_ns``).
"""
from java_codebase_rag.jrag import _resolve_cfg

real_root = tmp_path / "real-source"
real_root.mkdir()
# cwd sits inside a dir whose PARENT has a stray non-empty .java-codebase-rag/
# index dir — the hijack condition that used to override the env var.
# (_has_index_dir requires the marker dir to be non-empty: any(iterdir()).)
workdir = tmp_path / "has-marker" / "sub"
workdir.mkdir(parents=True)
stray_idx = tmp_path / "has-marker" / ".java-codebase-rag"
stray_idx.mkdir()
(stray_idx / "cocoindex.db").write_bytes(b"") # non-empty → recognized as marker
monkeypatch.chdir(workdir)
monkeypatch.setenv("JAVA_CODEBASE_RAG_SOURCE_ROOT", str(real_root))
monkeypatch.delenv("JAVA_CODEBASE_RAG_INDEX_DIR", raising=False)

class _Args:
index_dir = None

cfg = _resolve_cfg(_Args()) # type: ignore[arg-type]
assert cfg.source_root == real_root.resolve(), (
"JAVA_CODEBASE_RAG_SOURCE_ROOT must win over a stray ancestor marker"
)
Loading