first cycle lab release

This commit is contained in:
2026-10-04 00:30:09 +07:00
parent 85f6897365
commit d134daafd8
92 changed files with 5623 additions and 9 deletions
+171
View File
@@ -0,0 +1,171 @@
# SSH deployment — Phase 7
`./mcpack deploy` применяет готовый полный immutable release через SSH/rsync.
Если artifact ещё не создан, команда вызывает clean `build --release`;
если уже есть, проверяет его checksums и provenance. Development artifact
с `git.dirty=true` отклоняется, даже если текущий worktree уже чистый.
## Подготовка цели
На host нужны Linux x86_64, Node.js 22+, доступный Docker daemon, rsync,
GNU tar и zstd. Pinned Compose и небольшой Node agent с закреплённым YAML
parser передаются из локального bootstrap/dependencies; системный Docker
не устанавливается. У оператора должны быть права на весь runtime и его
владельцев; выбранные UID/GID согласовать с владельцем каталога.
В `platform/config.yaml` выбрать SSH host/alias и **отдельный root сборки**:
```yaml
server:
host: crab@192.168.1.61
directory: /home/crab/workspace/minecraft-lab/minecraft-lab-neoforge-spike
memory: 2G
port: 25565
```
Host `.env` готовится отдельно, вне releases, с EULA/UID/GID/memory/port
и стабильным MC_PROJECT_NAME. Runtime startup читает именно этот файл;
build defaults не перезаписывают host settings. Первое применение требует
пустого runtime без старого binding; существующий неопознанный мир не
принимается автоматически. У разных сборок разные roots, projects и ports.
SSH host key должен быть известен заранее. Используются BatchMode,
StrictHostKeyChecking=yes и keepalive. Ключи/порты задать в SSH config;
password-only сессия может использовать заранее аутентифицированный
ControlMaster. Пароль не записывается в config, env, artifact или agent.
```bash
./mcpack deploy --artifact /absolute/full-release
# Порт/ControlMaster/known_hosts в отдельном SSH config:
./mcpack deploy --artifact /absolute/full-release --ssh-config /absolute/ssh-config
# Node из nvm, которого нет в non-interactive SSH PATH:
./mcpack deploy --artifact /absolute/full-release \
--ssh-config /absolute/ssh-config \
--remote-node /home/crab/.nvm/versions/node/v26.10.0/bin/node
```
`--config FILE` выбирает другой platform config. Timeout берётся из его
`deploy.healthcheck_timeout`; он должен быть больше 10 секунд stability.
Первый image/dependency download может требовать 600+ секунд. IPv6 host
и другие сложные адреса сейчас задавать через SSH alias.
## Выполнение и state
Под SSH удерживается одна `.mcpack-runtime.lock` на всё получение релиза,
backup и применение. Перед передачей проверяются незавершённый deployment,
source/binding, target version collision, host settings/права и свободное место.
Релиз передаётся rsync в `releases/.incoming-*`, проверяется на host и
публикуется atomic rename. Сбой передачи не останавливает исходный сервер.
Agent в `.mcpack/agent-UUID/` также проверяется по полной checksum coverage.
Перед применением проверяется каждый replace-managed файл прошлого release.
При изменении/missing file или занятом новом managed path возвращается конфликт.
Для небольших server-owned text configs выводится content diff, для остальных
файлов — expected/actual hash. Неуправляемые файлы не удаляются. Нужно перенести
полезные правки в source и построить новую версию; флага force для молчаливого
перетирания конфигов нет.
Если тот же release уже активен, выполняется `health` без restart; успешный
повторный deploy возвращает noop. Одинаковая версия с другим manifest/checksums
отклоняется до остановки. Если активный release unhealthy, повторный deploy
не перезапускает его автоматически.
При смене релиза исходный сервер останавливается, подтверждается остановка,
создаётся verified snapshot всего runtime и исходного release. Только после
этого apply callback меняет binding, удаляет отсутствующие в новом релизе
известные replace-managed файлы и запускает target. Конфликты проверяются
повторно непосредственно перед apply. Image применяет pack/server overrides
при запуске; runtime-generated/unmanaged данные сохраняются.
После стабильного mc-health/status query обновляются previous-release и
active-release, каждый через atomic rename. Deployment state записывается
перед каждым этапом и связывает source/target identities, прежние pointers,
backup path и факт apply. Пара указателей не является единым filesystem
transaction: interrupted `committing` требует просмотра state перед recovery.
## Ошибка и ручное восстановление
Любой failed/incomplete `deployment-state.json` блокирует следующую операцию.
При прерывании SSH во время upload worker сохраняет failed state и освобождает
lock; при принудительном убийстве worker/power loss lock может остаться.
Не удалять stale lock, пока соответствующий host process действительно жив.
После ошибки **до apply** source/runtime binding не переключаются. При ошибке
backup сервер может остаться остановленным; проверить logs и backup operation.
После подтверждения неизменности мира можно запустить исходный release.
Failed state нужно сохранить для аудита и явно разрешить только после recovery.
Если `applied=false`, binding совпадает с active manifest, причина исправлена
и исходный сервер проверен health, сохранить failed record под отдельным именем
в server root, затем повторить deploy. Для ручного config edit новый clean
release должен содержать ровно текущие bytes этого managed файла — такой
осознанный перенос разрешается. Любое другое содержимое остаётся конфликтом.
Пример архивирования **только после перечисленных проверок на host**:
```bash
mv "$MC_ROOT/deployment-state.json" \
"$MC_ROOT/deployment-state-resolved-$(date -u +%Y%m%dT%H%M%SZ).json"
```
Не применять этот способ к `applied=true`, неизвестному binding или
interrupted committing: сначала восстановить/завершить реальное состояние.
После ошибки **apply/startup** target останавливается, source pointers не
продвигаются, binding остаётся на фактически применённом target и snapshot
сохраняется. Старый pack автоматически не запускается на изменённом мире.
Восстановить snapshot в новый root вместе с сохранённым source release по
[backups.md](backups.md), подготовить host settings, удалить старый контейнер,
запустить восстановленную копию и проверить health. Не менять binding вручную,
чтобы обойти защиту существующего мира.
Для первого неудачного deploy без snapshot использовать state/release/diagnostics:
пустой мир можно повторно инициализировать отдельно либо явно завершить запуск
того же target. При ошибке `committing` сначала проверить actual binding,
Docker health и оба pointer files. Snapshot recovery реализован в
[Phase 8](rollback.md). Простое удаление failed state не является recovery.
## Текущий проверенный host
На `crab@192.168.1.61:2228` подготовлен root
`/home/crab/workspace/minecraft-lab/minecraft-lab-neoforge-spike`.
Release 0.0.5 healthy (обновление публикации порта после Phase 7), UID/GID 1000:1000, memory 2G,
MC_PROJECT_NAME=minecraft-lab-neoforge-spike. Offline mode включён.
Clean release собран в отдельном checkout и опубликован локально:
```bash
./mcpack deploy \
--artifact dist/minecraft-lab-neoforge-spike-0.0.5 \
--ssh-config .tools/phase7-ssh-target/config \
--remote-node /home/crab/.nvm/versions/node/v26.10.0/bin/node
```
SSH config хранит только соединение/known_hosts/ControlPath. После окончания
ControlMaster для password login сначала заново аутентифицировать сессию:
```bash
ssh -F .tools/phase7-ssh-target/config minecraft-lab-target true
```
Host settings теперь содержат `MC_BIND_ADDRESS=0.0.0.0`; порт 25565
публикуется на всех IPv4-интерфейсах host. В Prism подключаться напрямую
к **192.168.1.61:25565**, без SSH tunnel. Локальный Phase 0 сервер остаётся
отдельным на 127.0.0.1:25565. Default для новых host остаётся 127.0.0.1;
LAN bind выбирается явно в host .env. Проверка — [network-results.md](network-results.md).
Сохранённый remote agent содержит `scripts/server.sh`. Для lifecycle на host
выбрать этот agent, задать абсолютные MC_ROOT/MC_RELEASE_DIR, указать nvm Node
в PATH и выполнить wrapper:
```bash
export PATH=/home/crab/.nvm/versions/node/v26.10.0/bin:$PATH
export MC_ROOT=/home/crab/workspace/minecraft-lab/minecraft-lab-neoforge-spike
export MC_RELEASE_DIR="$MC_ROOT/releases/$(cat "$MC_ROOT/active-release")"
# Подставить один из проверенных каталогов .mcpack/agent-*:
/absolute/agent/scripts/server.sh health
/absolute/agent/scripts/server.sh logs --tail 100
```
При failed deployment брать фактически применяемый release из state/binding,
а не автоматически active-release. Agents/releases/backups автоматически
не удаляются. Свидетельства и границы проверок — [phase7-results.md](phase7-results.md).