#!/usr/bin/env python3 """校验已构建镜像的 BUILDINFO 供应链清单(事后 lockfile 检查)。 事前 apt/pip 精确 lockfile 无法在本轨生成:apt 版本在构建 宿主上解析、pip 版本在客机里解析,没有构建宿主就编不出 版本号——本脚本绝不编造。构建时 build.sh 已从客机读回 dpkg + pip freeze 写进 BUILDINFO.versions(事后 lockfile), 本脚本校验它:versions 非空、tag 与预期一致、可选的冻结 清单(--frozen JSON,形如 {"dpkg": ["pkg version", ...]}) 逐项比对,漂移即退出非 0。 用法: python3 check_lockfile.py out/BUILDINFO.json [--tag TAG] [--frozen frozen.json] """ import json import sys def main(argv): if len(argv) < 2: print("用法:check_lockfile.py BUILDINFO.json [--tag TAG] " "[--frozen frozen.json]", file=sys.stderr) return 2 path = argv[1] tag = None frozen_path = None i = 2 while i < len(argv): if argv[i] == "--tag" and i + 1 < len(argv): tag = argv[i + 1] i += 2 elif argv[i] == "--frozen" and i + 1 < len(argv): frozen_path = argv[i + 1] i += 2 else: print("未知参数:%s" % argv[i], file=sys.stderr) return 2 with open(path) as fh: data = json.load(fh) if tag is not None and data.get("tag") != tag: print("tag 不一致:%s != %s" % (data.get("tag"), tag), file=sys.stderr) return 1 versions = data.get("versions") or {} if not versions.get("dpkg") or not versions.get("pip"): print("BUILDINFO.versions.dpkg/pip 为空,供应链清单缺失", file=sys.stderr) return 1 if frozen_path: with open(frozen_path) as fh: frozen = json.load(fh) for kind in ("dpkg", "pip"): want = set(frozen.get(kind) or []) got = set(versions.get(kind) or []) missing = want - got if missing: print("%s 漂移:冻结清单里有、构建产物里无:%s" % (kind, sorted(missing)), file=sys.stderr) return 1 print("lockfile 校验通过:tag=%s dpkg=%d pip=%d" % (data.get("tag"), len(versions["dpkg"]), len(versions["pip"]))) return 0 if __name__ == "__main__": raise SystemExit(main(sys.argv))