From 70d20f0f182fddd73a03b88ac2a3565e5ead6087 Mon Sep 17 00:00:00 2001 From: Jan Nedbal Date: Tue, 14 Apr 2026 22:30:45 +0200 Subject: [PATCH] cli: add m2pack binary discovery helper Resolve the m2pack-secure binary via M2PACK_BINARY env var or PATH, raising ValidationError(m2pack_not_found) when neither works. --- src/metin_release/m2pack_binary.py | 51 ++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 src/metin_release/m2pack_binary.py diff --git a/src/metin_release/m2pack_binary.py b/src/metin_release/m2pack_binary.py new file mode 100644 index 0000000..1ce41c9 --- /dev/null +++ b/src/metin_release/m2pack_binary.py @@ -0,0 +1,51 @@ +"""Resolve the m2pack binary for the m2pack wrapper subcommands. + +Resolution order: + +1. ``M2PACK_BINARY`` environment variable, if set, non-empty, and points at + an existing file. +2. :func:`shutil.which` on ``m2pack``. + +If neither works we raise :class:`~metin_release.errors.ValidationError` so +the CLI exits 1 with a clear, actionable message. Import of this module +must never trigger filesystem access — all discovery is runtime. +""" + +from __future__ import annotations + +import os +import shutil +from pathlib import Path + +from .errors import ValidationError + + +ENV_VAR = "M2PACK_BINARY" + +_NOT_FOUND_HINT = ( + "m2pack binary not found. Set the M2PACK_BINARY environment variable to " + "an absolute path, or make `m2pack` available on PATH. Build it from " + "https://gitea.jakubkadlec.dev/metin-server/m2pack-secure (see its " + "CMakeLists.txt)." +) + + +def resolve_m2pack_binary(env: dict[str, str] | None = None) -> Path: + """Return the resolved path to the m2pack binary or raise ValidationError.""" + environ = env if env is not None else os.environ + override = (environ.get(ENV_VAR) or "").strip() + if override: + candidate = Path(override).expanduser() + if candidate.is_file(): + return candidate.resolve() + raise ValidationError( + f"{ENV_VAR}={override!r} does not point at an existing file. " + f"{_NOT_FOUND_HINT}", + error_code="m2pack_not_found", + ) + + which = shutil.which("m2pack") + if which: + return Path(which).resolve() + + raise ValidationError(_NOT_FOUND_HINT, error_code="m2pack_not_found")