docs: update docs, archive old

This commit is contained in:
2026-08-29 17:04:01 +05:00
parent 1a2bb73b62
commit 9979af9702
3 changed files with 46 additions and 0 deletions
+176
View File
@@ -0,0 +1,176 @@
# План реализации xboctploy
Источник требований: `docs/pdr-adr.md` (PRD + ADR-001..005).
Статус: утверждено — конфиг локальный с фолбэком на глобальный, scope полный
(включая .env + ssh-agent), cargo-проект в корне репо.
---
## 1. Зафиксированные решения
| Вопрос | Решение |
| ------------------------ | ------------------------------------------------------------------------------------------------------- |
| Расположение deploy.toml | Сначала `./deploy.toml` из текущей папки; если не найден — глобальный `~/.config/xboctploy/deploy.toml` |
| Объём работ | Полный roadmap включительно (.env + ssh-agent) |
| Размещение проекта | Cargo-проект в корне репозитория xboctploy/ |
| Имя бинарника | `xboctploy` |
| Модель выполнения | Синхронная (ADR-003), без tokio |
Локальный конфиг приоритетен — так тулза тестируется рядом с проектом,
а глобальный остаётся для запуска «из любой папки».
## 2. Целевая структура
```txt
Cargo.toml
src/
main.rs # вход, anyhow-ошибки, коды выхода (0 успех / 1 fail-fast)
cli.rs # clap derive: xboctploy <PROJECT> [--config PATH] [--dry-run] [--list]
config.rs # парсинг и валидация deploy.toml (serde + toml)
local.rs # локальные команды, fail-fast
ssh.rs # ssh2-config (~/.ssh/config), auth по ключу, exec удалённых команд
sync.rs # SFTP: файлы + рекурсивная загрузка папок
deploy.rs # оркестрация Local -> Sync -> Remote
docs/
pdr-adr.md
implementation-plan.md
```
## 3. Схема deploy.toml
```toml
[projects.my-node-app]
workdir = "~/code/my-node-app" # где выполняются локальные команды
host = "my-vps" # алиас из ~/.ssh/config или IP
user = "deploy"
port = 22
key_path = "~/.ssh/id_rsa" # опционально, дефолт ~/.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/node-app" # абсолютный путь на VPS
[projects.my-node-app.remote]
commands = ["pm2 restart node-app"]
```
В Rust: `Config { projects: HashMap<String, Project> }`. Валидация: host обязателен,
commands не пустые для секций, которые присутствуют; неизвестные поля — ошибка
(serde deny_unknown_fields), чтобы опечатки ловились сразу.
## 4. Этапы реализации
### Этап 1 — Каркас + конфиг
1. `cargo init`, прописать зависимости (без фиксации версий):
- clap (feature derive)
- serde (feature derive), toml
- anyhow
- ssh2, ssh2-config
- colored, walkdir, home
2. src/cli.rs: подкоманда по умолчанию — имя проекта; флаги --config, --dry-run, --list.
3. src/config.rs: структуры + резолв конфига по цепочке:
`--config PATH` -> `./deploy.toml` -> `~/.config/xboctploy/deploy.toml`;
если ничего не найдено — понятная ошибка. Развёртывание `~` через крейт home.
4. `--list`: печатает имена проектов из конфига.
5. Юнит-тесты: валидный TOML, битый TOML, отсутствие файла, развёртывание путей,
приоритет локального конфига над глобальным.
Критерий приёмки: `xboctploy --list` показывает проекты из тестового конфига;
битый конфиг даёт понятную ошибку с именем поля.
### Этап 2 — Локальные команды
1. src/local.rs: последовательный запуск commands в workdir.
2. Кроссплатформенность спавна: Windows — `cmd /C <cmd>`, Unix — `sh -c <cmd>`
(иначе npm/npx не резолвятся в .cmd на Windows).
3. stdout/stderr наследуются в терминал; ненулевой exit code ->
немедленный Err с текстом упавшей команды (ADR-005, fail-fast).
4. --dry-run: печатает список команд без запуска.
Критерий приёмки: на тестовом проекте `npm run build` выполняется;
падающий шаг останавливает деплой с красной ошибкой и exit code 1.
### Этап 3 — SSH + удалённые команды
1. src/ssh.rs: резолв хоста через ssh2-config (алиасы ~/.ssh/config:
hostname, user, port, identityfile), затем явные значения из deploy.toml
поверх конфига ssh.
2. Подключение: TCP -> handshake -> userauth_pubkey_file по ключу
(key_path или ~/.ssh/id_rsa).
3. exec удалённой команды через ChannelSession: стриминг stdout/stderr,
проверка channel.exit_status(); != 0 -> Err со stderr (fail-fast).
Критерий приёмки: `pm2 restart node-app` (или echo) выполняется на реальной VPS
по ключу; неверный ключ/хост даёт читаемую ошибку, а не панику.
### Этап 4 — SFTP-синхронизация
1. src/sync.rs: открытие SFTP-сессии поверх установленной SSH-сессии
(subsystem sfp через russh + крейт russh-sftp, см. ADR-006).
2. Одиночный файл: remote create/truncate -> запись -> shutdown; если target —
существующий каталог, файл кладётся внутрь с локальным именем.
3. Рекурсивная папка: walkdir по source; для каждой подпапки mkdir если её ещё нет
(проверка metadata, аналог mkdir -p); файлы поверх.
4. Лог: `[SFTP] transferring dist/ -> /var/www/node-app ... Done!`,
счётчик файлов/байт.
Критерий приёмки: dist/ целиком появляется на VPS, повторный деплой
не падает на существующих папках.
### Этап 5 — Оркестрация + UX
1. src/deploy.rs: пайплайн local -> sync -> remote, fail-fast между фазами.
2. Цветной вывод (colored) в формате PRD:
- зелёный [Local] ... Success!
- синий [SFTP] ... Done!
- жёлтый [Remote] ... Success!
- `Deploy successful!` / красный блок ошибки с stderr.
3. Тайминги каждого шага (Instant::now).
4. indicatif progress-bar решено не добавлять: статистика файлов/байт и тайминги
уже дают достаточно информации, а прогресс-бар усложнил бы вывод при
параллельных логах команд (ADR-002, минимум зависимостей).
Критерий приёмки: вывод визуально совпадает с примером UX из PRD;
--dry-run печатает весь план всех трёх фаз без единого действия.
### Этап 6 — Продвинутый уровень
1. `.env` из workdir: ручной парсер (~20 строк, KEY=VALUE, игнор комментариев),
переменные инжектятся в env локальных дочерних процессов; .env не заливается
на VPS, если не указан явно в sync.
2. ssh-agent: ssh2 agent_connect -> agent_list_identities -> userauth_agent.
3. Цепочка аутентификации: ssh-agent -> ключевой файл -> понятная ошибка.
4. Флаг переопределения конфига --config уже есть с этапа 1.
Критерий приёмки: агент в цепочке используется первым; .env-переменные
видны внутри локальных команд сборки.
### Этап 7 — Финализация
1. `cargo fmt`, `cargo clippy -- -D warnings`, `cargo test`.
2. `cargo build --release`.
3. README: установка (cargo install --path .), схема конфига, примеры.
4. Smoke-тест на реальной VPS вручную (владелец): node-проект end-to-end.
## 5. Отклонения от ADR-002 (минимальные, оправданные)
| Крейт | Причина |
| ----------------- | ---------------------------------------------- |
| walkdir | рекурсивное копирование папок по SFTP (этап 4) |
| colored | цветные логи требуются PRD раздел 3 (этап 5) |
| home | развёртывание `~` в путях конфига |
| indicatif (опция) | progress-bar, только если понадобится |
Всё остальное — строго по ADR-002. Асинхронность не вводим (ADR-003).
## 6. Стратегия верификации
- Юнит-тесты: config (парсинг, валидация, пути, приоритет локального конфига)
— этап 1; парсер .env — этап 6.
- Интеграционный smoke-тест против реальной VPS — вручную владельцем (этап 7);
при желании позже добавляется test с гейтом XBOCTPLOY_TEST_HOST.
- Линтеры на каждом этапе: cargo clippy -D warnings + cargo fmt --check.