feat: strengthen BSP and PSP validation and handoff
This commit is contained in:
1 parent
bcea35a91e
commit
1641fac631
39 files changed
+2695
-90
No files matched your search
@@ -456,7 +456,7 @@ def inspect_project(target: Path, psp_root: Path = DEFAULT_PSP_ROOT) -> dict[str
|
||||
|
||||
|
||||
def prepare(
|
||||
reference: Path,
|
||||
reference: Path | None,
|
||||
target: Path,
|
||||
manifest_path: Path,
|
||||
old_tokens: list[str],
|
||||
@@ -464,9 +464,10 @@ def prepare(
|
||||
psp_root: Path = DEFAULT_PSP_ROOT,
|
||||
) -> dict[str, object]:
|
||||
psp_root = psp_root_directory(psp_root)
|
||||
reference = ordinary_directory(reference, "reference")
|
||||
if reference is not None:
|
||||
reference = ordinary_directory(reference, "reference")
|
||||
target = project_directory(target, psp_root, "target")
|
||||
if reference == target or reference in target.parents or target in reference.parents:
|
||||
if reference is not None and (reference == target or reference in target.parents or target in reference.parents):
|
||||
raise GuardError("reference、target 不能互相包含")
|
||||
target_git = git_state(target)
|
||||
target_repository = repository_identity(target, target_git)
|
||||
@@ -482,17 +483,22 @@ def prepare(
|
||||
if manifest_path.exists() or manifest_path.is_symlink():
|
||||
raise GuardError(f"manifest 已存在,禁止覆盖: {manifest_path}")
|
||||
|
||||
links = scan_symlinks(reference)
|
||||
if links:
|
||||
raise GuardError("参考 PSP 包含软链接,未复制: " + ", ".join(links))
|
||||
credentials = scan_credentials(reference)
|
||||
if credentials:
|
||||
raise GuardError("参考 PSP 包含疑似凭据文件,未复制: " + ", ".join(credentials))
|
||||
source_git = git_state(reference)
|
||||
if reference is not None:
|
||||
links = scan_symlinks(reference)
|
||||
if links:
|
||||
raise GuardError("参考 PSP 包含软链接,未复制: " + ", ".join(links))
|
||||
credentials = scan_credentials(reference)
|
||||
if credentials:
|
||||
raise GuardError("参考 PSP 包含疑似凭据文件,未复制: " + ", ".join(credentials))
|
||||
source_git = git_state(reference) if reference is not None else None
|
||||
staging = target.parent / f".{target.name}.psp-staging-{uuid.uuid4().hex[:12]}"
|
||||
if staging.exists() or staging.is_symlink():
|
||||
raise GuardError(f"随机 staging 已存在: {staging}")
|
||||
if source_git.get("dirty") is True and not include_dirty:
|
||||
if reference is None:
|
||||
staging.mkdir()
|
||||
baseline_mode = "empty"
|
||||
warnings = ["无参考 PSP:必须按 BSP handoff、硬件资料、应用 schema 和精确 Release 建立每个配置;不可将 Release 当完整 PSP"]
|
||||
elif source_git.get("dirty") is True and not include_dirty:
|
||||
copy_committed_head(reference, staging, source_git)
|
||||
baseline_mode = "committed-head"
|
||||
warnings = ["参考 PSP 工作区有未提交内容;staging 使用已提交 HEAD,不包含这些修改"]
|
||||
@@ -512,7 +518,7 @@ def prepare(
|
||||
"schema_version": 2,
|
||||
"created_at": now_utc(),
|
||||
"psp_root": str(psp_root),
|
||||
"reference": str(reference),
|
||||
"reference": str(reference) if reference is not None else None,
|
||||
"target": str(target),
|
||||
"staging": str(staging),
|
||||
"target_mode": "precreated-empty-repository",
|
||||
@@ -521,7 +527,7 @@ def prepare(
|
||||
"reference_baseline": baseline_mode,
|
||||
"include_dirty_reference": include_dirty,
|
||||
"old_tokens": sorted(set(token for token in old_tokens if token)),
|
||||
"excluded_paths": excluded_paths(reference),
|
||||
"excluded_paths": excluded_paths(reference) if reference is not None else [],
|
||||
"source_inventory": source_before,
|
||||
"staging_inventory_before_edits": staging_inventory,
|
||||
"warnings": warnings,
|
||||
@@ -529,7 +535,7 @@ def prepare(
|
||||
write_json_atomic(manifest_path, payload)
|
||||
return {
|
||||
"passed": True,
|
||||
"reference": str(reference),
|
||||
"reference": str(reference) if reference is not None else None,
|
||||
"target": str(target),
|
||||
"staging": str(staging),
|
||||
"psp_root": str(psp_root),
|
||||
@@ -550,12 +556,13 @@ def verify(
|
||||
) -> dict[str, object]:
|
||||
manifest_path = manifest_path.expanduser().resolve(strict=True)
|
||||
manifest = load_json(manifest_path)
|
||||
reference = ordinary_directory(Path(str(manifest.get("reference"))), "reference")
|
||||
reference = (ordinary_directory(Path(str(manifest["reference"])), "reference")
|
||||
if manifest.get("reference") is not None else None)
|
||||
staging = ordinary_directory(Path(str(manifest.get("staging"))), "staging")
|
||||
source = manifest.get("source_inventory")
|
||||
if not isinstance(source, dict):
|
||||
raise GuardError("manifest 缺少 source_inventory")
|
||||
if not reference_matches_manifest(reference, manifest):
|
||||
if reference is not None and not reference_matches_manifest(reference, manifest):
|
||||
raise GuardError("prepare 后参考 PSP 发生变化;必须重新 prepare")
|
||||
links = scan_symlinks(staging)
|
||||
credentials = scan_credentials(staging)
|
||||
@@ -586,7 +593,7 @@ def verify(
|
||||
"schema_version": 1,
|
||||
"checked_at": now_utc(),
|
||||
"manifest": str(manifest_path),
|
||||
"reference": str(reference),
|
||||
"reference": str(reference) if reference is not None else None,
|
||||
"staging": str(staging),
|
||||
"target": manifest.get("target"),
|
||||
"allowed_paths": sorted(allowed),
|
||||
@@ -693,7 +700,8 @@ def audit_final(
|
||||
raise GuardError("manifest 缺少 psp_root")
|
||||
psp_root = psp_root_directory(Path(root_value))
|
||||
target = project_directory(Path(str(manifest.get("target"))), psp_root, "target")
|
||||
reference = ordinary_directory(Path(str(manifest.get("reference"))), "reference")
|
||||
reference = (ordinary_directory(Path(str(manifest["reference"])), "reference")
|
||||
if manifest.get("reference") is not None else None)
|
||||
source = manifest.get("source_inventory")
|
||||
if not isinstance(source, dict):
|
||||
raise GuardError("manifest 缺少 source_inventory")
|
||||
@@ -732,8 +740,9 @@ def audit_final(
|
||||
"schema_version": 1,
|
||||
"checked_at": now_utc(),
|
||||
"manifest": str(manifest_path),
|
||||
"reference": str(reference),
|
||||
"reference_drifted_since_prepare": not reference_matches_manifest(reference, manifest),
|
||||
"reference": str(reference) if reference is not None else None,
|
||||
"reference_drifted_since_prepare": (not reference_matches_manifest(reference, manifest)
|
||||
if reference is not None else False),
|
||||
"target": str(target),
|
||||
"allowed_paths": sorted(allowed),
|
||||
"allowed_residue_paths": sorted(residue_allow),
|
||||
@@ -782,6 +791,11 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
prepare_parser.add_argument("--old-token", action="append", default=[])
|
||||
prepare_parser.add_argument("--include-dirty", action="store_true")
|
||||
|
||||
empty_parser = subparsers.add_parser("prepare-empty", help="无参考 PSP:为预创建空仓库准备空 staging")
|
||||
empty_parser.add_argument("--psp-root", type=Path, default=DEFAULT_PSP_ROOT)
|
||||
empty_parser.add_argument("--target", type=Path, required=True)
|
||||
empty_parser.add_argument("--manifest", type=Path, required=True)
|
||||
|
||||
verify_parser = subparsers.add_parser("verify", help="审计 staging 的精确差异与残留")
|
||||
verify_parser.add_argument("--manifest", type=Path, required=True)
|
||||
verify_parser.add_argument("--allow-path", action="append", default=[])
|
||||
@@ -817,6 +831,8 @@ def main() -> int:
|
||||
args.include_dirty,
|
||||
args.psp_root,
|
||||
)
|
||||
elif args.command == "prepare-empty":
|
||||
result = prepare(None, args.target, args.manifest, [], False, args.psp_root)
|
||||
elif args.command == "verify":
|
||||
result = verify(args.manifest, args.allow_path, args.allow_residue, args.report)
|
||||
elif args.command == "finalize":
|
||||
|
||||
Reference in new issue
Block a user