# Build layer and package ## Цель документа Зафиксировать локальный слой сборки и формат итогового install package. ## Базовое решение В baseline builder остаётся **shell-first** для packaging-слоя. То есть: - основной packaging pipeline — **sh/bash** - оркестратор при этом пишется на **Bun + TypeScript** - builder локально компилирует оркестратор в готовый install-артефакт - target machine не должна сама собирать или доустанавливать JS/TS toolchain Причина простая: packaging можно держать простым, а оркестратор — typed и модульным. ## Где работает builder Production builder работает на отдельном build host: - Debian 13 - amd64 / x86_64 - bash - доступ к интернету для apt и скачивания toolchain В текущей production-модели сборка выполняется **на Debian 13 amd64**, а не на Windows/macOS dev-машине. Builder не является частью target install flow: на target server приезжает уже готовый install package, без JS/TS/Go build step. ## Что хранится в репозитории проекта Минимум: - исходники оркестратора на **Bun + TypeScript** - shell packaging scripts - шаблоны конфигов - systemd unit templates - docs - исходный код **HY2XS admin** - шаблоны для `post-install.env` - package metadata ## Что делает builder 1. Проверяет структуру проекта. 2. Прогоняет тесты и типы оркестратора. 3. Разрешает upstream-версию Hysteria и проходит compatibility gate. 4. Компилирует оркестратор из Bun/TypeScript в install-артефакт. 5. Собирает / подготавливает HY2XS admin. 6. Прогоняет тесты HY2XS admin (после сборки frontend: `go:embed all:dist` требует готовых ассетов). 7. Копирует артефакты UI в package staging directory. 8. Кладёт entrypoint, templates, docs и service files. 9. Формирует итоговый install package. 10. Считает manifest/checksum. 11. Проверяет архив и прогоняет acceptance-проверки. 12. Выдаёт один переносимый результат для target machine. ## Что builder не делает - не ставит Hysteria2 на локальной машине «для продакшена» - не превращается в CI/CD платформу - не генерирует update pipeline - не делает uninstall manifests - не готовит миграции между старыми инсталляциями ## Рекомендуемая структура ```text project/ ├── tools/ │ └── build/ │ ├── build.sh │ ├── README.md │ └── lib/ ├── orchestrator/ │ ├── package.json │ ├── bun.lock │ ├── tsconfig.json │ └── src/ ├── package/ │ ├── install.sh │ ├── orchestrator/ │ ├── templates/ │ └── systemd/ ├── ui/ │ └── hy2xs-admin/ ├── docs/ └── dist/ ``` ## Формат итогового пакета Итоговый пакет должен содержать: - install-only orchestrator artifact - bundled HY2XS admin - unit templates - config templates - docs / examples - manifest версии проекта Итоговый пакет **не должен** содержать: - builder scripts - исходную локальную build-среду - временные каталоги сборки - мусор CI - target-side dependency install step для оркестратора ## Production builder bootstrap `tools/build/build.sh` должен быть самодостаточным для Debian 13 amd64: 1. Проверяет ОС и архитектуру. 2. Проверяет структуру репозитория и lock-файлы. 3. Доставляет отсутствующие системные build-зависимости через `apt-get`. 4. Проверяет версии Go, Bun, Node.js и pnpm. 5. При несовпадении версий скачивает управляемый локальный toolchain в `.toolchain/`. 6. Собирает только Linux amd64 артефакты. 7. Записывает версии toolchain в metadata пакета. ## Отношение к Hysteria2 Сам бинарь Hysteria2 **не вендорится** в install package как baseline-правило. Причина: - ядро Hysteria рассматривается как stable upstream component; - целевая установка скачивает его с official upstream, но **строго по замороженным координатам**. ### Разрешение версии на сборке ```text SOURCE │ ▼ resolve latest stable (HyNetworks/hysteria, только теги app/vX.Y.Z) │ ▼ resolve exact release asset (hysteria-linux-amd64) │ ▼ download + compute SHA-256 │ ▼ compatibility gate (реальный бинарник принимает канонический конфиг HY2XS) │ ▼ PACKAGE METADATA version = vX.Y.Z exact_url = sha256 = <...> resolution = latest-stable | pinned | override │ ▼ TARGET SERVER скачивает уже конкретный неизменяемый артефакт ``` Так одновременно выполняются оба требования: «по умолчанию брать последнюю стабильную» и «production-установка должна быть детерминированной и проверяемой». Переменные builder: | Переменная | Значение по умолчанию | Назначение | | --- | --- | --- | | `HYSTERIA_CHANNEL` | `stable` | `stable` — разрешить последнюю стабильную через upstream API; `pinned` — взять `tools/build/hysteria-lock.env` без сети | | `HYSTERIA_VERSION_OVERRIDE` | пусто | Закрепить конкретную версию `vX.Y.Z` | | `HYSTERIA_COMPAT_GATE` | `true` | Compatibility gate; для release-сборок обязателен | | `HYSTERIA_WRITE_LOCK` | `false` | Записать разрешённые значения обратно в `tools/build/hysteria-lock.env` | | `GITHUB_TOKEN` | пусто | Опционально, чтобы не упереться в anonymous rate limit | Дополнительно: - версия, URL и SHA256 фиксируются в metadata install package (`metadata/hysteria.version`, `metadata/hysteria.url`, `metadata/hysteria.sha256`); - способ выбора версии фиксируется в `metadata/hysteria.resolution` и `metadata/package.env`; - runtime `reconfigure` не обновляет и не откатывает бинарник Hysteria2; - install flow валидирует SHA256 и фактическую версию установленного бинарника; - install-time код не обращается к upstream API и не использует moving `latest` — это проверяется тестами и acceptance-шагом сборки. ### Compatibility gate Gate защищает от ситуации, когда upstream меняет схему конфигурации, а builder молча собирает неработающий HY2XS. Порядок: 1. скачать артефакт и сверить SHA-256; 2. сверить `hysteria version` с разрешённой версией; 3. отрендерить канонический конфиг HY2XS тем же кодом, что работает на target (`orchestrator/tools/render-canonical-config.ts`); 4. запустить реальный бинарник Hysteria с этим конфигом — для Gecko и для Salamander; 5. только после этого собирать release package. При несовместимости ломается сборка: ```text BUILD FAILED: unsupported Hysteria stable v2.13.0 ``` Это осознанно: ошибка должна проявиться на build machine, а не на сервере оператора. ## Инварианты Система считается правильной, если: 1. builder запускается на Debian 13 amd64 build host, не как target-side build step 2. пакет можно перенести на чистый Debian 13 3. на сервере нет отдельного build step 4. bundled UI уже находится внутри пакета 5. оркестратор authored as Bun/TypeScript, но на target приходит как готовый install-артефакт 6. Hysteria2 подтягивается install layer'ом с upstream по замороженным координатам, а не собирается на target из исходников 7. выход новой версии Hysteria после сборки не меняет содержимое уже собранного пакета 8. несовместимый upstream ломает сборку, а не установку у пользователя