diff --git a/Cargo.toml b/Cargo.toml index a62b8a0..bef1a62 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,13 @@ [package] name = "xboctploy" version = "0.1.0" +description = "Lightweight CLI to deploy hobby projects from a local PC to a VPS over SSH" +license = "MIT" +readme = "README.md" +repository = "https://xboct-git.duckdns.org/Ku6epXBOCTuK/xboctploy" +keywords = ["deploy", "ssh", "sftp", "vps", "cli"] +categories = ["command-line-utilities", "development-tools"] +rust-version = "1.88" edition = "2024" [dependencies] @@ -8,7 +15,11 @@ anyhow = "1.0.104" clap = { version = "4.6.6", features = ["derive"] } colored = "3.1.1" home = "0.5.12" -russh = { version = "0.62.7", default-features = false, features = ["ring", "rsa", "flate2"] } +russh = { version = "0.62.7", default-features = false, features = [ + "ring", + "rsa", + "flate2", +] } russh-sftp = "2.4.0" serde = { version = "1.0.229", features = ["derive"] } ssh2-config = "0.7.2" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8861728 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Ku6epXBOCTuK + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..fc319a2 --- /dev/null +++ b/README.md @@ -0,0 +1,112 @@ +# xboctploy + +Легковесная CLI-утилита на Rust для централизованного деплоя хобби-проектов +(Node.js, Rust) с локального ПК на слабую VPS по SSH. + +- сборка, тесты и линтеры выполняются **локально** — CPU VPS не тратится +- артефакты передаются по **SFTP** (без rsync, работает на Windows/Linux/macOS) +- сервисные команды (`pm2 restart`, `systemctl restart`) запускаются **по SSH** +- fail-fast: упавший шаг немедленно останавливает деплой (ADR-005) + +## Установка + +```sh +cargo install --path . +``` + +Требуется Rust 1.80+. Кроссплатформенно: Windows (MSVC), Linux, macOS. + +## Быстрый старт + +```sh +xboctploy --list # показать проекты из конфига +xboctploy --dry-run demo # напечатать план деплоя без выполнения +xboctploy demo # задеплоить проект demo +``` + +Вывод: + +```txt +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/xboctploy/deploy.toml` — запуск «из любой папки». + +Схема: + +```toml +[projects.my-node-app] +workdir = "~/code/my-node-app" # где выполнять локальные команды (~ разворачивается) +host = "my-vps" # алиас из ~/.ssh/config или IP +user = "deploy" # опционально; иначе из ~/.ssh/config или юзер ОС +port = 2222 # опционально; иначе из ~/.ssh/config, иначе 22 +key_path = "~/.ssh/id_ed25519" # опционально; иначе identity_file из ssh-config, + # иначе ~/.ssh/id_rsa + +[projects.my-node-app.local] # шаги сборки на вашей машине +commands = ["npm ci", "npm run build"] + +[[projects.my-node-app.sync]] # что заливать на сервер +source = "dist" # файл или папка (относительно workdir) +target = "/var/www/pages" + +[projects.my-node-app.remote] # команды управления на сервере +commands = ["pm2 restart pages"] +``` + +Приоритет параметров SSH: явное значение из deploy.toml → `~/.ssh/config` → дефолт. + +### Windows-нюанс TOML + +В basic-строках `"..."` бэкслеши — escape-символы, путь `C:\Users` сломает парсинг. +Для путей Windows используйте литеральные строки `'...'`: + +```toml +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_path` → `identity_file` → `~/.ssh/id_rsa` + (ключи с passphrase пока не поддерживаются); +3. иначе — понятная ошибка. + +Проверка host key — TOFU поверх вашего `~/.ssh/known_hosts`: неизвестный хост +доверяется и запоминается, смена ключа сервера — громкая ошибка (MITM-защита). + +## Переменные окружения (.env) + +Если рядом с workdir проекта есть `.env`, его переменные (KEY=VALUE, `#` +комментарии, кавычки, префикс `export`) доступны всем локальным командам. +Переменные уже существующего окружения ОС имеют приоритет над `.env`. +`.env` не копируется на сервер, если не указан явно в `sync`. + +```env +# .env +NODE_ENV=production +API_TOKEN="secret" +export DB_URL=postgres://localhost/app +``` + +## Разработка + +```sh +just test # fmt + clippy -D warnings + cargo test +just run # деплой из test/deploy.toml (в git не входит) +```