Mascota Batamanta

Empaqueta tus aplicaciones Elixir como ejecutables 100% autocontenidos. No requiere Erlang/Elixir en la máquina destino.


Hex Version HexDocs CI Status Self-contained ERTS Embedded


Características

  • Binarios autocontenidos: Un único archivo con tu app + ERTS incluido
  • Compilación cruzada: Construye para Linux, macOS y Windows desde cualquier plataforma
  • Compresión Zstandard: Óptimo equilibrio entre tamaño y velocidad
  • Formatos Versátiles: Soporte para :release (completo) y :escript (ligero)
  • Limpieza Automática: Borra temporales de construcción tras éxito, manteniendo el sistema limpio pero preservando la caché de ERTS
  • Aislamiento del Entorno: Aísla automáticamente el proceso de construcción de gestores de versiones (asdf, mise, kerl) para evitar discrepancias de versión
  • Caché Inteligente: Descargas de ERTS locales con bloqueos para evitar condiciones de carrera en compilaciones concurrentes

Requisitos

  • Erlang/OTP 25+
  • Elixir 1.15+
  • Rust (cargo)
  • Zstandard (zstd)

Dependencias del Banner (Opcional)

Cuando show_banner: true (por defecto), el proceso de construcción muestra un banner con imagen en la terminal. Para habilitar el soporte completo de imágenes en todos los terminales, instala estas dependencias:

macOS

# Para soporte Sixel (Alacritty, Ghostty, otros terminales)
brew install libsixel

# Opcional: para fallback a ASCII art
# img2txt viene incluido en libsixel

Linux

# Ubuntu/Debian
sudo apt install libsixel-tools

# Arch Linux
sudo pacman -S libsixel

# Fedora
sudo dnf install libsixel

Compatibilidad de Terminales

TerminalProtocoloRequiere
iTerm2Inline ImagesIncorporado
GhosttyProtocolo KittyIncorporado
WezTermProtocolo KittyIncorporado
AlacrittyProtocolo KittyIncorporado
KittyProtocolo KittyIncorporado
VS CodeSixellibsixel
footSixellibsixel
Otros terminalesFallback ASCIINinguno

Si no se detecta soporte de imágenes, el banner usa el modo solo texto.


Uso Rápido

1. Añadir Dependencia

# mix.exs
def deps do
  [{:batamanta, "~> 1.0", runtime: false}]
end

2. Configurar

def project do
  [
    app: :mi_app,
    version: "0.1.0",
    batamanta: [
      format: :escript,       # :escript | :release
      erts_target: :auto,        # Auto-detectar plataforma (RECOMENDADO)
      execution_mode: :cli,      # :cli | :tui | :daemon
      compression: 3,           # 1-19 (nivel zstd)
      binary_name: "mi_app",     # Opcional: nombre personalizado
      show_banner: true          # Opcional: mostrar banner de construcción
    ]
  ]
end

Opciones de Configuración

OpciónTipoDefaultDescripción
erts_targetatom:autoPlataforma objetivo (ver abajo)
otp_versionstring:autoVersión OTP (ej: "28.1")
formatatom:release¹:escript o :release
execution_modeatom:cli:cli, :tui, o :daemon
compressioninteger3Nivel de compresión zstd (1-19)
binary_namestringnombre de appNombre personalizado del binario
show_bannerbooleantrueMostrar banner de construcción
force_osstringnilForzar SO: "linux", "macos", "windows"
force_archstringnilForzar arquitectura: "x86_64", "aarch64"
force_libcstringnilForzar libc: "gnu", "musl" (solo Linux)

¹ Auto-detectado como :escript cuando el proyecto define escript: [main_module: ...] en mix.exs.

Nota para Linux: El objetivo (glibc vs musl) se detecta automáticamente según tu distribución:

  • Debian, Ubuntu, Arch, Fedora, CachyOS → usa linux-gnu
  • Alpine Linux → usa linux-musl

3. Construir

mix batamanta

Formatos de Salida

