docs: clean-install-only, versions.env и очистка предыдущего поколения
Новый docs/14-legacy-cleanup.md: как выглядит отказ установщика, полный список маркеров чужой установки, что сохранить перед очисткой, работа purge-v0.sh, ручная процедура и отдельно - случай незавершённой установки текущего поколения, где нужен repair, а не очистка. Обновлено под фактическое поведение: - README и package/docs: установка описана как две фазы, PHASE 0 ничего не меняет; добавлен troubleshooting по отказу clean-host; версии toolchain больше не передаются через окружение; - 02-build-layer: раздел про versions.env (что в нём есть и чего нет и почему), verify_versions_contract, проверка происхождения артефакта по upstream hashes.txt; - 08-orchestrator-spec: двухфазный контракт, read-only guard, идентификация поколения в install-state, ownership-aware rollback, расширенная семантическая проверка конфига, структурная редакция; - 04-admin-panel: таблица удалённых маршрутов и почему они удалены, а не оставлены заглушками; сужена формулировка гарантии санитайза; - 11-testing: новые unit-наборы, полный список инвариантов конфига, раздел про одну реализацию URI вместо двух, сценарий проверки границы установки на живом сервере; - 12-operations и 13-runbook: диагностика отказов по поколению, поведение diagnostics-бандла; - tools/build/README: контракт версий, обе суммы Bun, hashes.txt. CHANGELOG: раздел Unreleased с разбором каждого исправленного дефекта.
This commit is contained in:
@@ -40,20 +40,103 @@ Builder не является частью target install flow: на target serv
|
||||
- шаблоны для `post-install.env`
|
||||
- package metadata
|
||||
|
||||
## Контракт версий: `versions.env`
|
||||
|
||||
Корневой `versions.env` — **единственный источник истины** для контракта
|
||||
«продукт / платформа / toolchain».
|
||||
|
||||
Что в нём есть:
|
||||
|
||||
```bash
|
||||
HY2XS_VERSION=1.0.0
|
||||
HY2XS_RELEASE_LINE=1
|
||||
HY2XS_CONFIG_SCHEMA_VERSION=2
|
||||
|
||||
HY2XS_BUILD_OS=debian
|
||||
HY2XS_BUILD_OS_VERSION=13
|
||||
HY2XS_BUILD_ARCH=amd64
|
||||
HY2XS_TARGET_OS=debian
|
||||
HY2XS_TARGET_OS_VERSION=13
|
||||
HY2XS_TARGET_ARCH=amd64
|
||||
|
||||
GO_VERSION=1.21.13
|
||||
GO_LINUX_AMD64_SHA256=<sha256>
|
||||
BUN_VERSION=1.3.13
|
||||
BUN_LINUX_X64_SHA256=<sha256>
|
||||
BUN_LINUX_X64_BASELINE_SHA256=<sha256>
|
||||
NODE_VERSION=20.19.0
|
||||
NODE_LINUX_X64_SHA256=<sha256>
|
||||
PNPM_VERSION=9.15.9
|
||||
|
||||
HYSTERIA_CHANNEL=stable
|
||||
```
|
||||
|
||||
Чего в нём **нет** и быть не должно:
|
||||
|
||||
1. **Прикладных зависимостей** (Vue, Gin, GORM, npm/Go модули). У них уже есть
|
||||
канонические lock-механизмы: `apps/frontend/pnpm-lock.yaml`,
|
||||
`orchestrator/bun.lock`, `apps/go.sum`. Второй слой неизбежно разъедется с
|
||||
настоящим графом зависимостей.
|
||||
2. **Конкретной версии Hysteria.** Здесь живёт только *политика* выбора
|
||||
(`HYSTERIA_CHANNEL`); результат резолва замораживается в
|
||||
`tools/build/hysteria-lock.env`. Пин версии здесь вернул бы ручное
|
||||
обновление, от которого мы ушли.
|
||||
|
||||
Два Bun-артефакта зафиксированы отдельно намеренно: `select_bun_artifact()`
|
||||
выбирает `bun-linux-x64` или `bun-linux-x64-baseline` по наличию AVX2, поэтому
|
||||
одной контрольной суммы архитектурно недостаточно.
|
||||
|
||||
### Проверка, а не генерация
|
||||
|
||||
`profile.ts`, `package/config/hy2xs.env` и `packageManager` в двух `package.json`
|
||||
остаются обычными файлами. Сборка их **не генерирует**, а сверяет шагом
|
||||
`verify_versions_contract`.
|
||||
|
||||
Причина: генерируемые исходники ломают чистый чекаут — `bun test`, `tsc` и
|
||||
`go test` должны работать до запуска сборки. Проверка даёт тот же инвариант
|
||||
дешевле.
|
||||
|
||||
`verify_versions_contract` сверяет:
|
||||
|
||||
| Что | С чем |
|
||||
| --- | --- |
|
||||
| `PACKAGE_VERSION` | `HY2XS_VERSION` |
|
||||
| `orchestrator/package.json` → `packageManager` | `bun@$BUN_VERSION` |
|
||||
| `apps/frontend/package.json` → `packageManager` | `pnpm@$PNPM_VERSION` |
|
||||
| `package/config/hy2xs.env` → схема | `HY2XS_CONFIG_SCHEMA_VERSION` |
|
||||
| константы, **скомпилированные** в оркестратор | схема, release line, целевая платформа |
|
||||
| `apps/go.mod` → директива `go` | `GO_VERSION` |
|
||||
| `metadata/package.env` | версия, release line, схема, target |
|
||||
| `hy2xs-admin version` (готовый бинарь) | `v$HY2XS_VERSION` |
|
||||
|
||||
Контракт оркестратора сверяется не grep'ом по исходникам, а выводом
|
||||
`orchestrator/tools/print-contract.ts`: это доказывает, что в бинарь попало то
|
||||
же значение.
|
||||
|
||||
Версия админки приходит в бинарь через ldflags:
|
||||
|
||||
```bash
|
||||
-ldflags "-s -w -X 'hy2xs-admin/model/constant.Version=v${HY2XS_VERSION}'"
|
||||
```
|
||||
|
||||
Собственной константы версии в Go-коде больше нет: она уже успела разъехаться с
|
||||
версией пакета.
|
||||
|
||||
## Что делает 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.
|
||||
2. Загружает и проверяет контракт `versions.env`.
|
||||
3. Прогоняет тесты и типы оркестратора.
|
||||
4. Разрешает upstream-версию Hysteria и проходит compatibility gate.
|
||||
5. Компилирует оркестратор из Bun/TypeScript в install-артефакт.
|
||||
6. Собирает / подготавливает HY2XS admin и сверяет его версию с контрактом.
|
||||
7. Прогоняет тесты HY2XS admin (после сборки frontend: `go:embed all:dist` требует готовых ассетов).
|
||||
8. Копирует артефакты UI в package staging directory.
|
||||
9. Кладёт entrypoint, templates, docs и service files.
|
||||
10. Формирует итоговый install package.
|
||||
11. Считает manifest/checksum.
|
||||
12. Проверяет архив и прогоняет acceptance-проверки.
|
||||
13. Выдаёт один переносимый результат для target machine.
|
||||
|
||||
## Что builder не делает
|
||||
|
||||
@@ -110,14 +193,19 @@ project/
|
||||
|
||||
`tools/build/build.sh` должен быть самодостаточным для Debian 13 amd64:
|
||||
|
||||
1. Проверяет ОС и архитектуру.
|
||||
1. Проверяет ОС и архитектуру по `versions.env` (`HY2XS_BUILD_*`).
|
||||
2. Проверяет структуру репозитория и lock-файлы.
|
||||
3. Доставляет отсутствующие системные build-зависимости через `apt-get`.
|
||||
4. Проверяет версии Go, Bun, Node.js и pnpm.
|
||||
5. При несовпадении версий скачивает управляемый локальный toolchain в `.toolchain/`.
|
||||
4. Проверяет версии Go, Bun, Node.js и pnpm по `versions.env`.
|
||||
5. При несовпадении версий скачивает управляемый локальный toolchain в `.toolchain/`
|
||||
и **сверяет каждый архив с контрольной суммой из `versions.env`**.
|
||||
6. Собирает только Linux amd64 артефакты.
|
||||
7. Записывает версии toolchain в metadata пакета.
|
||||
|
||||
Собственных значений по умолчанию у `tools/build/lib/deps.sh` больше нет: без
|
||||
загруженного контракта сборка падает сразу, а не собирает пакет на неизвестном
|
||||
toolchain.
|
||||
|
||||
## Отношение к Hysteria2
|
||||
|
||||
Сам бинарь Hysteria2 **не вендорится** в install package как baseline-правило.
|
||||
@@ -135,10 +223,13 @@ SOURCE
|
||||
resolve latest stable (HyNetworks/hysteria, только теги app/vX.Y.Z)
|
||||
│
|
||||
▼
|
||||
resolve exact release asset (hysteria-linux-amd64)
|
||||
resolve exact release asset (hysteria-linux-amd64 + hashes.txt)
|
||||
│
|
||||
▼
|
||||
download + compute SHA-256
|
||||
download hashes.txt → ожидаемый SHA-256 от upstream
|
||||
│
|
||||
▼
|
||||
download artifact + сверка с ожидаемым SHA-256
|
||||
│
|
||||
▼
|
||||
compatibility gate (реальный бинарник принимает канонический конфиг HY2XS)
|
||||
@@ -165,8 +256,43 @@ TARGET SERVER
|
||||
| `HYSTERIA_VERSION_OVERRIDE` | пусто | Закрепить конкретную версию `vX.Y.Z` |
|
||||
| `HYSTERIA_COMPAT_GATE` | `true` | Compatibility gate; для release-сборок обязателен |
|
||||
| `HYSTERIA_WRITE_LOCK` | `false` | Записать разрешённые значения обратно в `tools/build/hysteria-lock.env` |
|
||||
| `HYSTERIA_VERIFY_UPSTREAM_HASHES` | `true` | Сверять артефакт с upstream `hashes.txt`; отключение — только break-glass |
|
||||
| `GITHUB_TOKEN` | пусто | Опционально, чтобы не упереться в anonymous rate limit |
|
||||
|
||||
### Проверка происхождения артефакта
|
||||
|
||||
Раньше SHA-256 считался локально от уже скачанного файла. Это защищает target от
|
||||
последующей подмены, но не доказывает, что builder скачал именно ожидаемый
|
||||
upstream artifact: сумма фиксирует то, что пришло, каким бы оно ни было
|
||||
(trust-on-first-use).
|
||||
|
||||
Upstream публикует контрольные суммы релиза отдельным ассетом `hashes.txt`:
|
||||
|
||||
```text
|
||||
6493dfff…f94 build/hysteria-linux-amd64
|
||||
f24f63be…189 build/hysteria-linux-amd64-avx
|
||||
```
|
||||
|
||||
Поэтому порядок теперь такой:
|
||||
|
||||
```text
|
||||
download hysteria-linux-amd64
|
||||
download hashes.txt
|
||||
↓
|
||||
ожидаемый SHA-256 из upstream
|
||||
↓
|
||||
сверка скачанного бинарника
|
||||
↓
|
||||
и только после этого — запись SHA-256 в HY2XS lock и metadata
|
||||
```
|
||||
|
||||
Сопоставление идёт по базовому имени и строго на равенство: `build/` — часть
|
||||
пути, а `hysteria-linux-amd64-avx` — другой артефакт, который не должен совпасть
|
||||
по префиксу. Разбор вынесен в `parseUpstreamHashes` и покрыт тестами.
|
||||
|
||||
Источник ожидаемой суммы фиксируется в `metadata/package.env`
|
||||
(`hysteria_sha_source=upstream-hashes | hy2xs-lock | local-download`).
|
||||
|
||||
Дополнительно:
|
||||
- версия, URL и SHA256 фиксируются в metadata install package (`metadata/hysteria.version`, `metadata/hysteria.url`, `metadata/hysteria.sha256`);
|
||||
- способ выбора версии фиксируется в `metadata/hysteria.resolution` и `metadata/package.env`;
|
||||
@@ -180,7 +306,7 @@ Gate защищает от ситуации, когда upstream меняет с
|
||||
|
||||
Порядок:
|
||||
|
||||
1. скачать артефакт и сверить SHA-256;
|
||||
1. скачать артефакт и сверить SHA-256 с upstream `hashes.txt`;
|
||||
2. сверить `hysteria version` с разрешённой версией;
|
||||
3. отрендерить канонический конфиг HY2XS тем же кодом, что работает на target (`orchestrator/tools/render-canonical-config.ts`);
|
||||
4. запустить реальный бинарник Hysteria с этим конфигом — для Gecko и для Salamander;
|
||||
@@ -206,3 +332,5 @@ BUILD FAILED: unsupported Hysteria stable v2.13.0
|
||||
6. Hysteria2 подтягивается install layer'ом с upstream по замороженным координатам, а не собирается на target из исходников
|
||||
7. выход новой версии Hysteria после сборки не меняет содержимое уже собранного пакета
|
||||
8. несовместимый upstream ломает сборку, а не установку у пользователя
|
||||
9. контрольная сумма Hysteria подтверждена upstream-ассетом `hashes.txt`, а не только локальным пересчётом
|
||||
10. версии продукта, платформы и toolchain объявлены в одном месте, а рассинхрон роняет сборку до создания tarball
|
||||
|
||||
Reference in New Issue
Block a user