odoo-addons-path 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- odoo_addons_path/__init__.py +4 -0
- odoo_addons_path/cli.py +87 -0
- odoo_addons_path/detector.py +246 -0
- odoo_addons_path/main.py +100 -0
- odoo_addons_path-1.0.0.dist-info/METADATA +62 -0
- odoo_addons_path-1.0.0.dist-info/RECORD +8 -0
- odoo_addons_path-1.0.0.dist-info/WHEEL +4 -0
- odoo_addons_path-1.0.0.dist-info/entry_points.txt +2 -0
odoo_addons_path/cli.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import glob
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Annotated
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from .main import get_addons_path
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _parse_paths(values: list[str] | None) -> list[Path]:
|
|
11
|
+
if not values:
|
|
12
|
+
return []
|
|
13
|
+
paths: list[Path] = []
|
|
14
|
+
for value in values:
|
|
15
|
+
for p_str in value.split(","):
|
|
16
|
+
p_str = p_str.strip()
|
|
17
|
+
if not p_str:
|
|
18
|
+
continue
|
|
19
|
+
p = Path(p_str).expanduser()
|
|
20
|
+
if "*" in str(p) or "?" in str(p) or "[" in str(p):
|
|
21
|
+
paths.extend(Path(g) for g in sorted(glob.glob(str(p), recursive=True)))
|
|
22
|
+
else:
|
|
23
|
+
paths.append(p)
|
|
24
|
+
return paths
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
app = typer.Typer()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@app.command()
|
|
31
|
+
def main(
|
|
32
|
+
codebase: Annotated[
|
|
33
|
+
Path,
|
|
34
|
+
typer.Argument(
|
|
35
|
+
envvar="CODEBASE",
|
|
36
|
+
help="Path to the Odoo project. Can also be set via the CODEBASE environment variable.",
|
|
37
|
+
exists=True,
|
|
38
|
+
file_okay=False,
|
|
39
|
+
dir_okay=True,
|
|
40
|
+
resolve_path=True,
|
|
41
|
+
),
|
|
42
|
+
] = Path("./"),
|
|
43
|
+
addons_dir: Annotated[
|
|
44
|
+
list[str] | None,
|
|
45
|
+
typer.Option(
|
|
46
|
+
help=(
|
|
47
|
+
"Paths that are addon directories (contain Odoo modules) or "
|
|
48
|
+
"paths that contain addon directories (repositories with multiple Odoo modules). "
|
|
49
|
+
"Globs and comma-separated values are supported."
|
|
50
|
+
),
|
|
51
|
+
),
|
|
52
|
+
] = None,
|
|
53
|
+
odoo_dir: Annotated[
|
|
54
|
+
str | None,
|
|
55
|
+
typer.Option(
|
|
56
|
+
help="Path containing the Odoo source code.",
|
|
57
|
+
),
|
|
58
|
+
] = None,
|
|
59
|
+
verbose: Annotated[
|
|
60
|
+
bool,
|
|
61
|
+
typer.Option(
|
|
62
|
+
"--verbose",
|
|
63
|
+
"-v",
|
|
64
|
+
),
|
|
65
|
+
] = False,
|
|
66
|
+
):
|
|
67
|
+
"""
|
|
68
|
+
Return addons_path constructor
|
|
69
|
+
"""
|
|
70
|
+
odoo_dir_path = None
|
|
71
|
+
if odoo_dir:
|
|
72
|
+
odoo_dir_path = Path(odoo_dir).expanduser()
|
|
73
|
+
if not odoo_dir_path.exists():
|
|
74
|
+
typer.secho(f"Odoo dir {odoo_dir} not found.", fg=typer.colors.RED)
|
|
75
|
+
raise typer.Exit(1)
|
|
76
|
+
|
|
77
|
+
paths = _parse_paths(addons_dir)
|
|
78
|
+
|
|
79
|
+
addons_path = get_addons_path(
|
|
80
|
+
codebase=codebase,
|
|
81
|
+
addons_dir=paths,
|
|
82
|
+
odoo_dir=odoo_dir_path,
|
|
83
|
+
verbose=verbose,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
if not verbose:
|
|
87
|
+
typer.echo(addons_path)
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Any, Optional
|
|
4
|
+
|
|
5
|
+
import yaml
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CodeBaseDetector(ABC):
|
|
9
|
+
_next_detector: Optional["CodeBaseDetector"] = None
|
|
10
|
+
|
|
11
|
+
def set_next(self, detector: "CodeBaseDetector") -> "CodeBaseDetector":
|
|
12
|
+
self._next_detector = detector
|
|
13
|
+
return detector
|
|
14
|
+
|
|
15
|
+
@abstractmethod
|
|
16
|
+
def detect(self, codebase: Path) -> tuple[str, dict[str, Any]] | None:
|
|
17
|
+
if self._next_detector:
|
|
18
|
+
return self._next_detector.detect(codebase)
|
|
19
|
+
return None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TrobzDetector(CodeBaseDetector):
|
|
23
|
+
def detect(self, codebase: Path) -> tuple[str, dict[str, Any]] | None:
|
|
24
|
+
if (codebase / ".trobz").is_dir():
|
|
25
|
+
addons_dirs = []
|
|
26
|
+
for item in (codebase / "addons").iterdir():
|
|
27
|
+
if item.is_dir():
|
|
28
|
+
addons_dirs.append(item)
|
|
29
|
+
return (
|
|
30
|
+
"Trobz",
|
|
31
|
+
{
|
|
32
|
+
"addons_dirs": addons_dirs,
|
|
33
|
+
"addons_dir": [codebase / "project"],
|
|
34
|
+
"odoo_dir": [
|
|
35
|
+
codebase / "odoo/addons",
|
|
36
|
+
codebase / "odoo/odoo/addons",
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
)
|
|
40
|
+
return super().detect(codebase)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class DoodbaDetector(CodeBaseDetector):
|
|
44
|
+
"""
|
|
45
|
+
┌─ root/
|
|
46
|
+
│ └── odoo/
|
|
47
|
+
│ └── custom/
|
|
48
|
+
│ └── src/
|
|
49
|
+
│ ├── odoo/ # Odoo core addons
|
|
50
|
+
│ │ └── addons/
|
|
51
|
+
│ ├── private/
|
|
52
|
+
│ │ └── addon4/
|
|
53
|
+
│ └── submodule/
|
|
54
|
+
│ └── addon1/
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def detect(self, codebase: Path) -> tuple[str, dict[str, Any]] | None:
|
|
58
|
+
copier_answers_file = codebase / ".copier-answers.yml"
|
|
59
|
+
if not copier_answers_file.is_file():
|
|
60
|
+
return super().detect(codebase)
|
|
61
|
+
with open(copier_answers_file) as f:
|
|
62
|
+
try:
|
|
63
|
+
answers = yaml.safe_load(f)
|
|
64
|
+
if "doodba" in answers.get("_src_path", ""):
|
|
65
|
+
addons_dirs = []
|
|
66
|
+
path = codebase / "odoo" / "custom" / "src"
|
|
67
|
+
if path.is_dir():
|
|
68
|
+
for item in path.iterdir():
|
|
69
|
+
if item.is_dir() and item.name not in ("odoo", "private"):
|
|
70
|
+
addons_dirs.append(item)
|
|
71
|
+
return (
|
|
72
|
+
"Doodba",
|
|
73
|
+
{
|
|
74
|
+
"addons_dirs": addons_dirs,
|
|
75
|
+
"addons_dir": [codebase / "odoo/custom/src/private"],
|
|
76
|
+
"odoo_dir": [
|
|
77
|
+
codebase / "odoo/custom/src/odoo/addons",
|
|
78
|
+
codebase / "odoo/custom/src/odoo/odoo/addons",
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
)
|
|
82
|
+
except yaml.YAMLError:
|
|
83
|
+
pass
|
|
84
|
+
return super().detect(codebase)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class C2CDetector(CodeBaseDetector):
|
|
88
|
+
"""
|
|
89
|
+
Supports both legacy and new C2C project structures:
|
|
90
|
+
|
|
91
|
+
Legacy Layout:
|
|
92
|
+
┌─ odoo/
|
|
93
|
+
│ ├── Dockerfile
|
|
94
|
+
│ ├── src/ # Odoo core source
|
|
95
|
+
│ │ ├── addons/
|
|
96
|
+
│ │ └── odoo/
|
|
97
|
+
│ │ └── addons/
|
|
98
|
+
│ ├── external-src/
|
|
99
|
+
│ │ └── custom-repo/
|
|
100
|
+
│ └── local-src/
|
|
101
|
+
│ └── addon2/
|
|
102
|
+
|
|
103
|
+
New Layout:
|
|
104
|
+
┌─ root/
|
|
105
|
+
├── Dockerfile
|
|
106
|
+
├── odoo/
|
|
107
|
+
│ ├── addons/
|
|
108
|
+
│ │ └── addon1/
|
|
109
|
+
│ ├── dev-src/
|
|
110
|
+
│ │ └── addon2/
|
|
111
|
+
│ ├── paid-modules/
|
|
112
|
+
│ │ └── addon3/
|
|
113
|
+
│ └── external-src/
|
|
114
|
+
│ ├── custom-repo/
|
|
115
|
+
│ └── addon4/
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
def _find_docker_file(self, codebase: Path) -> Path | None:
|
|
119
|
+
for path in [codebase / "odoo" / "Dockerfile", codebase / "Dockerfile"]:
|
|
120
|
+
if path.is_file():
|
|
121
|
+
return path
|
|
122
|
+
return None
|
|
123
|
+
|
|
124
|
+
def _is_c2c_dockerfile(self, docker_file: Path) -> bool:
|
|
125
|
+
try:
|
|
126
|
+
with open(docker_file) as f:
|
|
127
|
+
content = f.read()
|
|
128
|
+
return "LABEL maintainer='Camptocamp'" in content or 'LABEL maintainer="Camptocamp"' in content
|
|
129
|
+
except (FileNotFoundError, PermissionError, OSError):
|
|
130
|
+
return False
|
|
131
|
+
|
|
132
|
+
def _collect_external_src_dirs(self, codebase: Path) -> list[Path]:
|
|
133
|
+
addons_dirs = []
|
|
134
|
+
external_src_dir = codebase / "odoo" / "external-src"
|
|
135
|
+
if external_src_dir.is_dir():
|
|
136
|
+
for item in external_src_dir.iterdir():
|
|
137
|
+
if item.is_dir():
|
|
138
|
+
addons_dirs.append(item)
|
|
139
|
+
return addons_dirs
|
|
140
|
+
|
|
141
|
+
def _detect_legacy_layout(self, codebase: Path) -> bool:
|
|
142
|
+
odoo_src_dir = codebase / "odoo" / "src"
|
|
143
|
+
return odoo_src_dir.is_dir()
|
|
144
|
+
|
|
145
|
+
def _get_legacy_config(self, codebase: Path, addons_dirs: list[Path]) -> dict[str, Any]:
|
|
146
|
+
return {
|
|
147
|
+
"addons_dirs": addons_dirs,
|
|
148
|
+
"addons_dir": [codebase / "odoo/local-src"],
|
|
149
|
+
"odoo_dir": [
|
|
150
|
+
codebase / "odoo/src/addons",
|
|
151
|
+
codebase / "odoo/src/odoo/addons",
|
|
152
|
+
],
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
def _get_new_config(self, codebase: Path, addons_dirs: list[Path]) -> dict[str, Any]:
|
|
156
|
+
addons_dir_paths = []
|
|
157
|
+
odoo_dir_paths = []
|
|
158
|
+
|
|
159
|
+
for dir_name in ["dev-src", "paid-modules"]:
|
|
160
|
+
dir_path = codebase / "odoo" / dir_name
|
|
161
|
+
if dir_path.is_dir():
|
|
162
|
+
addons_dir_paths.append(dir_path)
|
|
163
|
+
|
|
164
|
+
odoo_addons_dir = codebase / "odoo" / "addons"
|
|
165
|
+
if odoo_addons_dir.is_dir():
|
|
166
|
+
odoo_dir_paths.append(odoo_addons_dir)
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
"addons_dirs": addons_dirs,
|
|
170
|
+
"addons_dir": addons_dir_paths,
|
|
171
|
+
"odoo_dir": odoo_dir_paths,
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
def detect(self, codebase: Path) -> tuple[str, dict[str, Any]] | None:
|
|
175
|
+
docker_file = self._find_docker_file(codebase)
|
|
176
|
+
if not docker_file or not self._is_c2c_dockerfile(docker_file):
|
|
177
|
+
return super().detect(codebase)
|
|
178
|
+
|
|
179
|
+
addons_dirs = self._collect_external_src_dirs(codebase)
|
|
180
|
+
|
|
181
|
+
if self._detect_legacy_layout(codebase):
|
|
182
|
+
return "Camptocamp (Legacy)", self._get_legacy_config(codebase, addons_dirs)
|
|
183
|
+
else:
|
|
184
|
+
return "Camptocamp", self._get_new_config(codebase, addons_dirs)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class OdooShDetector(CodeBaseDetector):
|
|
188
|
+
"""
|
|
189
|
+
Follow Odoo.sh mode
|
|
190
|
+
src/
|
|
191
|
+
├── enterprise/ # Odoo Enterprise
|
|
192
|
+
├── odoo/ # Odoo core
|
|
193
|
+
└── themes/
|
|
194
|
+
├── user/ # user's submodule
|
|
195
|
+
│ └── OCA/
|
|
196
|
+
"""
|
|
197
|
+
|
|
198
|
+
def detect(self, codebase: Path) -> tuple[str, dict[str, Any]] | None:
|
|
199
|
+
if (
|
|
200
|
+
(codebase / "enterprise").is_dir()
|
|
201
|
+
and (codebase / "odoo").is_dir()
|
|
202
|
+
and (codebase / "themes").is_dir()
|
|
203
|
+
and (codebase / "user").is_dir()
|
|
204
|
+
):
|
|
205
|
+
addons_dirs = [codebase / "enterprise", codebase / "themes"]
|
|
206
|
+
for item in (codebase / "user").iterdir():
|
|
207
|
+
if item.is_dir():
|
|
208
|
+
addons_dirs.append(item)
|
|
209
|
+
return (
|
|
210
|
+
"odoo.sh",
|
|
211
|
+
{
|
|
212
|
+
"addons_dirs": addons_dirs,
|
|
213
|
+
"addons_dir": [codebase / "project"],
|
|
214
|
+
"odoo_dir": [
|
|
215
|
+
codebase / "odoo/addons",
|
|
216
|
+
codebase / "odoo/odoo/addons",
|
|
217
|
+
],
|
|
218
|
+
},
|
|
219
|
+
)
|
|
220
|
+
return super().detect(codebase)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class GenericDetector(CodeBaseDetector):
|
|
224
|
+
"""
|
|
225
|
+
A fallback detector that looks for any folder that contains __manifest__.py
|
|
226
|
+
"""
|
|
227
|
+
|
|
228
|
+
def detect(self, codebase: Path) -> tuple[str, dict[str, Any]] | None:
|
|
229
|
+
manifest_files = []
|
|
230
|
+
for manifest_file in codebase.glob("**/__manifest__.py"):
|
|
231
|
+
# ignore setup folder in a module
|
|
232
|
+
if "setup" in manifest_file.parts:
|
|
233
|
+
continue
|
|
234
|
+
# ignore folder in a same folder
|
|
235
|
+
str_manifest_file = str(manifest_file)
|
|
236
|
+
if str_manifest_file.count("__manifest__.py") > 1:
|
|
237
|
+
continue
|
|
238
|
+
manifest_files.append(str_manifest_file)
|
|
239
|
+
if not manifest_files:
|
|
240
|
+
return super().detect(codebase)
|
|
241
|
+
|
|
242
|
+
addons_dirs = set()
|
|
243
|
+
for manifest_file in manifest_files:
|
|
244
|
+
addons_dirs.add(Path(manifest_file).parent.parent)
|
|
245
|
+
|
|
246
|
+
return "fallback", {"addons_dirs": list(addons_dirs)}
|
odoo_addons_path/main.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
|
|
5
|
+
from odoo_addons_path.detector import C2CDetector, DoodbaDetector, GenericDetector, OdooShDetector, TrobzDetector
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _add_to_path(path_list: list[str], dirs_to_add: list[Path], is_sorted: bool = False):
|
|
9
|
+
if is_sorted:
|
|
10
|
+
dirs_to_add = sorted(dirs_to_add, key=lambda p: p.name)
|
|
11
|
+
for d in dirs_to_add:
|
|
12
|
+
if d.is_dir():
|
|
13
|
+
resolved_path = str(d.resolve())
|
|
14
|
+
if resolved_path not in path_list:
|
|
15
|
+
path_list.append(resolved_path)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _detect_codebase_layout(codebase: Path, verbose: bool = False) -> dict:
|
|
19
|
+
trobz = TrobzDetector()
|
|
20
|
+
c2c = C2CDetector()
|
|
21
|
+
odoo_sh = OdooShDetector()
|
|
22
|
+
doodba = DoodbaDetector()
|
|
23
|
+
fallback = GenericDetector()
|
|
24
|
+
trobz.set_next(c2c).set_next(odoo_sh).set_next(doodba).set_next(fallback)
|
|
25
|
+
res = trobz.detect(codebase)
|
|
26
|
+
if not res:
|
|
27
|
+
typer.secho("No codebase layout detected", fg=typer.colors.RED)
|
|
28
|
+
raise typer.Exit(1)
|
|
29
|
+
detector_name, detected_paths = res
|
|
30
|
+
if verbose:
|
|
31
|
+
typer.echo(f"Codebase layout: {detector_name}")
|
|
32
|
+
return detected_paths
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _process_paths(
|
|
36
|
+
all_paths: dict[str, list[str]],
|
|
37
|
+
detected_paths: dict,
|
|
38
|
+
addons_dir: list[Path] | None,
|
|
39
|
+
odoo_dir: Path | None,
|
|
40
|
+
):
|
|
41
|
+
if odoo_dir:
|
|
42
|
+
_add_to_path(
|
|
43
|
+
all_paths["odoo_dir"],
|
|
44
|
+
[odoo_dir / "addons", odoo_dir / "odoo" / "addons"],
|
|
45
|
+
)
|
|
46
|
+
if detected_paths.get("odoo_dir"):
|
|
47
|
+
_add_to_path(all_paths["odoo_dir"], detected_paths["odoo_dir"])
|
|
48
|
+
|
|
49
|
+
all_addon_paths_to_process = (
|
|
50
|
+
(addons_dir or []) + detected_paths.get("addons_dirs", []) + detected_paths.get("addons_dir", [])
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
result = []
|
|
54
|
+
|
|
55
|
+
for p in all_addon_paths_to_process:
|
|
56
|
+
if not p.is_dir():
|
|
57
|
+
continue
|
|
58
|
+
manifests = p.glob("**/__manifest__.py")
|
|
59
|
+
for manifest in sorted(manifests):
|
|
60
|
+
repo_path = manifest.parent.parent
|
|
61
|
+
if repo_path not in result:
|
|
62
|
+
result.append(repo_path)
|
|
63
|
+
|
|
64
|
+
_add_to_path(
|
|
65
|
+
all_paths["addon_repositories"],
|
|
66
|
+
result,
|
|
67
|
+
is_sorted=not bool(addons_dir),
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def get_addons_path(
|
|
72
|
+
codebase: Path,
|
|
73
|
+
addons_dir: list[Path] | None = None,
|
|
74
|
+
odoo_dir: Path | None = None,
|
|
75
|
+
verbose: bool = False,
|
|
76
|
+
) -> str:
|
|
77
|
+
all_paths: dict[str, list[str]] = {
|
|
78
|
+
"odoo_dir": [],
|
|
79
|
+
"addon_repositories": [],
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
detected_paths = {}
|
|
83
|
+
if not addons_dir:
|
|
84
|
+
detected_paths = _detect_codebase_layout(codebase, verbose)
|
|
85
|
+
|
|
86
|
+
_process_paths(all_paths, detected_paths, addons_dir, odoo_dir)
|
|
87
|
+
|
|
88
|
+
result = [path for paths in all_paths.values() for path in paths]
|
|
89
|
+
addons_path = ",".join(result)
|
|
90
|
+
|
|
91
|
+
if verbose:
|
|
92
|
+
for category, paths in all_paths.items():
|
|
93
|
+
if paths:
|
|
94
|
+
typer.echo(f"\n# {category}")
|
|
95
|
+
for path in paths:
|
|
96
|
+
typer.echo(path)
|
|
97
|
+
typer.echo("\n# addons_path")
|
|
98
|
+
typer.echo(addons_path)
|
|
99
|
+
|
|
100
|
+
return addons_path
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: odoo-addons-path
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Create the ultimate Odoo addons_path constructor
|
|
5
|
+
Project-URL: Repository, https://github.com/trobz/odoo-addons-path
|
|
6
|
+
Author-email: trisdoan <doanminhtri8183@gmail.com>
|
|
7
|
+
Keywords: python
|
|
8
|
+
Requires-Python: <4.0,>=3.10
|
|
9
|
+
Requires-Dist: pyyaml
|
|
10
|
+
Requires-Dist: typer>=0.19.2
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# odoo-addons-path
|
|
14
|
+
|
|
15
|
+
A tool to auto-detect and construct Odoo `addons_path` for various project layouts.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install odoo-addons-path
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick start
|
|
24
|
+
|
|
25
|
+
### As a CLI tool
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
odoo-addons-path /path/to/your/odoo/project
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
#### Example: This will find addon directories that inside '18.0' directories
|
|
32
|
+
```bash
|
|
33
|
+
odoo-addons-path --addons-dir "./tests/data/repo-version-module/*/18.0"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
#### Example: List of repo directories
|
|
37
|
+
```bash
|
|
38
|
+
odoo-addons-path --verbose --addons-dir "./tests/data/c2c-new/odoo/external-src/, ./tests/data/c2c/odoo/external-src/"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Note:** The path to the codebase can also be set via the `CODEBASE` environment variable.
|
|
42
|
+
|
|
43
|
+
### As a library
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from pathlib import Path
|
|
47
|
+
from odoo_addons_path import get_addons_path
|
|
48
|
+
|
|
49
|
+
addons_path = get_addons_path(Path("/path/to/your/odoo/project"))
|
|
50
|
+
print(addons_path)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Codebase layouts supported
|
|
54
|
+
|
|
55
|
+
There are several out-of-the-box supported layouts:
|
|
56
|
+
|
|
57
|
+
- `c2c`
|
|
58
|
+
- `doodba`
|
|
59
|
+
- `odoo.sh`
|
|
60
|
+
- `trobz`
|
|
61
|
+
|
|
62
|
+
For more details on the layouts, please refer to the `tests/data/` directory.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
odoo_addons_path/__init__.py,sha256=9KUyE7AUseiDPwHNZ3v35BA9KYzsyPuAMV1hbo9baKI,93
|
|
2
|
+
odoo_addons_path/cli.py,sha256=C6wgoGx7jfCeQWYlvNO1gQKJD4ghMyRqFRjvviutIzc,2234
|
|
3
|
+
odoo_addons_path/detector.py,sha256=RTFVpTCvsN8La-NnYch0lTAFArkbnV1ZZ_BNsiFOzE0,8658
|
|
4
|
+
odoo_addons_path/main.py,sha256=M61uCoeSdQYqhvm6Fc15WO3Kh2TaZkp5sUman_pzPtc,2965
|
|
5
|
+
odoo_addons_path-1.0.0.dist-info/METADATA,sha256=-9MTSqnyem65crtXG7YuUtBnjsp2jhxRJu9KR5lwipQ,1445
|
|
6
|
+
odoo_addons_path-1.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
7
|
+
odoo_addons_path-1.0.0.dist-info/entry_points.txt,sha256=m4J4eCgDvGLRCbp0yrKdSpj9QIlUOubSMan7cnvx2-g,62
|
|
8
|
+
odoo_addons_path-1.0.0.dist-info/RECORD,,
|