bl-odoo 0.2.7__tar.gz → 0.3.1__tar.gz
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.
- {bl_odoo-0.2.7/bl_odoo.egg-info → bl_odoo-0.3.1}/PKG-INFO +1 -1
- {bl_odoo-0.2.7 → bl_odoo-0.3.1}/bl/__main__.py +13 -9
- {bl_odoo-0.2.7 → bl_odoo-0.3.1}/bl/freezer.py +2 -4
- bl_odoo-0.3.1/bl/spec_parser.py +159 -0
- bl_odoo-0.3.1/bl/spec_processor.py +471 -0
- bl_odoo-0.3.1/bl/types.py +86 -0
- {bl_odoo-0.2.7 → bl_odoo-0.3.1}/bl/utils.py +6 -4
- {bl_odoo-0.2.7 → bl_odoo-0.3.1/bl_odoo.egg-info}/PKG-INFO +1 -1
- {bl_odoo-0.2.7 → bl_odoo-0.3.1}/bl_odoo.egg-info/SOURCES.txt +1 -0
- {bl_odoo-0.2.7 → bl_odoo-0.3.1}/pyproject.toml +1 -1
- bl_odoo-0.2.7/bl/spec_parser.py +0 -225
- bl_odoo-0.2.7/bl/spec_processor.py +0 -459
- {bl_odoo-0.2.7 → bl_odoo-0.3.1}/LICENSE +0 -0
- {bl_odoo-0.2.7 → bl_odoo-0.3.1}/README.md +0 -0
- {bl_odoo-0.2.7 → bl_odoo-0.3.1}/bl/__init__.py +0 -0
- {bl_odoo-0.2.7 → bl_odoo-0.3.1}/bl_odoo.egg-info/dependency_links.txt +0 -0
- {bl_odoo-0.2.7 → bl_odoo-0.3.1}/bl_odoo.egg-info/entry_points.txt +0 -0
- {bl_odoo-0.2.7 → bl_odoo-0.3.1}/bl_odoo.egg-info/requires.txt +0 -0
- {bl_odoo-0.2.7 → bl_odoo-0.3.1}/bl_odoo.egg-info/top_level.txt +0 -0
- {bl_odoo-0.2.7 → bl_odoo-0.3.1}/setup.cfg +0 -0
- {bl_odoo-0.2.7 → bl_odoo-0.3.1}/tests/test_frozen_parser.py +0 -0
- {bl_odoo-0.2.7 → bl_odoo-0.3.1}/tests/test_frozen_processor.py +0 -0
|
@@ -12,15 +12,19 @@ def run():
|
|
|
12
12
|
parser = argparse.ArgumentParser(
|
|
13
13
|
description="Process a project specification.", formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
|
14
14
|
)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
parser.add_argument(
|
|
15
|
+
|
|
16
|
+
parent_parser = argparse.ArgumentParser(add_help=False)
|
|
17
|
+
parent_parser.add_argument(
|
|
19
18
|
"-c", "--config", type=Path, help="Path to the project specification file.", default="spec.yaml"
|
|
20
19
|
)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
parent_parser.add_argument("-z", "--frozen", type=Path, help="Path to the frozen specification file.")
|
|
21
|
+
parent_parser.add_argument("-j", "--concurrency", type=int, default=28, help="Number of concurrent tasks.")
|
|
22
|
+
parent_parser.add_argument("-w", "--workdir", type=Path, help="Working directory. Defaults to config directory.")
|
|
23
|
+
|
|
24
|
+
sub = parser.add_subparsers(help="subcommand help", dest="command")
|
|
25
|
+
build = sub.add_parser("build", parents=[parent_parser], help="build help")
|
|
26
|
+
freeze = sub.add_parser("freeze", parents=[parent_parser], help="freeze help")
|
|
27
|
+
|
|
24
28
|
args = parser.parse_args()
|
|
25
29
|
|
|
26
30
|
project_spec = load_spec_file(args.config, args.frozen, args.workdir)
|
|
@@ -28,9 +32,9 @@ def run():
|
|
|
28
32
|
sys.exit(1)
|
|
29
33
|
|
|
30
34
|
try:
|
|
31
|
-
if args.freeze:
|
|
35
|
+
if args.command == "freeze":
|
|
32
36
|
asyncio.run(freeze_project(project_spec, args.freeze, concurrency=args.concurrency))
|
|
33
|
-
|
|
37
|
+
elif args.command == "build":
|
|
34
38
|
asyncio.run(process_project(project_spec, concurrency=args.concurrency))
|
|
35
39
|
except Exception:
|
|
36
40
|
sys.exit(1)
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import yaml
|
|
3
|
-
from operator import countOf
|
|
4
3
|
from pathlib import Path
|
|
5
|
-
from typing import TextIO
|
|
6
4
|
|
|
7
5
|
from rich.console import Console
|
|
8
6
|
from rich.live import Live
|
|
9
7
|
from rich.progress import BarColumn, MofNCompleteColumn, Progress, TaskID, TextColumn
|
|
10
|
-
from bl.
|
|
8
|
+
from bl.types import RepoInfo, ProjectSpec
|
|
11
9
|
from bl.utils import get_local_ref, get_module_path, run_git
|
|
12
10
|
|
|
13
11
|
console = Console()
|
|
@@ -18,7 +16,7 @@ async def freeze_spec(
|
|
|
18
16
|
progress: Progress,
|
|
19
17
|
task_id: TaskID,
|
|
20
18
|
module_name: str,
|
|
21
|
-
module_spec:
|
|
19
|
+
module_spec: RepoInfo,
|
|
22
20
|
workdir: Path,
|
|
23
21
|
):
|
|
24
22
|
result = {module_name: {}}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import warnings
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any, Dict, List, Optional, Type
|
|
7
|
+
|
|
8
|
+
import yaml
|
|
9
|
+
|
|
10
|
+
from bl.types import RepoInfo, OriginType, ProjectSpec, RefspecInfo
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def make_remote_merge_from_src(src: str) -> tuple[dict, list]:
|
|
14
|
+
"""
|
|
15
|
+
Creates a remote and merge entry from the src string.
|
|
16
|
+
"""
|
|
17
|
+
remotes = {}
|
|
18
|
+
merges = []
|
|
19
|
+
|
|
20
|
+
parts = src.split(" ", 1)
|
|
21
|
+
remotes["origin"] = parts[0]
|
|
22
|
+
merges.append(f"origin {parts[1]}")
|
|
23
|
+
|
|
24
|
+
return remotes, merges
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_origin_type(origin_value: str) -> OriginType:
|
|
28
|
+
"""
|
|
29
|
+
Determines the origin type based on the origin value.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
origin_value: The origin string to evaluate.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
The corresponding OriginType.
|
|
36
|
+
"""
|
|
37
|
+
# Pattern to match GitHub PR references: refs/pull/{pr_id}/head
|
|
38
|
+
pr_pattern = re.compile(r"^refs/pull/\d+/head$")
|
|
39
|
+
# Pattern to match that matches git reference hashes (40 hex characters)
|
|
40
|
+
ref_pattern = re.compile(r"^[a-z0-9]{40}$")
|
|
41
|
+
|
|
42
|
+
if pr_pattern.match(origin_value):
|
|
43
|
+
return OriginType.PR
|
|
44
|
+
elif ref_pattern.match(origin_value):
|
|
45
|
+
return OriginType.REF
|
|
46
|
+
else:
|
|
47
|
+
return OriginType.BRANCH
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def parse_remote_refspec_from_parts(parts: List[str], frozen_repo: Dict[str, Dict[str, str]]):
|
|
51
|
+
if len(parts) == 2:
|
|
52
|
+
parts.insert(1, "")
|
|
53
|
+
else:
|
|
54
|
+
warnings.warn(
|
|
55
|
+
"Deprecated src format: use <url> <sha> format for the src property",
|
|
56
|
+
DeprecationWarning,
|
|
57
|
+
)
|
|
58
|
+
remote_key, _, ref_spec = parts
|
|
59
|
+
ref_type = get_origin_type(ref_spec)
|
|
60
|
+
|
|
61
|
+
ref_name = None
|
|
62
|
+
remote_freezes = frozen_repo.get(remote_key, {})
|
|
63
|
+
|
|
64
|
+
if ref_spec in remote_freezes:
|
|
65
|
+
ref_type = OriginType.REF
|
|
66
|
+
ref_name = ref_spec
|
|
67
|
+
ref_spec = remote_freezes.get(ref_name)
|
|
68
|
+
|
|
69
|
+
return RefspecInfo(remote_key, ref_spec, ref_type, ref_name)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def get_with_syntax_check(name, data, key: str, type: Type):
|
|
73
|
+
result = data.get(key, type())
|
|
74
|
+
if not isinstance(result, type):
|
|
75
|
+
raise Exception(f"Key {key} not of proper syntax should be {str(type)} in {name} description")
|
|
76
|
+
return result
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def load_spec_file(config: Path, frozen: Path, workdir: Path) -> Optional[ProjectSpec]:
|
|
80
|
+
"""
|
|
81
|
+
Loads and parses the project specification from a YAML file.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
file_path: The path to the YAML specification file.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
A ProjectSpec object if successful, None otherwise.
|
|
88
|
+
"""
|
|
89
|
+
if not config.exists():
|
|
90
|
+
if config.is_relative_to("."):
|
|
91
|
+
config = config.resolve()
|
|
92
|
+
# If the file is not in the current directory, check inside the odoo subdirectory
|
|
93
|
+
odoo_config = config.parent / "odoo" / config.name
|
|
94
|
+
# TODO(franz): should use rich console for prettiness
|
|
95
|
+
if not odoo_config.exists():
|
|
96
|
+
print(f"Error: Neither '{config}' nor '{odoo_config}' exists.")
|
|
97
|
+
return None
|
|
98
|
+
config = odoo_config
|
|
99
|
+
else:
|
|
100
|
+
print(f"Error: File '{config}' does not exist.")
|
|
101
|
+
return None
|
|
102
|
+
|
|
103
|
+
workdir = workdir or config.parent
|
|
104
|
+
|
|
105
|
+
with config.open("r") as f:
|
|
106
|
+
try:
|
|
107
|
+
data: Dict[str, Any] = yaml.safe_load(f)
|
|
108
|
+
except yaml.YAMLError as e:
|
|
109
|
+
print(f"Error parsing YAML file '{config}': {e}")
|
|
110
|
+
return None
|
|
111
|
+
|
|
112
|
+
frozen_mapping: Dict[str, Dict[str, Dict[str, str]]] = {}
|
|
113
|
+
frozen_path = frozen or Path(config).with_name("frozen.yaml")
|
|
114
|
+
if frozen_path.exists():
|
|
115
|
+
try:
|
|
116
|
+
with frozen_path.open("r") as frozen_file:
|
|
117
|
+
loaded_freezes = yaml.safe_load(frozen_file) or {}
|
|
118
|
+
if isinstance(loaded_freezes, dict):
|
|
119
|
+
frozen_mapping = loaded_freezes
|
|
120
|
+
except yaml.YAMLError as e:
|
|
121
|
+
print(f"Error parsing frozen YAML file '{frozen_path}': {e}")
|
|
122
|
+
|
|
123
|
+
repos: Dict[str, RepoInfo] = {}
|
|
124
|
+
for repo_name, repo_data in data.items():
|
|
125
|
+
modules = get_with_syntax_check(repo_name, repo_data, "modules", list)
|
|
126
|
+
src = get_with_syntax_check(repo_name, repo_data, "src", str)
|
|
127
|
+
remotes = get_with_syntax_check(repo_name, repo_data, "remotes", dict)
|
|
128
|
+
merges = get_with_syntax_check(repo_name, repo_data, "merges", list)
|
|
129
|
+
shell_commands = get_with_syntax_check(repo_name, repo_data, "shell_command_after", list)
|
|
130
|
+
patch_globs_to_apply = get_with_syntax_check(repo_name, repo_data, "patch_globs", list)
|
|
131
|
+
target_folder = get_with_syntax_check(repo_name, repo_data, "target_folder", str)
|
|
132
|
+
locales = get_with_syntax_check(repo_name, repo_data, "locales", list)
|
|
133
|
+
|
|
134
|
+
frozen_repo = frozen_mapping.get(repo_name, {})
|
|
135
|
+
|
|
136
|
+
# Parse merges into RefspecInfo objects
|
|
137
|
+
refspec_infos: List[RefspecInfo] = []
|
|
138
|
+
if src:
|
|
139
|
+
# If src is defined, create a remote and merge entry from it
|
|
140
|
+
src_remotes, src_merges = make_remote_merge_from_src(src)
|
|
141
|
+
remotes.update(src_remotes)
|
|
142
|
+
merges = src_merges + merges
|
|
143
|
+
|
|
144
|
+
for merge_entry in merges:
|
|
145
|
+
parts = merge_entry.split(" ", 2)
|
|
146
|
+
refspec_info = parse_remote_refspec_from_parts(parts, frozen_repo)
|
|
147
|
+
refspec_infos.append(refspec_info)
|
|
148
|
+
|
|
149
|
+
repos[repo_name] = RepoInfo(
|
|
150
|
+
modules,
|
|
151
|
+
remotes,
|
|
152
|
+
refspec_infos,
|
|
153
|
+
shell_commands,
|
|
154
|
+
patch_globs_to_apply,
|
|
155
|
+
target_folder,
|
|
156
|
+
locales,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
return ProjectSpec(repos, workdir)
|