{ lib, erofs-utils, runCommand, python3Minimal, bubblewrap, writeClosure, }: { name ? "${label}.erofs", label ? "image", roots ? [ ], rootsExclude ? [ ], erofsArgs ? [ "--mount-point=/nix/store" "-z" "lz4hc,level=3" "-T0" "-L" label "--all-root" ], ... }@attrs: let old = writeClosure rootsExclude; new = writeClosure roots; attrs' = lib.removeAttrs [ "name" "roots" "rootsExclude" "erofsArgs" ] attrs; in runCommand name ( attrs // { __structuredAttrs = true; inherit name label; unsafeDiscardReferences.out = true; nativeBuildInputs = [ erofs-utils bubblewrap python3Minimal ] ++ attrs.nativeBuildInputs or [ ]; inherit old new erofsArgs; } ) '' mkdir -p store python3 << EOF import hashlib, json, os, subprocess from uuid import UUID def uuid_from_node(node: int = 0) -> UUID: return UUID(fields=(*((0,) * 5), node)) out_path = os.environ.get("out") default_uuid = uuid_from_node((2**48 - 1) & int.from_bytes(hashlib.sha256(out_path.encode("ascii")).digest())) with open(os.environ.get("NIX_ATTRS_JSON_FILE"), mode="r") as f: attrs = json.load(f) name = attrs["name"] with open(attrs["old"], mode="r") as f: old_paths = set(ln.rstrip() for ln in f) with open(attrs["new"], mode="r") as f: new_paths = [ln.rstrip() for ln in f] new_paths = [p for p in new_paths if p not in old_paths] erofs_args = attrs.get("erofsArgs", []) if not any(a.startswith("-U") for a in erofs_args): erofs_args.extend(["-U", str(default_uuid)]) subprocess.run( check=True, env=os.environ, args=[ "bwrap", "--dev-bind", "/", "/", "--chdir", os.getcwd(), *[a for p in new_paths for a in [f"--ro-bind", p, f"{os.getcwd()}/store/{os.path.basename(p)}"]], "--", "mkfs.erofs", *attrs.get("erofsArgs", []), out_path, "store", ], ) EOF ''