From 197c5ba8a29cc51089d0f80d0f56e065d55541fc Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Tue, 14 Apr 2026 22:30:56 +0200 Subject: [PATCH] cli: implement m2pack diff and export-runtime-key wrappers diff promotes m2pack's added/removed/changed/unchanged counts into data.stats when m2pack reports them as lists or ints. export-runtime-key follows the real binary's --key/--public-key/--output/--key-id/--format surface (not the original plan's --pack/--master-key). --- src/metin_release/commands/m2pack_diff.py | 45 +++++++++++++++ .../commands/m2pack_export_runtime_key.py | 57 +++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 src/metin_release/commands/m2pack_diff.py create mode 100644 src/metin_release/commands/m2pack_export_runtime_key.py diff --git a/src/metin_release/commands/m2pack_diff.py b/src/metin_release/commands/m2pack_diff.py new file mode 100644 index 0000000..4eebf11 --- /dev/null +++ b/src/metin_release/commands/m2pack_diff.py @@ -0,0 +1,45 @@ +"""m2pack diff: wrap ``m2pack diff`` to compare directories and/or .m2p archives.""" + +from __future__ import annotations + +import argparse +from pathlib import Path + +from ..result import Result +from ._m2pack_runner import run_m2pack + + +def add_parser(sub: argparse._SubParsersAction) -> argparse.ArgumentParser: + p = sub.add_parser( + "diff", + help="Diff two directories and/or .m2p archives (left vs right).", + ) + p.add_argument("--left", required=True, type=Path, help="Left side: directory or .m2p archive.") + p.add_argument("--right", required=True, type=Path, help="Right side: directory or .m2p archive.") + return p + + +def run(ctx, args: argparse.Namespace) -> Result: + argv = ["--left", str(args.left), "--right", str(args.right)] + raw = run_m2pack("diff", argv) + + # Best-effort promotion of the diff counts m2pack reports. Fall back + # gracefully when the key is missing or the shape differs. + stats: dict[str, object] = {} + for key in ("added", "removed", "changed", "unchanged"): + value = raw.get(key) + if isinstance(value, int): + stats[f"{key}_count"] = value + elif isinstance(value, list): + stats[f"{key}_count"] = len(value) + + ok = bool(raw.get("ok", True)) + return Result( + command="m2pack diff", + ok=ok, + status="diffed" if ok else "failed", + data={ + "stats": stats, + "m2pack": raw, + }, + ) diff --git a/src/metin_release/commands/m2pack_export_runtime_key.py b/src/metin_release/commands/m2pack_export_runtime_key.py new file mode 100644 index 0000000..78773e0 --- /dev/null +++ b/src/metin_release/commands/m2pack_export_runtime_key.py @@ -0,0 +1,57 @@ +"""m2pack export-runtime-key: wrap ``m2pack export-runtime-key``. + +Emits a launcher runtime-key payload in either ``json`` or ``blob`` form +from the master content key + signing public key. The plan originally +described this as ``--pack --master-key --out``; the real m2pack CLI +uses ``--key --public-key --key-id --format --output``, so we follow the +real tool. +""" + +from __future__ import annotations + +import argparse +from pathlib import Path + +from ..result import Result +from ._m2pack_runner import run_m2pack + + +def add_parser(sub: argparse._SubParsersAction) -> argparse.ArgumentParser: + p = sub.add_parser( + "export-runtime-key", + help="Export a launcher runtime-key payload (json or blob form).", + ) + p.add_argument("--key", required=True, type=Path, help="Master content key file.") + p.add_argument("--public-key", required=True, type=Path, help="Ed25519 public key file.") + p.add_argument("--output", required=True, type=Path, help="Output payload path.") + p.add_argument("--key-id", type=int, help="Content key id (default 1).") + p.add_argument( + "--format", + choices=("json", "blob"), + help="Payload format (default json).", + ) + return p + + +def run(ctx, args: argparse.Namespace) -> Result: + argv = [ + "--key", str(args.key), + "--public-key", str(args.public_key), + "--output", str(args.output), + ] + if args.key_id is not None: + argv.extend(["--key-id", str(args.key_id)]) + if args.format is not None: + argv.extend(["--format", args.format]) + + raw = run_m2pack("export-runtime-key", argv) + ok = bool(raw.get("ok", True)) + return Result( + command="m2pack export-runtime-key", + ok=ok, + status="exported" if ok else "failed", + data={ + "artifacts": {"runtime_key_path": str(args.output)}, + "m2pack": raw, + }, + )