Files
xboct-deploy/README.md
T

7.6 KiB
Raw Blame History

xboct-deploy

Легковесная CLI-утилита на Rust для централизованного деплоя хобби-проектов (Node.js, Rust) с локального ПК на слабую VPS по SSH.

  • сборка, тесты и линтеры выполняются локально — CPU VPS не тратится
  • артефакты передаются по SFTP (без rsync, работает на Windows/Linux/macOS)
  • сервисные команды (pm2 restart, systemctl restart) запускаются по SSH
  • fail-fast: упавший шаг немедленно останавливает деплой (ADR-005)

Установка

cargo install --path .

Требуется Rust 1.88+. Кроссплатформенно: Windows (MSVC), Linux, macOS.

Быстрый старт

xboct-deploy --list          # показать проекты из конфига
xboct-deploy --list-servers  # показать серверы из конфига
xboct-deploy --pick          # интерактивно выбрать проект (нечёткий поиск)
xboct-deploy --dry-run demo  # напечатать план деплоя без выполнения
xboct-deploy demo            # задеплоить проект demo
xboct-deploy --this          # задеплоить проект текущей папки (глобальный конфиг)

Вывод:

deploying 'demo'
[Local] running: npm run build... Success! (1.2s)
[SFTP] transferring dist to /var/www/pages... Done! (14 file(s), 231.5 KiB) (829ms)
[Remote] running: pm2 restart pages... Success! (102ms)
Deploy successful! (total 2.3s)

Конфигурация

Файл ищется по цепочке:

  1. --config PATH, если указан явно;
  2. ./deploy.toml в текущей папке — удобно держать рядом с проектом и в тестах;
  3. глобальный ~/.config/xboct-deploy/deploy.toml — запуск «из любой папки».

--this деплоит проект, чей workdir совпадает с текущей папкой; конфиг ищется по обычной цепочке, так что временный локальный ./deploy.toml переопределяет глобальный — создали файл, задеплоились, удалили.

Схема — один проект = один блок, все параметры точечными ключами:

[servers.my-vps]                 # профиль сервера: описывается один раз
host = "195.0.2.10"              # алиас из ~/.ssh/config или IP
port = 2222
user = "deploy"
key_path = "~/.ssh/id_ed25519"
base_dir = "/var/www/pages"      # корень для относительных sync target

[projects.my-node-app]
server = "my-vps"                # ссылка на профиль SSH
workdir = 'C:\code\my-node-app'  # где выполнять локальные команды (~ разворачивается)
branch = "main"                  # опционально: защита от деплоя чужой ветки
env.PUBLIC_BASE_PATH = "/my-node-app"     # переменные для локальной сборки
local.commands = ["npm ci", "npm run build"]  # шаги сборки на вашей машине
sync = "dist"                    # залить dist -> <base_dir>/my-node-app
remote.commands = ["pm2 restart pages"]    # команды управления по SSH

Поле sync

Три формы — выбирай по смыслу:

sync = "dist"                                  # один источник; target = имя проекта
sync = ["dist", "static"]                      # несколько источников; target = имя проекта
sync = [{ source = "web/build", target = "/opt/static" }]  # полные правила

Во всех формах target без / на конце резолвится через base_dir сервера ("my-node-app"/var/www/pages/my-node-app). Абсолютный target ("/opt/static") используется как есть. Отдельных блоков [[...sync]], [projects.x.env], [projects.x.local], [projects.x.remote] больше нет — это не нужно помнить и копировать, всё в блоке проекта.

Проект может задать параметры подключения и напрямую (host, user, port, key_path вместо server) — тогда они имеют приоритет над профилем.

Приоритет параметров SSH: проект → [servers.<name>]~/.ssh/config → дефолт.

Локальные команды после деплоя (local_after)

Выполняются на вашей машине после sync + remote и всегда — и при успехе, и после любого упавшего шага (deploy всё равно упадёт). Для очистки артефактов или вебхука с результатом:

[projects.my-node-app]
server = "my-vps"
workdir = 'C:\code\my-node-app'
local.commands = ["npm run build"]
sync = "dist"
remote.commands = ["pm2 restart pages"]

[projects.my-node-app.local_after]
commands = [
  'del /q dist',
  'curl -s -X POST https://example.com/hook -d "result=%XD_DEPLOY_RESULT%"',
]

Внутри доступны переменные (в cmd — %VAR%, в sh — $VAR):

  • XD_DEPLOY_RESULTsuccess либо failed;
  • XD_ERROR — текст ошибки (только при failed).

Эти же переменные доступны в --dry-run, где показываются в плане без выполнения.

Windows-нюанс TOML

В basic-строках "..." бэкслеши — escape-символы, путь C:\Users сломает парсинг. Для путей Windows используйте литеральные строки '...':

workdir = 'C:\code\my-node-app'

или прямые слэши C:/code/my-node-app.

Аутентификация

Цепочка (первый успешный способ используется):

  1. ssh-agent: OpenSSH-агент (named pipe на Windows / SSH_AUTH_SOCK на Unix), затем Pageant; перебираются все ключи агента;
  2. файловый ключ key_pathidentity_file~/.ssh/id_rsa (ключи с passphrase пока не поддерживаются);
  3. иначе — понятная ошибка.

Проверка host key — TOFU поверх вашего ~/.ssh/known_hosts: неизвестный хост доверяется и запоминается, смена ключа сервера — громкая ошибка (MITM-защита).

Переменные окружения (.env)

Если рядом с workdir проекта есть .env, его переменные (KEY=VALUE, # комментарии, кавычки, префикс export) доступны всем локальным командам. .env не копируется на сервер, если не указан явно в sync.

Приоритет источников: [env] из deploy.toml → .env → окружение ОС.

# .env
NODE_ENV=production
API_TOKEN="secret"
export DB_URL=postgres://localhost/app

Разработка

just test   # fmt + clippy -D warnings + cargo test
just run    # деплой из test/deploy.toml (в git не входит)