Ctrl K
ComunidadGuías…

build.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env python3
import argparse
import os
import shutil
import subprocess
import sys
import zipfile
from pathlib import Path

TARGETS = (
    ("linux", "amd64", ""),
    ("linux", "arm64", ""),
    ("darwin", "amd64", ""),
    ("darwin", "arm64", ""),
    ("windows", "amd64", ".exe"),
)
ARCHIVE_TARGETS = {
    ("darwin", "amd64"): ("macos", "amd64", "vylk"),
    ("darwin", "arm64"): ("macos", "arm64", "vylk"),
    ("windows", "amd64"): ("windows", "amd64", "vylk.exe"),
}


def build_binary(os_name, arch, version, output, upx):
    """Build one target binary and return its path."""
    if (os_name, arch) not in {(target[0], target[1]) for target in TARGETS}:
        raise ValueError(f"unsupported target combination: {os_name}/{arch}")
    output = Path(output)
    output.parent.mkdir(parents=True, exist_ok=True)
    env = os.environ.copy()
    env["GOOS"] = os_name
    env["GOARCH"] = arch
    env["CGO_ENABLED"] = "0"
    ldflags = f"-s -w -X vylk/internal/server.version={version}"
    cmd = [
        "go",
        "build",
        "-trimpath",
        "-ldflags",
        ldflags,
        "-o",
        str(output),
        "./cmd/vylk",
    ]
    print(f"building {output.name}...", end=" ", flush=True)
    result = subprocess.run(
        cmd, env=env, capture_output=True, text=True, check=False
    )
    if result.returncode != 0:
        print("FAILED")
        print(result.stderr)
        raise SystemExit(1)
    print("ok")

    if upx and os_name == "linux":
        temporary = output.with_name(output.name + ".tmp")
        print("  compressing...", end=" ", flush=True)
        subprocess.run([upx, "-q", "-o", str(temporary), str(output)], check=True)
        os.replace(temporary, output)
        size = output.stat().st_size
        print(f"{size // 1024}K")
    return output


def archive_binary(binary, platform, arch, member, version, output_dir):
    """Place one binary in a Homebrew/direct-download-friendly ZIP."""
    archive = output_dir / f"vylk-{version}-{platform}-{arch}.zip"
    print(f"archiving {archive.name}...", end=" ", flush=True)
    with zipfile.ZipFile(archive, "w", compression=zipfile.ZIP_DEFLATED) as file:
        file.write(binary, arcname=member)
    print("ok")


def build_all(version, output_dir, upx):
    """Build every supported target and archive non-Linux binaries."""
    output_dir.mkdir(parents=True, exist_ok=True)
    binaries = {}
    for os_name, arch, _extension in TARGETS:
        name = f"vylk_{os_name}_{arch}_{version}"
        if os_name == "windows":
            name += ".exe"
        binaries[(os_name, arch)] = build_binary(
            os_name, arch, version, output_dir / name, upx
        )

    for (os_name, arch), (platform, archive_arch, member) in ARCHIVE_TARGETS.items():
        archive_binary(
            binaries[(os_name, arch)],
            platform,
            archive_arch,
            member,
            version,
            output_dir,
        )
        if os_name == "darwin":
            binaries[(os_name, arch)].unlink()


def target_output(args, version):
    """Return the requested single-target output path."""
    if args.output:
        return Path(args.output)
    extension = ".exe" if args.target_os == "windows" else ""
    name = f"vylk_{args.target_os}_{args.target_arch}_{version}{extension}"
    return Path(args.output_dir) / name


def project_version():
    """Use Docker's VERSION argument when supplied, otherwise VERSION file."""
    return os.environ.get("VERSION") or Path("VERSION").read_text().strip()


def main():
    parser = argparse.ArgumentParser(
        description="Build Vylk binaries and direct-download archives."
    )
    parser.add_argument(
        "--all",
        action="store_true",
        help="Build every supported target (the default).",
    )
    parser.add_argument(
        "--target-os",
        choices=sorted({target[0] for target in TARGETS}),
        help="Build one target OS for a packaging stage.",
    )
    parser.add_argument(
        "--target-arch",
        choices=sorted({target[1] for target in TARGETS}),
        help="Build one target architecture for a packaging stage.",
    )
    parser.add_argument(
        "--output-dir",
        default="dist",
        help="Directory for all-target output (default: dist).",
    )
    parser.add_argument(
        "--output",
        help="Output path for single-target mode.",
    )
    parser.add_argument(
        "--upx",
        choices=("auto", "never", "required"),
        default="auto",
        help="UPX policy for Linux binaries (default: auto).",
    )
    args = parser.parse_args()

    if bool(args.target_os) != bool(args.target_arch):
        parser.error("--target-os and --target-arch must be provided together")
    if args.all and args.target_os:
        parser.error("--all cannot be combined with single-target options")
    if args.output and not args.target_os:
        parser.error("--output requires single-target options")

    version = project_version()
    upx = None if args.upx == "never" else shutil.which("upx")
    if args.upx == "required" and not upx:
        parser.error("--upx required but UPX was not found")
    if args.target_os:
        if (args.target_os, args.target_arch) not in {
            (target[0], target[1]) for target in TARGETS
        }:
            parser.error(
                f"unsupported target combination: {args.target_os}/{args.target_arch}"
            )
        build_binary(
            args.target_os,
            args.target_arch,
            version,
            target_output(args, version),
            upx,
        )
    else:
        build_all(version, Path(args.output_dir), upx)


if __name__ == "__main__":
    sys.exit(main())