just es un ejecutor de comandos escrito en Rust que guarda los comandos habituales de un proyecto en un archivo justfile, con una sintaxis parecida a la de Make pero pensada solo para ejecutar tareas. No compara fechas de archivos, admite argumentos con valores por defecto y muestra errores claros cuando algo falla. En este tutorial instalarás just en Ubuntu 24.04 y crearás paso a paso un justfile para un proyecto de ejemplo.

Los ejemplos usan solo herramientas del sistema (cp, tar, rsync) para que funcionen sin instalar ningún lenguaje. En un proyecto real sustituirás esos comandos por los de tu toolchain.

Requisitos previos

  • Un servidor o equipo con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con un usuario no root con privilegios sudo.
  • git y rsync instalados.
  • Conocimientos básicos de la terminal.

Paso 1: Instalar just

Ubuntu 24.04 incluye just en el repositorio universe, así que la forma más sencilla es instalarlo con apt:

sudo apt update
sudo apt install -y just

Comprueba la versión:

just --version
just 1.21.0

La versión de Ubuntu 24.04 cubre todo lo que se usa en esta guía. Si necesitas una más reciente, el proyecto ofrece un script que descarga el binario precompilado. Descárgalo, revísalo y ejecútalo indicando un directorio de tu PATH:

curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh -o install-just.sh
less install-just.sh
mkdir -p ~/.local/bin
bash install-just.sh --to ~/.local/bin

Para activar el autocompletado de bash solo para tu usuario:

mkdir -p ~/.local/share/bash-completion/completions
just --completions bash > ~/.local/share/bash-completion/completions/just

Abre una terminal nueva para que se cargue.

Paso 2: Crear el proyecto y el primer justfile

Crea un proyecto de ejemplo con un par de archivos fuente:

mkdir -p ~/demo-just/src && cd ~/demo-just
git init -q
echo '<h1>Hola desde just</h1>' > src/index.html
git add . && git commit -qm "Primer commit"

Crea el archivo justfile en la raíz del proyecto:

nano justfile
# Mostrar las recetas disponibles
default:
    @just --list

# Copiar los fuentes a dist
build:
    mkdir -p dist
    cp -r src/. dist/

# Borrar los archivos generados
clean:
    rm -rf dist *.tar.gz

Cada receta tiene un nombre seguido de : y sus comandos sangrados debajo. La sangría puede ser de espacios o de tabuladores, pero debe ser la misma en toda la receta. El comentario justo encima de una receta se convierte en su descripción.

Lista las recetas y ejecuta build:

just --list
just build
Available recipes:
    build   # Copiar los fuentes a dist
    clean   # Borrar los archivos generados
    default # Mostrar las recetas disponibles
mkdir -p dist
cp -r src/. dist/

just imprime cada línea antes de ejecutarla. Si una línea empieza por @, como en default, se ejecuta sin mostrarse. Si un comando falla, just se detiene y devuelve su código de error. Ejecutar just sin argumentos lanza la primera receta del archivo, en este caso default.

Paso 3: Añadir dependencias entre recetas

Las recetas que aparecen después de los dos puntos se ejecutan antes, y cada una solo una vez por invocación. Añade una receta que empaquete el resultado de build:

# Crear un tar.gz con el contenido de dist
package: build
    tar -czf demo.tar.gz -C dist .
    ls -lh demo.tar.gz

Ejecútala:

just package
mkdir -p dist
cp -r src/. dist/
tar -czf demo.tar.gz -C dist .
ls -lh demo.tar.gz
-rw-rw-r-- 1 usuario usuario 180 sep 25 10:00 demo.tar.gz

A diferencia de Make, just no comprueba si dist ya existe: la receta build se ejecuta siempre. Es el comportamiento esperado de un ejecutor de comandos. Para ver qué haría una receta sin ejecutarla, usa just --dry-run package.

Puedes lanzar varias recetas en una sola orden, que se ejecutan en el orden indicado:

just clean package

Paso 4: Usar variables y expresiones

Las variables se declaran con := al principio del archivo y se insertan en los comandos con {{nombre}}. Las expresiones entre comillas invertidas ejecutan un comando y guardan su salida. Añade estas líneas al principio del justfile:

app := "demo-web"
commit := `git rev-parse --short HEAD`
destino := env_var_or_default("DEPLOY_PATH", "/tmp/demo-deploy")
  • commit toma el hash del último commit al cargar el justfile.
  • env_var_or_default lee una variable de entorno y usa el valor por defecto si no existe.

Cambia la receta package para usar las variables:

# Crear un tar.gz con el nombre de la app y el commit
package: build
    tar -czf {{app}}-{{commit}}.tar.gz -C dist .
    ls -lh {{app}}-{{commit}}.tar.gz

Consulta el valor que just asigna a cada variable:

just --evaluate
app     := "demo-web"
commit  := "a1b2c3d"
destino := "/tmp/demo-deploy"

Puedes sobrescribir cualquier variable desde la línea de comandos poniendo nombre=valor antes de la receta:

just app=portal package

Paso 5: Recibir argumentos en las recetas

Las recetas aceptan parámetros después de su nombre. Un parámetro con = tiene valor por defecto; uno con + recoge uno o más argumentos y uno con *, cero o más. Añade una receta de despliegue:

# Publicar dist en el entorno indicado (staging por defecto)
deploy entorno="staging": build
    mkdir -p "{{destino}}/{{entorno}}"
    rsync -a --delete dist/ "{{destino}}/{{entorno}}/"
    @echo "Desplegado {{app}} ({{commit}}) en {{destino}}/{{entorno}}"

