just — command runner
just — кроссплатформенный command runner на Rust. Заменяет make в роли «склада проектных команд», без багажа build-системы.
- Репозиторий: casey/just
- Сайт: just.systems
- Манул: just.systems/man/en
- Лицензия: CC0-1.0
- Версия (на момент написания): 1.43.x
Зачем
make спроектирован как build-система с зависимостями файлов; команды — побочный эффект. just решает только одну задачу: сохранять и запускать проектные команды.
| Свойство | make | just |
|---|---|---|
| Назначение | Сборка артефактов | Запуск команд |
.PHONY | Нужен | Не нужен |
| Кроссплатформенность | Зависит от GNU make | Linux / macOS / Windows / BSD из коробки |
| Ошибки | Скудные | С контекстом строки |
| Поиск justfile | Только CWD | Поднимается вверх по дереву |
| Шебанги | Ограниченно | Любой язык (python, node, ruby, perl, …) |
.env | Нет | Встроено |
Установка
# macOS / Linux (Homebrew)
brew install just
# Cargo
cargo install just
# Debian 13+ / Ubuntu 24.04+
apt install just
# Conda
conda install -c conda-forge just
# Универсальный установщик (bash)
curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash
Windows
# Winget (рекомендуется, идёт из коробки в Windows 11)
winget install --id Casey.Just --exact
# Scoop
scoop install just
# Chocolatey
choco install just
# Cargo (если установлен Rust toolchain)
cargo install just
# WSL — используй Linux-инструкции внутри WSL
PowerShell-установщик (аналог install.sh):
# в текущий каталог
irm https://just.systems/install.ps1 | iex
# в конкретный каталог (PATH)
$env:JUST_INSTALL_DIR = "$env:USERPROFILE\bin"
irm https://just.systems/install.ps1 | iex
На Windows just по умолчанию вызывает sh — если его нет, переопредели shell в justfile:
set windows-shell := ["pwsh", "-NoLogo", "-Command"]
# или cmd:
set windows-shell := ["cmd.exe", "/c"]
Другие способы: asdf, Nix, Snap, npm, MacPorts, готовые бинарники с GitHub Releases, Docker-образ ghcr.io/casey/just.
Быстрый старт
Файл justfile в корне проекта:
default:
@just --list
build:
cargo build --release
test: build
cargo test
lint:
cargo clippy -- -D warnings
just # запустит первый рецепт (default)
just build # запустит конкретный рецепт
just --list # покажет все рецепты
just --show test # покажет тело рецепта
Синтаксис
Рецепт
recipe-name:
echo 'тело рецепта'
echo 'каждая строка — отдельный shell-вызов'
Префикс @ перед командой отключает её эхо (как в make):
quiet:
@echo 'выведется без префиксного echo команды'
Параметры
# обязательный параметр
build target:
cargo build --target {{target}}
# значение по умолчанию
test target tests='all':
./test --tests {{tests}} {{target}}
# variadic — один или больше (+), ноль или больше (*)
backup +FILES:
scp {{FILES}} me@server.com:
deploy *ENVS:
./deploy.sh {{ENVS}}
# variadic с дефолтом
test +FLAGS='-q':
cargo test {{FLAGS}}
Экспорт параметров в env
Префикс $ экспортирует параметр как переменную окружения:
test $RUST_BACKTRACE='1':
cargo test
Зависимости
build:
cc main.c -o main
test: build
./test
# зависимость с аргументами
deploy: (build 'release')
release: build test
./publish
after-dependencies (запускаются после тела) — через &&:
build: lint && test
cargo build
Переменные
version := '1.0.0'
target := 'x86_64-unknown-linux-gnu'
# экспорт в env рецепта
export RUST_LOG := 'debug'
build:
cargo build --release --target {{target}}
echo 'v{{version}}'
Условные выражения
bar foo:
echo {{ if foo == "bar" { "hello" } else { "goodbye" } }}
mode := if env_var_or_default('CI', '') == '1' { 'release' } else { 'debug' }
Shell-подстановка через бэктики
hash := `git rev-parse --short HEAD`
date := `date +%Y-%m-%d`
build:
echo 'building {{hash}} on {{date}}'
Шебанг-рецепты (произвольный язык)
python:
#!/usr/bin/env python3
import sys
print(f"python {sys.version_info.major}.{sys.version_info.minor}")
node:
#!/usr/bin/env node
console.log(`node ${process.version}`);
Settings
Объявляются через set <name> := <value> в начале файла.
| Setting | Назначение |
|---|---|
set shell := ["bash", "-cu"] | Shell для всех строк-рецептов и бэктиков |
set windows-shell := ["pwsh", "-c"] | Shell на Windows |
set dotenv-load | Загружать .env из корня |
set dotenv-filename := ".env.local" | Имя dotenv-файла |
set dotenv-path := "config/.env" | Явный путь |
set dotenv-required | Падать если .env нет |
set dotenv-override | dotenv перекрывает окружение |
set export | Все переменные/параметры — в env |
set positional-arguments | $1, $2, … вместо {{ ... }} |
set allow-duplicate-recipes | Перезапись одноимённых рецептов |
set fallback | Искать рецепт в родительском justfile |
set ignore-comments | Не передавать комментарии в shell |
set unstable | Включить нестабильные фичи |
set working-directory := 'apps' | Сменить CWD для всех рецептов |
Пример:
set shell := ["bash", "-euo", "pipefail", "-c"]
set dotenv-load
set export
DATABASE_URL := "postgres://localhost/app"
migrate:
diesel migration run
Атрибуты
Ставятся над рецептом в [brackets]. Можно стекать.
| Атрибут | Эффект |
|---|---|
[private] | Скрыть из --list (альтернатива — префикс _) |
[no-cd] | Не менять CWD на каталог justfile |
[no-exit-message] | Не печатать error: Recipe X failed |
[confirm] / [confirm('Точно?')] | Запросить подтверждение перед запуском |
[doc('описание')] | Документация в --list |
[group('build')] | Группировка в --list |
[linux] / [macos] / [windows] / [unix] | Только на этой ОС |
[script] / [script('python3')] | Рецепт целиком — один скрипт |
[working-directory('apps/backend')] | CWD для одного рецепта |
[positional-arguments] | Локальный аналог setting |
[no-quiet] | Отключить @-quiet |
Пример:
[group('deploy')]
[confirm('Деплоим в prod?')]
[doc('Деплой в production')]
deploy-prod: build test
./scripts/deploy.sh prod
[linux]
[macos]
open-browser url:
xdg-open {{url}} 2>/dev/null || open {{url}}
[private]
_internal-helper:
echo 'не виден в --list'
Встроенные функции
Полезный минимум — полный список в manual: functions.
Пути и окружение
justfile()— путь к текущему justfilejustfile_directory()— каталог justfileinvocation_directory()— откуда вызвалиjustsource_file()/source_directory()— файл/каталог текущего исходника (с учётом импортов)env_var('NAME')— обязательная env-переменнаяenv_var_or_default('NAME', 'fallback')env('NAME')/env('NAME', 'fallback')— короткие алиасыhome_directory()cache_directory(),config_directory(),data_directory()
Платформа
os()—"linux","macos","windows", …os_family()—"unix"/"windows"arch()—"x86_64","aarch64", …num_cpus()
Строки
uppercase(s)/lowercase(s)/capitalize(s)trim(s)/trim_start(s)/trim_end(s)replace(s, from, to)/replace_regex(s, re, to)split(s, sep)/join(sep, a, b, …)quote(s)— экранирование для shell
Пути
absolute_path(p),canonicalize(p)parent_directory(p),file_name(p),file_stem(p),extension(p)join(a, b),path_exists(p)
Хэши и UUID
sha256(s),sha256_file(p),blake3(s),blake3_file(p)uuid()
Командная строка
just # запустить рецепт по умолчанию
just <recipe> [args...] # запустить рецепт
just --list # все рецепты
just --list --unsorted # в порядке объявления
just --show <recipe> # тело рецепта
just --summary # одной строкой
just --choose # интерактивный выбор (fzf-like)
just --evaluate # значения всех переменных
just --evaluate VAR # значение одной переменной
just --dump # AST/нормализованный justfile
just --fmt # форматирование (in-place)
just --init # сгенерировать justfile
just --variables # имена переменных
just --groups # имена групп
just --doc <recipe> # docstring рецепта
just -f path/to/justfile # явный путь
just -d apps/backend # рабочий каталог
just --shell bash # переопределить shell
just --dry-run <recipe> # показать что будет запущено
Модули и импорты
# импорт — содержимое подмешивается в текущий файл
import 'common.just'
import? 'optional.just' # не падать если нет
# модуль — отдельное пространство имён
mod frontend 'apps/frontend/justfile'
mod backend 'apps/backend/justfile'
Запуск рецепта модуля: just frontend::build.
Пример под 4Partners (моно-репо)
set shell := ["bash", "-euo", "pipefail", "-c"]
set dotenv-load
frontend := 'apps/frontend'
backend := 'apps/backend'
[group('dev')]
[doc('Запустить frontend dev-сервер')]
fe-dev:
cd {{frontend}} && pnpm dev
[group('dev')]
[doc('Запустить backend')]
be-dev:
cd {{backend}} && python -m app
[group('quality')]
[doc('Линтинг всего проекта')]
lint:
cd {{frontend}} && pnpm lint
cd {{backend}} && ruff check .
[group('quality')]
typecheck:
cd {{frontend}} && pnpm typecheck
cd {{backend}} && mypy .
[group('quality')]
[doc('Полная проверка: lint + typecheck')]
check: lint typecheck
[group('db')]
[confirm('Сбросить локальную БД?')]
db-reset:
dropdb --if-exists app_dev
createdb app_dev
just db-migrate
[group('db')]
db-migrate:
cd {{backend}} && alembic upgrade head
[private]
_ensure-pnpm:
command -v pnpm >/dev/null || npm i -g pnpm
Когда использовать just vs альтернативы
just— проектные команды (lint, test, build, deploy, db-migrate, codegen). Не build-система.make— если реально нужно отслеживать file timestamps и инкрементальная сборка.npm/pnpmscripts — если проект чисто Node и команд мало (< 10).task(Taskfile) — аналогjustна YAML, если синтаксисjustне зашёл.- Bash-скрипты в
bin/— для одиночных сложных операций.justвызывает их как тонкая обёртка.
В 4Partners моно-репо just хорошо ложится поверх apps/frontend/, apps/backend/, bin/ — единая точка входа для всех частых команд.
Ссылки
- Manual: https://just.systems/man/en/
- Cheatsheet: https://cheatography.com/linux-china/cheat-sheets/justfile/
- GitHub: https://github.com/casey/just
- Awesome list: https://github.com/casey/just/wiki