Batamanta soporta dos formatos de salida:

:release (Default)

Genera un release completo de OTP con supervisor tree. Ideal para:

  • Servicios y aplicaciones de larga duración
  • Aplicaciones que necesitan supervisión OTP completa
  • Distribuciones Erlang
batamanta: [
  format: :release
]

:escript

Genera un escript ligero con runtime de Elixir embebido. Ideal para:

  • Herramientas CLI
  • Proyectos que ya usan mix escript.build
  • Binarios autocontenidos: Embebe un ERTS ligero y no requiere Erlang en el host
  • Binarios pequeños (~60-70% más pequeños)
batamanta: [
  format: :escript
]

Detección automática: Si tu proyecto tiene configuración :escript en mix.exs, batamanta usará automáticamente el formato :escript.

Comparación

Aspecto:release:escript
Tamaño~80-150 MB~15-30 MB
StartupLentoRápido
Supervisor Tree✅ Completo❌ No disponible
Daemon Mode
Hot Upgrades
Elixir embebidoNo

CLI Override

# Forzar formato escript
mix batamanta --format escript

# Forzar formato release
mix batamanta --format release

Esto genera: mi_app-0.1.0-x86_64-linux


Opciones CLI

Sobrescribir configuración desde línea de comandos:

# Usar auto-detección (default)
mix batamanta

# Forzar objetivo ERTS
mix batamanta --erts-target alpine_3_19_x86_64

# Forzar componentes individuales
mix batamanta --force-os linux --force-arch aarch64 --force-libc musl

# Ajustar nivel de compresión
mix batamanta --compression 9

# Combinar opciones
mix batamanta --erts-target ubuntu_22_04_arm64 --compression 5

Flags CLI Disponibles

FlagDescripción
--formatFormato de salida: release o escript
--erts-targetSobrescribir objetivo ERTS
--otp-versionVersión OTP exacta (ej: "28.1")
--force-osForzar SO: linux, macos, windows
--force-archForzar arquitectura: x86_64, aarch64
--force-libcForzar libc: gnu, musl (solo Linux)
--compressionNivel de compresión zstd (1-19)

Control de Versión OTP

Tú especificas, tú respondes. Si especificas otp_version, se usa esa versión exacta. Si no se especifica, se usa un fallback conservador.

Configuración

# Usar versión OTP exacta (recomendado para producción)
batamanta: [
  otp_version: "28.1"
]

Comportamiento

ModoDescripciónCuándo Usarlo
ExplícitoUsa la versión exacta. Falla si no está disponible en el repositorio.Producción, reproducibilidad
AutoUsa fallback conservador (28.0 → 28.1 → ...). Usa ERTS del sistema si no encuentra nada.Desarrollo, builds rápidos

CLI

# Especificar versión OTP exacta
mix batamanta --otp-version 28.1

# Modo auto (por defecto)
mix batamanta

Resolución de Versión

En modo auto, si la versión exacta no está disponible:

  1. Prueba OTP-28.0 primero (más común)
  2. Luego OTP-28.1, OTP-28.2, etc.
  3. Hace fallback al ERTS del sistema si no encuentra nada

Modos de Ejecución

ModoDescripciónModo de TerminalPlataforma
:cliCLI estándar con stdin/stdout/stderr heredadosCooked (modo canónico, entrada por líneas)Todos
:tuiUI de texto con modo terminal raw, navegación con teclas de flechasRaw (entrada directa, sin buffering de línea)Unix only
:daemonEjecuta en segundo plano, sin E/S de terminalN/A (desacoplado)Unix only

Nota: En modo :cli (cooked), la terminal procesa la entrada línea por línea - Enter envía la línea, backspace funciona normalmente, y teclas especiales como flechas no se capturan directamente. En modo :tui (raw), la app tiene control directo de la terminal y puede capturar pulsaciones individuales incluyendo teclas de flecha, teclas de función, etc.


Compatibilidad de Plataformas

Sistemas Operativos

SOArquitecturasModosEstado
macOS 11+x86_64, aarch64CLI, TUI, Daemon✅ Soporte Completo
Linux (glibc)x86_64, aarch64CLI, TUI, Daemon✅ Soporte Completo
Linux (musl)x86_64, aarch64CLI, Daemon✅ Soportado
Windows 10+x86_64CLI✅ Soportado

Restricciones

  • ❌ Windows + modo TUI (requiere terminal Unix)
  • ❌ Windows + modo Daemon (requiere gestión de procesos Unix)
  • ❌ OTP < 25 (funciones BEAM requeridas no disponibles)
  • ❌ Elixir < 1.15 (funciones del lenguaje requeridas no disponibles)

Para Aplicaciones CLI

Al no haber wrapper de shell, usa :init de Erlang para leer argumentos:

defmodule MiApp do
  use Application

  @impl true
  def start(_type, _args) do
    args = :init.get_plain_arguments()
           |> Enum.map(&to_string/1)
           |> Enum.reject(&(&1 == "--"))
    
    case args do
      ["hola", nombre] -> IO.puts("Hola, #{nombre}!")
      _ -> IO.puts("Uso: mi_app hola <nombre>")
    end
    
    System.halt(0)
  end
end

¡No olvides System.halt/1 cuando tu CLI termine!


Objetivos ERTS Soportados

Batamanta usa un sistema unificado de objetivo ERTS para especificar la plataforma.

Targets Soportados

Target AtomSOArqLibcCaso de Uso
:auto---Auto-detectar host (default)
:ubuntu_22_04_x86_64Linuxx86_64glibcDebian, Ubuntu, Arch, CachyOS
:ubuntu_22_04_arm64Linuxaarch64glibcServidores ARM, Raspberry Pi 4
:alpine_3_19_x86_64Linuxx86_64muslAlpine Linux, contenedores
:alpine_3_19_arm64Linuxaarch64muslAlpine en ARM
:macos_12_x86_64macOSx86_64-Mac Intel
:macos_12_arm64macOSaarch64-Apple Silicon (M1/M2/M3)
:windows_x86_64Windowsx86_64msvc✅ Soportado

Override Manual

Fuerza un objetivo específico independientemente del host:

batamanta: [
  erts_target: :alpine_3_19_x86_64,  # Forzar musl
  execution_mode: :cli
]

O usa overrides individuales:

batamanta: [
  force_os: "linux",
  force_arch: "x86_64",
  force_libc: "musl"
]

Compilación cruzada desde macOS: Instala los targets de Rust:

rustup target add x86_64-unknown-linux-gnu
rustup target add aarch64-unknown-linux-gnu
rustup target add x86_64-unknown-linux-musl
rustup target add aarch64-unknown-linux-musl

Solución de Problemas: Linux musl/glibc

Problema: Advertencia "libc mismatch detected"

Si ves una advertencia como:

  libc mismatch detected!
  Expected: glibc (Debian/Ubuntu/Arch/Fedora)
  Detected: musl libc (Alpine)

Esto significa que el tipo libc de tu sistema no coincide con el objetivo ERTS esperado.

Solución 1: Dejar que Batamanta auto-detecte (recomendado)

batamanta: [
  erts_target: :auto  # Auto-detecta musl vs glibc
]

Solución 2: Forzar objetivo específico

batamanta: [
  erts_target: :alpine_3_19_x86_64  # Forzar musl
]

Solución 3: Usar override CLI

mix batamanta --erts-target alpine_3_19_x86_64

Problema: La descarga de ERTS falla en Alpine/musl

Si la descarga de ERTS falla con error 404 en sistemas musl:

Solución 1: Usar auto-detección (recomendado)

batamanta: [
  erts_target: :auto  # Auto-detecta musl vs glibc
]

Solución 2: Usar una versión OTP específica

batamanta: [
  otp_version: "28.0"  # Probar una versión anterior con builds musl
]

Problema: El binario no funciona en el sistema destino

Verifica compatibilidad libc:

# En máquina de construcción
ldd --version

# En máquina destino  
ldd --version

# Ambos deben coincidir (glibc o musl)

Solución: Construir para glibc más antiguo

# Usar objetivo Ubuntu 22.04 (glibc más compatible)
batamanta: [
  erts_target: :ubuntu_22_04_x86_64
]

Cómo funciona la Detección de libc

Batamanta usa múltiples métodos en orden:

  1. ldd --version - Más confiable, busca "musl" o "glibc" en la salida
  2. Archivos de dynamic loader - Busca /lib/ld-musl-*.so vs /lib64/ld-linux-*.so
  3. /etc/os-release - Busca ID=alpine, ID=void, etc.
  4. /proc/self/maps - Avanzado, verifica librerías cargadas

La detección siempre hace fallback a glibc si hay incertidumbre (90%+ de sistemas usan glibc).

Fallback de Descarga de ERTS

Batamanta intenta descargar ERTS pre-compilados de Hex.pm builds. Si la descarga falla:

  Could not download ERTS, using system ERTS instead.

El build continúa usando el ERTS del sistema. Esto significa:

  • Build exitoso - Tu aplicación compila
  • ⚠️ Binario requiere ERTS - La máquina destino necesita Erlang/Elixir compatible
  • Portable dentro del mismo SO - Funciona en máquinas con el mismo tipo libc

Para binarios autocontenidos de producción:

  1. Asegura acceso a red durante el build
  2. Usa versión ERTS específica: batamanta: [otp_version: "26.2.5"]
  3. Asegura que la plataforma objetivo tenga ERTS pre-compilados disponibles

Arquitectura

  1. Release: mix release compila tu código
  2. Fetch: Descarga el ERTS correspondiente a la plataforma objetivo
  3. Package: Crea el tarball comprimido (release + ERTS)
  4. Compile: El dispenser de Rust embeber el payload
  5. Run: El dispenser extrae el payload y lanza la VM de Erlang

Aislamiento del Entorno de Construcción

La versión 1.4.0+ de Batamanta incluye Batamanta.EnvCleaner, diseñado para asegurar un aislamiento total del proceso de construcción del binario frente al host.

¿Por qué es importante?

Al usar gestores como asdf, mise o kerl, el PATH apunta a versiones gestionadas mediante shims. Si estas versiones no coinciden exactamente con el ERTS que se está embebiendo, pueden surgir:

  • Fallos de "Corrupt atom table" en ejecución.
  • Errores de compatibilidad entre el bytecode compilado y el runtime.
  • Fallos silenciosos en entornos CI.

¿Cómo funciona?

Cuando ejecutas mix batamanta, la herramienta:

  1. Detecta y filtra cualquier ruta de gestor de versiones del PATH.
  2. Sanitiza el entorno limitándolo solo a variables esenciales del sistema.
  3. (En modo escript) Antepone el binario del ERTS descargado al PATH, garantizando paridad total entre tiempo de compilación y ejecución.

Esto garantiza que el binario generado sea 100% consistente con el runtime que transporta.


Repositorio de ERTS

Batamanta usa un repositorio separado para binarios ERTS precompilados:

Batamanta ERTS Repository

Este repositorio aloja binarios ERTS precompilados para:

  • macOS: aarch64 (Apple Silicon)
  • Linux (glibc): x86_64 & aarch64
  • Linux (musl): x86_64 & aarch64

Los binarios están compilados desde las fuentes oficiales de Erlang/OTP y están sujetos a la Licencia Apache 2.0 (consulta el repositorio para más detalles).


Testing

Ejecuta la matriz de tests localmente:

# Probar en diferentes distribuciones Linux (requiere Docker)
./docker_matrix.sh

# Ejecutar smoke tests manualmente
cd smoke_tests/test_cli && mix batamanta && ./test_cli-* arg1 arg2
cd smoke_tests/test_tui && mix batamanta && ./test_tui-*
cd smoke_tests/test_daemon && mix batamanta && ./test_daemon-* &

Licencia

MIT