# Buscar texto en los fuentes, por ejemplo: just buscar -i hola
buscar +args:
    grep -rn {{args}} src/

Los argumentos se pasan por posición:

just deploy
just deploy produccion
just buscar -i hola
mkdir -p "/tmp/demo-deploy/produccion"
rsync -a --delete dist/ "/tmp/demo-deploy/produccion/"
Desplegado demo-web (a1b2c3d) en /tmp/demo-deploy/produccion

Comprueba que el despliegue ha dejado los archivos en su sitio:

ls /tmp/demo-deploy/produccion
index.html

Paso 6: Configurar el shell y escribir recetas de varias líneas

Por defecto, just ejecuta cada línea con sh -cu. Puedes cambiarlo para todo el archivo con set shell, y cargar un archivo .env con set dotenv-load. Añade al principio del justfile:

set shell := ["bash", "-uc"]
set dotenv-load

Crea el .env con un valor que ahora tendrá prioridad sobre el valor por defecto de destino:

echo 'DEPLOY_PATH=/tmp/demo-deploy-env' > .env

Cuando necesites condicionales, bucles o variables de shell que duren varias líneas, empieza la receta con un shebang. just guarda la receta en un archivo temporal y la ejecuta completa con ese intérprete:

# Comprobar que el árbol de git está limpio antes de publicar
check-git:
    #!/usr/bin/env bash
    set -euo pipefail
    if ! git diff --quiet; then
        echo "Hay cambios sin confirmar. Haz commit antes de desplegar." >&2
        exit 1
    fi
    echo "Árbol limpio en la rama $(git branch --show-current)"

# Contar los archivos de dist con Python
contar:
    #!/usr/bin/env python3
    from pathlib import Path
    archivos = [p for p in Path("dist").rglob("*") if p.is_file()]
    print(f"{len(archivos)} archivos en dist")

Haz que deploy dependa de la comprobación cambiando su línea de cabecera a deploy entorno="staging": check-git build, y ejecuta:

just contar
just deploy
1 archivos en dist
Árbol limpio en la rama main
mkdir -p "/tmp/demo-deploy-env/staging"
...

Si modificas un archivo versionado sin hacer commit, just deploy se detendrá en check-git.

Paso 7: Pedir confirmación y ocultar recetas internas

Para recetas peligrosas, el atributo [confirm] pide una respuesta antes de ejecutarlas. Las versiones más recientes de just admiten además un mensaje propio, como [confirm("¿Seguro?")]. Las recetas cuyo nombre empieza por _ no aparecen en just --list, lo que sirve para pasos auxiliares:

# Borrar el despliegue del entorno indicado
[confirm]
borrar-despliegue entorno: _aviso
    rm -rf "{{destino}}/{{entorno}}"

_aviso:
    @echo "Operando sobre {{destino}}"

Pruébalo:

just borrar-despliegue produccion
Run recipe `borrar-despliegue`? y
Operando sobre /tmp/demo-deploy-env
rm -rf "/tmp/demo-deploy-env/produccion"

En scripts o CI, donde no hay nadie para responder, añade --yes para confirmar automáticamente.

Paso 8: Dividir el justfile con import

Cuando el archivo crece, puedes mover recetas a otros archivos e importarlos. Crea uno para tareas de servidor:

mkdir -p just
nano just/servidor.just
# Mostrar el espacio libre en disco
espacio:
    df -h /

# Listar los servicios de systemd que han fallado
fallidos:
    systemctl --failed --no-pager

Impórtalo al principio del justfile principal:

import "just/servidor.just"

Las recetas importadas se usan como si estuvieran en el archivo principal:

just espacio

Si el archivo importado puede no existir (por ejemplo, un local.just que cada desarrollador crea a su gusto), usa import? "just/local.just".

just frente a Make

AspectoMakejust
PropósitoConstruir archivos según dependenciasEjecutar comandos del proyecto
Recetas repetidasSe omiten si el objetivo está al díaSe ejecutan siempre
ArgumentosSolo variables VAR=valorParámetros con valores por defecto y variádicos
SangríaTabulador obligatorioEspacios o tabuladores, de forma coherente
Recetas en otros lenguajesNoCon shebang
DisponibilidadPreinstalado casi siemprePaquete just o binario

Si necesitas recompilar solo lo que ha cambiado, Make (o Task, con sus sources) sigue siendo la herramienta adecuada. Para documentar y ejecutar los comandos de un proyecto, just es más sencillo.

Solución de problemas

error: No justfile found. just busca justfile o Justfile en el directorio actual y en los superiores. Revisa el nombre o indica la ruta con just --justfile ruta/justfile.

error: Recipe line has inconsistent leading whitespace. Una receta mezcla tabuladores y espacios. Localiza la línea con cat -A justfile, donde los tabuladores aparecen como ^I, y usa el mismo tipo de sangría en toda la receta.

Un cd no tiene efecto en la línea siguiente. Es el comportamiento normal: cada línea es un shell independiente. Encadena los comandos en una línea (cd dir && make) o usa una receta con shebang.

Una variable del .env no se lee. Comprueba que el justfile tiene set dotenv-load y que el .env está en el mismo directorio que el justfile. Verifica los valores con just --evaluate.

Conclusión

Tienes just instalado y un justfile con recetas encadenadas, variables, argumentos, carga de .env, recetas en bash y Python, confirmación para las operaciones peligrosas y recetas repartidas en varios archivos. Como siguientes pasos, pasa a just los comandos que hoy tienes dispersos en el README, usa just --list como ayuda del proyecto y llama a las mismas recetas desde tu pipeline de CI.