Skip to content

Commit 98bebed

Browse files
authored
Merge pull request #12 from hailangvn/project_dir
feat: add --project-dir option to create command
2 parents dcfa231 + 4184ac7 commit 98bebed

3 files changed

Lines changed: 118 additions & 6 deletions

File tree

odoo_venv/cli/main.py

Lines changed: 101 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
from typing import Annotated
55

66
import typer
7+
from odoo_addons_path import (
8+
detect_codebase_layout,
9+
get_addons_path,
10+
get_odoo_version_from_release,
11+
)
712

813
from odoo_venv.exceptions import PresetNotFoundError
914
from odoo_venv.launcher import create_launcher
@@ -48,6 +53,20 @@ def preset_callback(ctx: typer.Context, param: typer.CallbackParam, value: str):
4853
return value
4954

5055

56+
def project_dir_callback(ctx: typer.Context, param: typer.CallbackParam, value: str | None):
57+
if not value:
58+
return None
59+
60+
# Auto-apply "project" preset if no preset was explicitly set.
61+
# --preset is also is_eager and declared before --project-dir, so if the user
62+
# passed --preset explicitly, ctx.default_map is already populated here.
63+
if not ctx.default_map:
64+
preset_callback(ctx, param, "project")
65+
66+
ctx.ensure_object(dict)["project_dir"] = value
67+
return value
68+
69+
5170
def version_callback(value: bool):
5271
if value:
5372
typer.echo(f"odoo-venv {version('odoo-venv')}")
@@ -70,10 +89,69 @@ def main_callback(
7089
pass
7190

7291

92+
def _detect_project_layout(project_dir_value: str) -> tuple[Path | None, str | None, str | None]:
93+
"""Detect odoo_dir, odoo_version, and addons_path from a project directory.
94+
95+
Returns:
96+
(odoo_dir_path, odoo_version, addons_path) — any value may be None if not detected.
97+
"""
98+
project_dir_path = Path(project_dir_value).expanduser().resolve()
99+
detected_paths = detect_codebase_layout(project_dir_path)
100+
101+
addons_path = get_addons_path(project_dir_path, detected_paths=detected_paths)
102+
103+
# Resolve odoo_dir from detected layout
104+
odoo_dir_path = None
105+
if detected_paths.get("odoo_dir"):
106+
odoo_dir_path = detected_paths["odoo_dir"][0].parent
107+
108+
# Infer version from release.py inside the detected odoo dir
109+
odoo_version = None
110+
if odoo_dir_path:
111+
odoo_version = get_odoo_version_from_release(odoo_dir_path)
112+
113+
return odoo_dir_path, odoo_version, addons_path
114+
115+
116+
def _resolve_odoo_dir_and_version(
117+
odoo_dir: str | None,
118+
odoo_version: str | None,
119+
detected_odoo_dir: Path | None,
120+
detected_version: str | None,
121+
) -> tuple[Path, str]:
122+
"""Determine odoo_dir and odoo_version from explicit args or detected values.
123+
124+
Priority: explicit CLI flags > auto-detected from --project-dir > default path.
125+
Exits with an error if neither can be resolved.
126+
"""
127+
# Resolve odoo_dir: explicit flag > detected > default path from version
128+
if odoo_dir:
129+
odoo_dir_path = Path(odoo_dir).expanduser().resolve()
130+
elif detected_odoo_dir:
131+
odoo_dir_path = detected_odoo_dir
132+
elif odoo_version:
133+
odoo_dir_path = Path(f"~/code/odoo/odoo/{odoo_version}").expanduser()
134+
else:
135+
typer.secho("error: ODOO_VERSION is required when --project-dir is not used.", fg=typer.colors.RED)
136+
raise typer.Exit(1)
137+
138+
# Resolve odoo_version: explicit arg > detected from release.py
139+
resolved_version = odoo_version or detected_version
140+
if not resolved_version:
141+
typer.secho(
142+
"error: Could not detect Odoo version from source. Provide ODOO_VERSION explicitly.", fg=typer.colors.RED
143+
)
144+
raise typer.Exit(1)
145+
146+
return odoo_dir_path, resolved_version
147+
148+
73149
@app.command()
74150
def create(
75151
ctx: typer.Context,
76-
odoo_version: Annotated[str, typer.Argument(help="Odoo version, e.g: 18.0")],
152+
odoo_version: Annotated[
153+
str | None, typer.Argument(help="Odoo version, e.g: 18.0. Inferred from --project-dir if omitted.")
154+
] = None,
77155
python_version: Annotated[
78156
str | None,
79157
typer.Option("--python-version", "-p", help="Specify Python version."),
@@ -154,12 +232,27 @@ def create(
154232
help="Generate a launcher script in ~/.local/bin/.",
155233
),
156234
] = False,
235+
project_dir: Annotated[
236+
str | None,
237+
typer.Option(
238+
"--project-dir",
239+
callback=project_dir_callback,
240+
is_eager=True,
241+
help="Path to project directory. Auto-detects --addons-path, --odoo-dir "
242+
"via odoo-addons-path and applies --preset=project.",
243+
),
244+
] = None,
157245
):
158246
"""Create virtual environment to run Odoo"""
159-
if not odoo_dir:
160-
odoo_dir_path = Path(f"~/code/odoo/odoo/{odoo_version}").expanduser()
161-
else:
162-
odoo_dir_path = Path(odoo_dir).expanduser().resolve()
247+
# Auto-detect layout from --project-dir if provided
248+
project_dir_value = ctx.obj.get("project_dir") if ctx.obj else None
249+
detected_odoo_dir, detected_version, detected_addons_path = (
250+
_detect_project_layout(project_dir_value) if project_dir_value else (None, None, None)
251+
)
252+
253+
odoo_dir_path, odoo_version = _resolve_odoo_dir_and_version(
254+
odoo_dir, odoo_version, detected_odoo_dir, detected_version
255+
)
163256

164257
if not python_version:
165258
python_version = ODOO_PYTHON_VERSIONS.get(odoo_version)
@@ -173,6 +266,9 @@ def create(
173266
else:
174267
extra_requirements_list = list(extra_requirement)
175268

269+
if not addons_path and detected_addons_path:
270+
addons_path = detected_addons_path
271+
176272
addons_path_list = (
177273
[str(Path(p.strip()).expanduser().resolve()) for p in addons_path.split(",")] if addons_path else None
178274
)

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ dependencies = [
1414
"typing_extensions",
1515
"tomli",
1616
"packaging>=25.0",
17+
"odoo-addons-path>=1.1.0",
1718
]
1819

1920
[dependency-groups]

uv.lock

Lines changed: 16 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)