diff --git a/scripts/generate_pack.py b/scripts/generate_pack.py index 3ec62374..f8b6c90a 100644 --- a/scripts/generate_pack.py +++ b/scripts/generate_pack.py @@ -291,7 +291,7 @@ def generate_pack( missing_files = [] user_provided = [] seen_destinations = set() - # Per-file status: worst status wins (missing > wrong_hash > ok) + # Per-file status: worst status wins (missing > untested > ok) file_status: dict[str, str] = {} file_reasons: dict[str, str] = {} @@ -365,15 +365,20 @@ def generate_pack( from verify import check_inside_zip inner_md5 = file_entry.get("md5", "") inner_result = check_inside_zip(local_path, zf_name, inner_md5) - if inner_result != "ok": - file_status[dedup_key] = "wrong_hash" - reason = f"{zf_name} hash mismatch inside ZIP" - file_reasons[dedup_key] = reason - else: + if inner_result == "ok": file_status.setdefault(dedup_key, "ok") + elif inner_result == "not_in_zip": + file_status[dedup_key] = "untested" + file_reasons[dedup_key] = f"{zf_name} not found inside ZIP" + elif inner_result == "error": + file_status[dedup_key] = "untested" + file_reasons[dedup_key] = f"cannot read ZIP" + else: + file_status[dedup_key] = "untested" + file_reasons[dedup_key] = f"{zf_name} MD5 mismatch inside ZIP" else: - file_status[dedup_key] = "wrong_hash" - file_reasons[dedup_key] = "container hash mismatch" + file_status[dedup_key] = "untested" + file_reasons[dedup_key] = "hash mismatch" else: file_status.setdefault(dedup_key, "ok") @@ -440,22 +445,24 @@ def generate_pack( total_files += 1 files_ok = sum(1 for s in file_status.values() if s == "ok") - files_wrong = sum(1 for s in file_status.values() if s == "wrong_hash") + files_untested = sum(1 for s in file_status.values() if s == "untested") files_miss = sum(1 for s in file_status.values() if s == "missing") total_checked = len(file_status) parts = [f"{files_ok}/{total_checked} files OK"] - if files_wrong: - parts.append(f"{files_wrong} wrong hash") + if files_untested: + parts.append(f"{files_untested} untested") if files_miss: parts.append(f"{files_miss} missing") extras_msg = f", {extra_count} extras" if extra_count else "" print(f" {zip_path}: {total_files} files packed{extras_msg}, {', '.join(parts)} [{verification_mode}]") - for key, reason in file_reasons.items(): - print(f" WRONG HASH: {key} — {reason}") + for key, reason in sorted(file_reasons.items()): + status = file_status.get(key, "") + label = "UNTESTED" + print(f" {label}: {key} — {reason}") for name in missing_files: - print(f" MISSING: {name}") + print(f" MISSING: {name}") return zip_path diff --git a/scripts/verify.py b/scripts/verify.py index 7ff66b04..00b6efa2 100644 --- a/scripts/verify.py +++ b/scripts/verify.py @@ -35,9 +35,9 @@ DEFAULT_PLATFORMS_DIR = "platforms" class Status: - OK = "ok" # Verified - hash matches (or existence for existence-only platforms) - UNTESTED = "untested" # File present but hash mismatch (Batocera term) - MISSING = "missing" # File not found at all + OK = "ok" # hash matches (or exists for existence-only) + UNTESTED = "untested" # file present, hash not confirmed (Batocera terminology) + MISSING = "missing" # file not found at all def check_inside_zip(container: str, file_name: str, expected_md5: str) -> str: @@ -114,15 +114,16 @@ def verify_entry_md5( elif result != "not_in_zip": found_in_zip = True if had_error and not found_in_zip: - reason = f"{local_path} is not a valid ZIP or read error" - elif not found_in_zip: - reason = f"{zipped_file} not found inside ZIP" - else: - reason = f"{zipped_file} MD5 mismatch inside ZIP" - return { - "name": name, "status": Status.UNTESTED, "path": local_path, - "reason": reason, - } + # Can't read the ZIP at all + return {"name": name, "status": Status.UNTESTED, "path": local_path, + "reason": f"{local_path} is not a valid ZIP or read error"} + if not found_in_zip: + # Inner file not in the ZIP — can't verify + return {"name": name, "status": Status.UNTESTED, "path": local_path, + "reason": f"{zipped_file} not found inside ZIP"} + # Inner file found but MD5 doesn't match — wrong version + return {"name": name, "status": Status.UNTESTED, "path": local_path, + "reason": f"{zipped_file} MD5 mismatch inside ZIP"} if not md5_list: return {"name": name, "status": Status.OK, "path": local_path} @@ -154,7 +155,7 @@ def verify_entry_md5( return { "name": name, "status": Status.UNTESTED, "path": local_path, - "expected_md5": md5_list[0] if md5_list else "", "actual_md5": actual_md5, + "reason": f"expected {md5_list[0][:12]}… got {actual_md5[:12]}…", } @@ -220,17 +221,15 @@ def verify_platform(config: dict, db: dict) -> dict: dest = file_entry.get("destination", file_entry.get("name", "")) if not dest: dest = f"{sys_id}/{file_entry.get('name', '')}" + # Worst status wins: missing > untested > ok cur = result["status"] prev = file_status.get(dest) - if prev is None: + severity = {Status.OK: 0, Status.UNTESTED: 1, Status.MISSING: 2} + if prev is None or severity.get(cur, 0) > severity.get(prev, 0): file_status[dest] = cur - elif cur == Status.MISSING: - file_status[dest] = Status.MISSING - elif cur == Status.UNTESTED and prev != Status.MISSING: - file_status[dest] = Status.UNTESTED files_ok = sum(1 for s in file_status.values() if s == Status.OK) - files_mismatch = sum(1 for s in file_status.values() if s == Status.UNTESTED) + files_untested = sum(1 for s in file_status.values() if s == Status.UNTESTED) files_missing = sum(1 for s in file_status.values() if s == Status.MISSING) return { @@ -238,7 +237,7 @@ def verify_platform(config: dict, db: dict) -> dict: "verification_mode": mode, "total_files": len(file_status), "files_ok": files_ok, - "files_mismatch": files_mismatch, + "files_untested": files_untested, "files_missing": files_missing, "details": results, } @@ -303,13 +302,13 @@ def main(): mode = result["verification_mode"] total = result["total_files"] ok = result["files_ok"] - mismatch = result["files_mismatch"] + untested = result["files_untested"] miss = result["files_missing"] label = " / ".join(group) parts = [f"{ok}/{total} files OK"] - if mismatch: - parts.append(f"{mismatch} wrong hash") + if untested: + parts.append(f"{untested} untested") if miss: parts.append(f"{miss} missing") print(f"{label}: {', '.join(parts)} [{mode}]") @@ -317,9 +316,7 @@ def main(): for d in result["details"]: if d["status"] == Status.UNTESTED: reason = d.get("reason", "") - if not reason and "expected_md5" in d: - reason = f"expected {d['expected_md5'][:12]}… got {d['actual_md5'][:12]}…" - print(f" WRONG HASH: {d['system']}/{d['name']} — {reason}") + print(f" UNTESTED: {d['system']}/{d['name']} — {reason}") for d in result["details"]: if d["status"] == Status.MISSING: