Ejecución síncrona de comandos con soporte para gestión de versiones.
Proporciona una interfaz limpia para ejecutar comandos shell con validación, selección de shell, detección automática de archivos de configuración, integración con asdf/mise y parseo estructurado de resultados.
Para ejecución en paralelo, usar Arrea.Parallel o Arrea.Leader.
Resolución de shell
El shell se resuelve con la siguiente prioridad (de menor a mayor):
Arrea.Config.get(:shell)— config del proyecto consumidor o runtime- Variable de entorno
$SHELL - Login shell del usuario actual (
/etc/passwd) - Opción
:shellpasada aexecute/2— máxima prioridad "sh"como último recurso si ninguno de los anteriores es válido
Se aceptan tanto nombres ("zsh") como rutas ("/bin/zsh").
Los nombres se resuelven a rutas mediante System.find_executable/1.
Si el shell configurado no existe, cae al default del sistema ($SHELL o "sh").
Según el shell detectado, se fuerza la carga de su archivo de configuración
(ej: ~/.bashrc para bash, ~/.zshrc para zsh, ~/.config/fish/config.fish para fish).
Timeout real
El timeout se aplica durante la ejecución: si el comando no termina
en timeout ms, el proceso de ejecución es cancelado. El proceso OS subyacente
recibe SIGKILL cuando el puerto de Erlang es cerrado al morir el proceso propietario.
Gestión de versiones asdf/mise
Se pueden forzar versiones de runtimes mediante dos mecanismos:
asdf — opción
asdf_<lenguaje>: generaexport ASDF_<LANG>_VERSION=<version>antes del comando. Funciona tanto con asdf como con mise.mise — opción
mise_<lenguaje>: envuelve el comando conmise exec <lenguaje>@<version> -- <comando>.
Ejemplos
iex> Arrea.Command.execute("echo hello")
{:ok, %{stdout: "hello\n", exit_code: 0, duration_ms: 3}}
iex> Arrea.Command.execute("mix test", asdf_elixir: "1.18.0")
{:ok, %{stdout: "...", exit_code: 0, duration_ms: 1200}}
iex> Arrea.Command.execute("node -v", mise_node: "20.0.0")
{:ok, %{stdout: "v20.0.0\n", exit_code: 0, duration_ms: 80}}
iex> Arrea.Command.execute("sleep 60", timeout: 500)
{:error, :timeout}
Summary
Functions
Extrae los argumentos mise_<lang> de las opciones y los formatea
como "lang@version" para el comando mise exec.
Ejecuta una cadena de comando de forma síncrona con configuración opcional.
Ejecuta un comando con una versión de lenguaje gestionada por ASDF.
Parsea un mapa de resultado crudo a una forma estructurada.
Resuelve el shell a usar según la prioridad (de menor a mayor)
Resuelve la ruta al archivo de configuración del shell.
Resuelve el nombre de un shell a su ruta absoluta.
Types
@type result() :: %{ stdout: String.t(), exit_code: non_neg_integer(), duration_ms: non_neg_integer() }
Functions
Extrae los argumentos mise_<lang> de las opciones y los formatea
como "lang@version" para el comando mise exec.
Ejecuta una cadena de comando de forma síncrona con configuración opcional.
El comando se valida antes de la ejecución. Comandos inválidos o peligrosos
retornan {:error, razon} sin ejecutar nada.
El timeout es real: si el comando no termina dentro del límite, el proceso de ejecución es cancelado activamente (no post-hoc).
Opciones
:timeout— Tiempo máximo de ejecución en ms (default:30_000):cd— Directorio de trabajo (default: directorio actual):shell— Shell a usar — tiene prioridad máxima sobre config y env:shell_config— Ruta al archivo de configuración del shell a cargar (opcional):env— Variables de entorno adicionales como mapa (opcional):quiet— Si es true, suprime la captura de stderr (default: false):asdf_elixir— Forzar versión de Elixir via asdf/miseasdf_<lang>— Forzar versión de cualquier lenguaje via asdf/misemise_<lang>— Forzar versión viamise exec
Retorna
{:ok, result}— Mapa con:stdout,:exit_code,:duration_ms{:error, :timeout}— El comando fue cancelado por exceder el timeout{:error, reason}— Error de validación o ejecución
@spec execute_with_asdf(String.t(), atom(), String.t(), keyword()) :: {:ok, result()} | {:error, term()}
Ejecuta un comando con una versión de lenguaje gestionada por ASDF.
Envoltorio conveniente para execute/2 que antepone la activación del shim de asdf.
Ejemplos
iex> Command.execute_with_asdf("mix test", :elixir, "1.18.0")
{:ok, %{stdout: "...", exit_code: 0, duration_ms: 1200}}
@spec parse_result(result()) :: {:ok, result()} | {:error, {:exit_code, non_neg_integer()}}
Parsea un mapa de resultado crudo a una forma estructurada.
Detecta patrones comunes de error y retorna resultados etiquetados.
Resuelve el shell a usar según la prioridad (de menor a mayor):
Arrea.Config.get(:shell)(config del proyecto oConfig.set/2)- Variable de entorno
$SHELL - Login shell del usuario en
/etc/passwd - Opción
:shellpasada en opts — máxima prioridad "sh"como fallback si ninguno es válido
Si el shell resuelto es un nombre (ej: "zsh"), lo busca en PATH.
Si no se encuentra, cae al default del sistema.
Resuelve la ruta al archivo de configuración del shell.
Retorna la ruta expandida al archivo de config (ej: ~/.zshrc para zsh)
o nil si el shell no tiene un archivo de config conocido.
Resuelve el nombre de un shell a su ruta absoluta.
Si ya es una ruta (contiene /), se devuelve tal cual si existe.
Si es solo un nombre, se busca en PATH via System.find_executable/1.
Retorna nil si no se encuentra el ejecutable.