#!/usr/bin/env python3
"""Sign a release archive with the production RELEASE private key.

Exactly two ways to supply the key are supported (see docs/RELEASE_GUIDE.md):
  RELEASE_PRIVATE_KEY_FILE=/secure/path/release_private_key.pem
  RELEASE_PRIVATE_KEY="<PEM_CONTENT>"

There is intentionally NO mock/fallback signing path here: a release that
cannot find a real production key must fail loudly rather than silently
produce a "signed" artifact nobody can trust. Never logs key material.
"""
import sys
import os
import tempfile
import subprocess
from pathlib import Path


class SigningError(Exception):
    pass


def resolve_private_key_file() -> tuple[Path, bool]:
    """Resolve the production signing key from the two supported env vars.

    Returns (path_to_pem, is_temp_file). Raises SigningError with a clear,
    secret-free message if no key is configured.
    """
    pem_file = os.environ.get("RELEASE_PRIVATE_KEY_FILE")
    if pem_file:
        path = Path(pem_file)
        if not path.is_file():
            raise SigningError(f"Release aborted: RELEASE_PRIVATE_KEY_FILE does not exist: {pem_file}")
        return path, False

    pem_content = os.environ.get("RELEASE_PRIVATE_KEY")
    if pem_content and pem_content.strip():
        fd, tmp_path = tempfile.mkstemp(suffix=".pem")
        os.close(fd)
        tmp = Path(tmp_path)
        tmp.write_text(pem_content.strip() + "\n", encoding="utf-8")
        os.chmod(tmp, 0o600)
        return tmp, True

    raise SigningError(
        "Release aborted: no production signing key configured. "
        "Set RELEASE_PRIVATE_KEY_FILE=/secure/path/release_private_key.pem "
        "or RELEASE_PRIVATE_KEY=\"<PEM_CONTENT>\"."
    )


def sign_file(file_path: Path) -> str:
    key_path, is_temp = resolve_private_key_file()
    sig_out = file_path.with_suffix(file_path.suffix + ".sig_tmp")
    try:
        result = subprocess.run(
            ["openssl", "dgst", "-sha256", "-sign", str(key_path), "-out", str(sig_out), str(file_path)],
            capture_output=True,
        )
        if result.returncode != 0:
            # Never include stdout/stderr verbatim if it could echo key
            # material; openssl's own error output does not, so this is safe,
            # but we still avoid printing the key path contents themselves.
            raise SigningError(f"Release aborted: openssl signing failed (exit {result.returncode})")
        return sig_out.read_bytes().hex()
    finally:
        if sig_out.exists():
            sig_out.unlink()
        if is_temp and key_path.exists():
            key_path.unlink()


def main():
    if len(sys.argv) < 2:
        print("Usage: sign_release.py <file_to_sign>", file=sys.stderr)
        sys.exit(1)

    file_path = Path(sys.argv[1])
    try:
        print(sign_file(file_path))
    except SigningError as exc:
        print(str(exc), file=sys.stderr)
        sys.exit(1)


if __name__ == "__main__":
    main()
