""" Deploy hash manifest at flash root (file_hashes.json). Updated by led-cli after directory uploads; used to skip unchanged files on the next deploy. Format: {"version": 1, "algorithm": "sha256", "files": {...}} """ import json import os MANIFEST_VERSION = 1 MANIFEST_FILENAME = "file_hashes.json" HASH_ALGO = "sha256" _SKIP_NAMES = frozenset({MANIFEST_FILENAME, "__pycache__"}) _SKIP_SUFFIXES = (".pyc", ".pyo") def _normalize_path(path): return path.replace("\\", "/").lstrip("/") def load(): """Return path -> sha256 hex map, or {} if missing or invalid.""" try: with open(MANIFEST_FILENAME, "r") as f: doc = json.load(f) except OSError: return {} if not isinstance(doc, dict): return {} files = doc.get("files") return files if isinstance(files, dict) else {} def save(files): """Write manifest (path keys use forward slashes, no leading slash).""" if not isinstance(files, dict): files = {} doc = { "version": MANIFEST_VERSION, "algorithm": HASH_ALGO, "files": files, } with open(MANIFEST_FILENAME, "w") as f: json.dump(doc, f) def _hash_file(path): import hashlib h = hashlib.sha256() with open(path, "rb") as f: while True: chunk = f.read(256) if not chunk: break h.update(chunk) return h.hexdigest() def _walk_dir(base, prefix, out): try: names = os.listdir(base) except OSError: return for name in names: if name in _SKIP_NAMES or name.endswith(_SKIP_SUFFIXES): continue full = base + "/" + name if base else name key = _normalize_path((prefix + "/" + name) if prefix else name) try: mode = os.stat(full)[0] except OSError: continue if mode & 0x4000: _walk_dir(full, key, out) else: out[key] = _hash_file(full) def rebuild(): """Rebuild manifest from root .py files plus patterns/ and lib/ trees.""" files = {} try: for name in os.listdir("."): if name in _SKIP_NAMES or name.endswith(_SKIP_SUFFIXES): continue try: mode = os.stat(name)[0] except OSError: continue if mode & 0x4000: if name in ("patterns", "lib"): _walk_dir(name, name, files) else: files[_normalize_path(name)] = _hash_file(name) except OSError: pass save(files) return files