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 решает только одну задачу: сохранять и запускать проектные команды.

Свойствоmakejust
НазначениеСборка артефактовЗапуск команд
.PHONYНуженНе нужен
КроссплатформенностьЗависит от GNU makeLinux / 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-overridedotenv перекрывает окружение
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() — путь к текущему justfile
  • justfile_directory() — каталог justfile
  • invocation_directory() — откуда вызвали just
  • source_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/pnpm scripts — если проект чисто 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