- PVSM.RU - https://www.pvsm.ru -
Привет! 👋
Знаете это чувство, когда ты просто хочешь написать код, но вместо этого полдня ковыряешь bash-скрипты? Или когда твой Makefile разрастается до таких размеров, что открывать его страшно, а редактировать — опасно для психики?
Если да — добро пожаловать в клуб.
Меня зовут Даниил, и я устал. Устал от того, что инструменты, которые должны помогать, начинают мешать. Устал гуглить "как передать аргументы в make target" и "почему в bash if не работает". И в один прекрасный момент я решил: хватит.
Я взял Rust, закрылся в комнате и написал Nest. И сегодня я хочу рассказать, почему вам, возможно, тоже пора выбросить свой Makefile.
Давайте честно: make — это легенда. Он с нами с 1976 года. Он собирал Unix, он собирал Linux, он собирал, наверное, вообще всё. Но давайте признаем: он не для нас.
Он для сборки C-файлов. Он для отслеживания зависимостей компиляции. Он не для того, чтобы запускать докер-контейнеры, деплоить на стейджинг или гонять линтеры с хитрыми флагами.
Каждый раз, когда я вижу это:
.PHONY: deploy
deploy:
@echo "Deploying..."
./deploy.sh $(ARGS) # А передадутся ли аргументы? А если пробелы?
...у меня дергается глаз. Табы вместо пробелов? Серьезно? В 2026 году?
А потом появляются баш-скрипты. deploy.sh, build.sh, test.sh. Они валяются в корне, в папке scripts, в папке bin. Никто не помнит, что они делают. Никто не хочет их трогать.
Конечно, я не первый, кто это заметил. Есть Just [1].
Just — классный. Он убрал табы, добавил нормальные аргументы. Это был глоток свежего воздуха. Я пользовался им какое-то время. Но потом мой Justfile тоже начал пухнуть.
Проблема Just (и Make) в том, что они плоские. Все команды свалены в одну кучу. Хочешь сделать db reset? Пиши db-reset. Хочешь test frontend? Пиши test-frontend.
build-backend:
cargo build
build-frontend:
npm run build
deploy-prod:
...
Это не структура. Это список покупок на неделю. А мне нужна иерархия. Я хочу писать nest db reset, nest dev start, nest build frontend. Я хочу, чтобы мои задачи были организованы так же красиво, как мой код.
Так родился Nest.
Я решил, что таск-раннер должен быть:
Читаемым. Никаких скобок, никаких $(). Чистый DSL на отступах, как в Python или YAML.
Структурированным. Вложенность команд любой глубины.
Типизированным. Если я жду число, я хочу получить число, а не строку, которую надо парсить в баше.
Умным. Переменные из .env файлов, шаблонизация, авто-help.
И, конечно, он должен быть написан на Rust. Потому что:
Скорость. Он летает.
Один бинарник. Никаких зависимостей. Скачал и работаешь. Статическая линковка (musl) — работает везде.
Безопасность. Ну, вы знаете.
Давайте сравним.
Make:
# Mmakefile
run:
ifdef HOT
nodemon src/index.js
else
node src/index.js
endif
# Запуск: make run HOT=true (или HOT=1? или yes?)
Just:
# Justfile
run hot='false':
if [ "{{hot}}" == "true" ]; then nodemon src/index.js; else node src/index.js; fi
# Запуск: just run true (или just run --hot true)
Nest:
# nestfile
run(!hot|h: bool = false):
> desc: Run server with optional hot reload
> script: |
#!/bin/sh
if [ "{{hot}}" = "true" ]; then
nodemon src/index.js
else
node src/index.js
fi
Запуск:
nest run # hot = false
nest run -h true # hot = true
nest run --hot # Ошибка! Ждет bool, а не просто флаг (пока что, но строгость — это хорошо)
Вот где Nest сияет. Вы можете группировать команды логически.
database:
> desc: Database operations
reset(!force: bool = false):
> desc: Drop and recreate database
> script: ...
migrate(step: num = 1):
> desc: Run migrations
> script: ...
И вызывать это как человек:
nest database reset --force true
nest database migrate 2
Автокомплит в терминале (zsh, bash, fish) генерируется сам, так что вам не придется вспоминать, какие там команды.
Один из моих любимых примеров — когда нужно запустить что-то для списка сервисов. В make это боль (через foreach или shell-хаки). В Nest это нативно:
restart(services: arr, !force: bool = false):
> desc: Restart specific services
> script: |
#!/bin/sh
for svc in {{services}}; do
echo "Restarting $svc..."
if [ "{{force}}" = "true" ]; then
docker-compose restart -t 0 "$svc"
else
docker-compose restart "$svc"
fi
done
Запуск:
nest restart --services web,api,db
# или
nest restart api --force true
А вот что-то более реальное. Деплой с проверкой ветки, подтверждением и уведомлением.
deploy(version: str, !env|e: str = "prod"):
> desc: Deploy to environment
> env: .env.{{env}} # Подгружаем .env.prod или .env.staging
> env: DEPLOYER={{user}}
# Сначала запускаем тесты (зависимость)
> depends: test(scope="all")
> script: |
#!/bin/bash
set -e
echo "Deploying version {{version}} to {{env}}..."
echo "User: $DEPLOYER"
echo "DB Host: $DB_HOST" # Пришло из .env файла
# Простая защита от дурака
if [ "{{env}}" = "prod" ]; then
read -p "Are you sure? (y/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Aborted."
exit 1
fi
fi
./deploy-script.sh --tag {{version}} --target $SERVER_IP
Сколько раз у вас падал CI, и вы узнавали об этом только через час? Nest умеет запускать fallback скрипты при ошибке.
# Определяем константы
@const SLACK_WEBHOOK = "https://hooks.slack.com/..."
backup():
> desc: Backup database
> fallback: alert_failure
> script: |
pg_dump dbname > dump.sql
# Допустим, тут кончилось место на диске и скрипт упал
alert_failure():
> desc: Send alert to Slack
> script: |
curl -X POST -H 'Content-type: application/json'
--data '{"text":"Backup failed! Check server."}'
{{SLACK_WEBHOOK}}
Если backup упадет, Nest автоматически запустит alert_failure. Никаких || ./alert.sh в каждой строке.
Если у вас монорепозиторий, вам понравятся глобальные переменные и переиспользуемые функции.
@var TAG = "latest"
@const REGISTRY = "docker.io"
# Функция (это не таск, её нельзя вызвать из CLI напрямую, только из скриптов)
@function build_image(name: str):
docker build -t {{REGISTRY}}/{{name}}:{{TAG}} .
build-front():
> script: build_image(name="frontend")
build-back():
> script: build_image(name="backend")
Это позволяет писать DRY (Don't Repeat Yourself) конфигурации.
Если Nestfile разросся, просто разбейте его на файлы. Nest умеет собирать конфиг из кусочков.
# Основной nestfile
@include db.nest
@include docker/*.nest # Да, маски тоже работают!
@include services/ # И целые папки
# Остальные таски...
Теперь db.nest лежит отдельно, но его команды доступны как родные. Это спасение для команд, где каждый сервис хочет свой кусочек конфига.
Nest парсит свой DSL, строит граф команд и исполняет их. Все переменные шаблонизируются через {{variable}}.
Плюс, я добавил удобную работу с окружением:
deploy(version: str):
> env: .env.production # Загрузит переменные из файла
> env: USER={{user}} # Подставит текущего юзера
> env: DATE={{now}} # И дату
> script: ./deploy.sh
npm scripts? Хороши для JS, но ужасны для всего остального. Попробуйте написать там сложную логику без && и || ада.
Gulp/Grunt? JS-only, требуют node_modules. Nest — это один бинарник.
Bash? Сложно поддерживать. Нет типов. Нет help.
Потому что писать DSL в блокноте — это моветон. Я написал полноценное расширение для VS Code с подсветкой синтаксиса, навигацией и сниппетами.
В планах добавить:
Запуск команд по клику (CodeLens).
Автокомплит имен переменных.
Валидацию "на лету".
Так что опыт (DX) будет приятным. Сами файлы Nestfile выглядят в редакторе красиво и читаемо.
Я использую Nest каждый день в своих пет-проектах и даже начал внедрять на работе (пока никто не видит).
Да, это MVP. Там нет плагинов, нет параллельного выполнения (пока). Но оно решает главную проблему: превращает хаос скриптов в упорядоченную систему.
Проект полностью открыт (MIT). Я буду рад, если вы попробуете, напишете ишью или даже кинете PR.
А если вам тоже надоело воевать с Makefile — дайте Nest шанс. Вдруг вам понравится?
👉 GitHub: https://github.com/quonaro/Nest [2]
📦 Установка (Linux/macOS):
curl -fsSL https://raw.githubusercontent.com/quonaro/nest/main/install.sh | sh
P.S. Если у вас тоже депрессия от тулинга — пишите в комменты, поплачем вместе.
Спасибо за внимание!
Автор: quonaro
Источник [3]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/make/442113
Ссылки в тексте:
[1] Just: https://github.com/casey/just
[2] https://github.com/quonaro/Nest: https://github.com/quonaro/Nest
[3] Источник: https://habr.com/ru/articles/986112/?utm_campaign=986112&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.