diff --git a/src/odoo_addons_path/__init__.py b/src/odoo_addons_path/__init__.py index 5bd72e8..237f911 100644 --- a/src/odoo_addons_path/__init__.py +++ b/src/odoo_addons_path/__init__.py @@ -1,4 +1,4 @@ from .cli import app -from .main import get_addons_path +from .main import detect_codebase_layout, get_addons_path, get_odoo_version_from_release -__all__ = ["app", "get_addons_path"] +__all__ = ["app", "detect_codebase_layout", "get_addons_path", "get_odoo_version_from_release"] diff --git a/src/odoo_addons_path/main.py b/src/odoo_addons_path/main.py index 0da6e56..06a61d4 100644 --- a/src/odoo_addons_path/main.py +++ b/src/odoo_addons_path/main.py @@ -1,3 +1,4 @@ +import re from pathlib import Path import typer @@ -15,7 +16,7 @@ def _add_to_path(path_list: list[str], dirs_to_add: list[Path], is_sorted: bool path_list.append(resolved_path) -def _detect_codebase_layout(codebase: Path, verbose: bool = False) -> dict: +def detect_codebase_layout(codebase: Path, verbose: bool = False) -> dict: trobz = TrobzDetector() c2c = C2CDetector() odoo_sh = OdooShDetector() @@ -32,6 +33,18 @@ def _detect_codebase_layout(codebase: Path, verbose: bool = False) -> dict: return detected_paths +def get_odoo_version_from_release(odoo_dir: Path) -> str | None: + """Read the Odoo version (e.g. '18.0') from ``odoo/release.py``.""" + release_py = odoo_dir / "odoo" / "release.py" + if not release_py.is_file(): + return None + content = release_py.read_text() + match = re.search(r"version_info\s*=\s*\((\d+),\s*(\d+)", content) + if match: + return f"{match.group(1)}.{match.group(2)}" + return None + + def _process_paths( all_paths: dict[str, list[str]], detected_paths: dict, @@ -73,18 +86,18 @@ def get_addons_path( addons_dir: list[Path] | None = None, odoo_dir: Path | None = None, verbose: bool = False, + detected_paths: dict | None = None, ) -> str: all_paths: dict[str, list[str]] = { "odoo_dir": [], "addon_repositories": [], } - detected_paths = {} # Skip detector only if both paths are None (no explicit paths provided) - if not addons_dir and not odoo_dir: - detected_paths = _detect_codebase_layout(codebase, verbose) + if detected_paths is None and not addons_dir and not odoo_dir: + detected_paths = detect_codebase_layout(codebase, verbose) - _process_paths(all_paths, detected_paths, addons_dir, odoo_dir) + _process_paths(all_paths, detected_paths or {}, addons_dir, odoo_dir) result = [path for paths in all_paths.values() for path in paths] addons_path = ",".join(result) diff --git a/tests/test_get_addons_path.py b/tests/test_get_addons_path.py index a27980a..3476cd9 100644 --- a/tests/test_get_addons_path.py +++ b/tests/test_get_addons_path.py @@ -3,7 +3,7 @@ import pytest -from odoo_addons_path.main import get_addons_path +from odoo_addons_path.main import detect_codebase_layout, get_addons_path, get_odoo_version_from_release @pytest.fixture @@ -72,3 +72,50 @@ def test_layouts(base_dir: Path, layout: str, expected_paths: list[str]): result = get_addons_path(base_dir) assert result == expected_addons_path + + +@pytest.mark.parametrize("layout", ["trobz", "c2c", "doodba", "odoo-sh"]) +def test_detect_codebase_layout(base_dir: Path, layout: str): + shutil.copytree(Path(__file__).parent / "data" / layout, base_dir, dirs_exist_ok=True) + + result = detect_codebase_layout(base_dir) + + assert isinstance(result, dict) + assert "odoo_dir" in result or "addons_dirs" in result or "addons_dir" in result + + +def test_get_odoo_version_from_release(tmp_path: Path): + release_dir = tmp_path / "odoo" + release_dir.mkdir() + release_py = release_dir / "release.py" + release_py.write_text("version_info = (18, 0, 0, 'final', 0)\nversion = '18.0'\n") + + result = get_odoo_version_from_release(tmp_path) + + assert result == "18.0" + + +def test_get_odoo_version_from_release_missing(tmp_path: Path): + result = get_odoo_version_from_release(tmp_path) + + assert result is None + + +def test_get_odoo_version_from_release_no_version_info(tmp_path: Path): + release_dir = tmp_path / "odoo" + release_dir.mkdir() + (release_dir / "release.py").write_text("# no version_info here\n") + + result = get_odoo_version_from_release(tmp_path) + + assert result is None + + +def test_get_addons_path_with_detected_paths(base_dir: Path): + shutil.copytree(Path(__file__).parent / "data" / "trobz", base_dir, dirs_exist_ok=True) + + detected = detect_codebase_layout(base_dir) + result_auto = get_addons_path(base_dir) + result_predetected = get_addons_path(base_dir, detected_paths=detected) + + assert result_auto == result_predetected