feat: strengthen BSP and PSP validation and handoff

This commit is contained in:
tangfahong committed 2026-09-24 11:52:22 +08:00
1 parent bcea35a91e
commit 1641fac631
39 files changed
+2695 -90

No files matched your search

@@ -87,10 +87,33 @@ def under(path: Path, root: Path) -> bool:
return False
def iter_regular_files(root: Path) -> Iterator[Path]:
"""Yield repository files without following runtime or Git directory links."""
for current, dirnames, filenames in os.walk(root, followlinks=False):
current_path = Path(current)
dirnames[:] = sorted(
name
for name in dirnames
if name != ".git" and not (current_path / name).is_symlink()
)
for name in sorted(filenames):
path = current_path / name
if path.is_symlink() or not path.is_file():
continue
yield path
def probe_path_exists(path: Path) -> tuple[bool, str | None]:
try:
return path.exists(), None
except OSError as exc:
return False, str(exc)
def collect_json(root: Path, errors: list[str]) -> dict[str, Any]:
payloads: dict[str, Any] = {}
for path in sorted(root.rglob("*.json")):
if ".git" in path.parts:
for path in iter_regular_files(root):
if path.suffix.lower() != ".json":
continue
relative = path.relative_to(root).as_posix()
try:
@@ -102,8 +125,8 @@ def collect_json(root: Path, errors: list[str]) -> dict[str, Any]:
def validate_ini(root: Path, errors: list[str]) -> int:
count = 0
for path in sorted(root.rglob("*.ini")):
if ".git" in path.parts:
for path in iter_regular_files(root):
if path.suffix.lower() != ".ini":
continue
parser = configparser.ConfigParser(interpolation=None, strict=False)
try:
@@ -200,11 +223,9 @@ def validate_sensor_baseline(
errors.append("非 CV181X/CV184X 项目不应提供 CV18 Sensor 枚举校验参数")
return result
candidates = sorted(
path
for path in psp_root.rglob("sensor_cfg.ini")
if ".git" not in path.parts and path.is_file() and not path.is_symlink()
)
candidates = [
path for path in iter_regular_files(psp_root) if path.name == "sensor_cfg.ini"
]
if len(candidates) != 1:
errors.append(
f"CV18 PSP 必须唯一定位一个 sensor_cfg.ini,实际 {len(candidates)} 个"
@@ -320,8 +341,8 @@ def validate_sensor_baseline(
def validate_shell(root: Path, errors: list[str]) -> int:
count = 0
for path in sorted(root.rglob("*.sh")):
if ".git" in path.parts:
for path in iter_regular_files(root):
if path.suffix.lower() != ".sh":
continue
result = subprocess.run(
["bash", "-n", str(path)],
@@ -346,12 +367,9 @@ def path_risk(path: Path) -> str | None:
def text_absolute_paths(root: Path) -> Iterator[tuple[str, str, str]]:
for path in sorted(root.rglob("*")):
for path in iter_regular_files(root):
if (
not path.is_file()
or path.is_symlink()
or ".git" in path.parts
or path.suffix.lower() not in TEXT_PATH_SUFFIXES
path.suffix.lower() not in TEXT_PATH_SUFFIXES
or path.stat().st_size > 4 * 1024 * 1024
):
continue
@@ -409,7 +427,7 @@ def validate_absolute_paths(
continue
seen.add(key)
path = Path(value).resolve(strict=False)
exists = path.exists()
exists, access_error = probe_path_exists(path)
allowed_by = next((item for item in normalized_allowed if under(path, item)), None)
risk_root = None if allowed_by is not None else path_risk(path)
build_input = source in BUILD_INPUT_JSONS
@@ -419,12 +437,19 @@ def validate_absolute_paths(
"path": value,
"resolved_path": str(path),
"exists": exists,
"access_error": access_error,
"build_input": build_input,
"allowed_by": str(allowed_by) if allowed_by else None,
"personal_or_temporary_root": risk_root,
}
entries.append(entry)
if not exists:
if access_error:
message = (
f"外部绝对路径无法在当前主机检查 {source}:{location}: "
f"{value}: {access_error}"
)
(errors if build_input else warnings).append(message)
elif not exists:
message = f"外部绝对路径不存在 {source}:{location}: {value}"
if build_input:
errors.append(f"外部绝对路径不存在 {source}:{location}: {value}")
@@ -503,7 +528,7 @@ def scan_overrides(
if not psp_dir.is_dir():
errors.append(f"PSP 配置目录不存在: {psp_dir}")
continue
for app_path in sorted(path for path in app_dir.rglob("*") if path.is_file()):
for app_path in iter_regular_files(app_dir):
relative = app_path.relative_to(app_dir)
psp_path = psp_dir / relative
if not psp_path.is_file():
@@ -533,6 +558,10 @@ def scan_overrides(
def build_bsp_lock(dependency: dict[str, object], warnings: list[str]) -> dict[str, object]:
permissions = dependency.get("output_permissions")
permissions = permissions if isinstance(permissions, dict) else {}
live_check = permissions.get("current_check")
live_check = live_check if isinstance(live_check, dict) else {}
return {
"schema_version": 1,
"handoff_path": dependency.get("manifest_path"),
@@ -548,6 +577,13 @@ def build_bsp_lock(dependency: dict[str, object], warnings: list[str]) -> dict[s
"imgs_version_path": dependency.get("imgs_version_path"),
"artifacts": dependency.get("artifacts", []),
"image_partition_margin": dependency.get("image_partition_margin"),
"output_permissions": {
"policy": permissions.get("policy"),
"evidence_path": dependency.get("output_permissions_path"),
"output_root": permissions.get("output_root"),
"imgs_version_path": permissions.get("imgs_version_path"),
"passed": live_check.get("passed") is True,
},
"mixed_build": None,
"warnings": warnings,
}
@@ -736,6 +772,7 @@ def validate_psp(
expected_sdk=expected_bsp_sdk,
expected_build_type=expected_build_type,
expected_system_form=expected_system_form,
repair_output_permissions=True,
)
if bsp_result.get("passed") is not True:
errors.extend(f"BSP handoff: {item}" for item in bsp_result.get("errors", []))