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:
+100
-33
@@ -16,7 +16,9 @@ dist/hy2xs-install-<version>.tar.gz
|
||||
|
||||
Основные части проекта:
|
||||
|
||||
- [`versions.env`](../../versions.env) — контракт продукта, платформы и toolchain. Единственный источник истины для версий и контрольных сумм.
|
||||
- [`tools/build/build.sh`](build.sh) — главный entrypoint сборки.
|
||||
- [`tools/build/lib/versions.sh`](lib/versions.sh) — загрузка `versions.env` и `verify_versions_contract`.
|
||||
- [`tools/build/lib/deps.sh`](lib/deps.sh) — проверка Debian/amd64, установка build dependencies, установка Go/Bun/Node.js/pnpm.
|
||||
- [`tools/build/lib/package.sh`](lib/package.sh) — сборка orchestrator, сборка HY2XS admin, создание stage directory и tar.gz архива.
|
||||
- [`tools/build/lib/verify.sh`](lib/verify.sh) — проверка структуры репозитория и итогового архива.
|
||||
@@ -53,24 +55,49 @@ Windows и macOS можно использовать для редактиров
|
||||
|
||||
При запуске builder:
|
||||
|
||||
1. Проверяет, что host — Debian 13 amd64.
|
||||
2. Проверяет структуру репозитория.
|
||||
3. Устанавливает недостающие системные build dependencies через `apt-get`.
|
||||
4. Проверяет или скачивает локальные версии:
|
||||
- Go `1.21.13`;
|
||||
- Bun `1.3.13`;
|
||||
- Node.js `20.19.0`;
|
||||
- pnpm `9.15.9`.
|
||||
5. Прогоняет тесты и типы оркестратора (`bun test`, `tsc --noEmit`).
|
||||
6. Разрешает upstream-версию Hysteria, скачивает артефакт и считает SHA-256.
|
||||
7. Проходит compatibility gate: реальный бинарник Hysteria должен принять канонический конфиг HY2XS.
|
||||
8. Копирует package skeleton.
|
||||
9. Собирает install-only orchestrator в standalone binary.
|
||||
10. Собирает frontend и backend HY2XS admin в Linux amd64 binary.
|
||||
11. Прогоняет `go vet` и `go test` для HY2XS admin (после сборки frontend, потому что `go:embed all:dist` требует готовых ассетов).
|
||||
12. Записывает metadata и checksums.
|
||||
13. Создаёт `dist/hy2xs-install-<version>.tar.gz`.
|
||||
14. Проверяет архив и прогоняет acceptance-проверки.
|
||||
1. Загружает и валидирует [`versions.env`](../../versions.env).
|
||||
2. Проверяет, что host соответствует `HY2XS_BUILD_*` (по умолчанию Debian 13 amd64).
|
||||
3. Проверяет структуру репозитория.
|
||||
4. Устанавливает недостающие системные build dependencies через `apt-get`.
|
||||
5. Проверяет или скачивает локальные версии Go/Bun/Node.js/pnpm **из контракта**
|
||||
и сверяет каждый архив с контрольной суммой из `versions.env`.
|
||||
6. Выполняет `verify_versions_contract`: рассинхрон версий роняет сборку до создания tarball.
|
||||
7. Прогоняет тесты и типы оркестратора (`bun test`, `tsc --noEmit`).
|
||||
8. Разрешает upstream-версию Hysteria, берёт ожидаемый SHA-256 из upstream `hashes.txt` и сверяет с ним скачанный артефакт.
|
||||
9. Проходит compatibility gate: реальный бинарник Hysteria должен принять канонический конфиг HY2XS.
|
||||
10. Копирует package skeleton.
|
||||
11. Собирает install-only orchestrator в standalone binary.
|
||||
12. Собирает frontend и backend HY2XS admin в Linux amd64 binary, проставляя версию админки через ldflags.
|
||||
13. Прогоняет `go vet` и `go test` для HY2XS admin (после сборки frontend, потому что `go:embed all:dist` требует готовых ассетов).
|
||||
14. Записывает metadata и checksums.
|
||||
15. Создаёт `dist/hy2xs-install-<version>.tar.gz`.
|
||||
16. Проверяет архив и прогоняет acceptance-проверки.
|
||||
|
||||
## Контракт версий
|
||||
|
||||
Версии продукта, платформы и toolchain объявлены в корневом
|
||||
[`versions.env`](../../versions.env). Собственных значений по умолчанию у
|
||||
`deps.sh` больше нет: без загруженного контракта сборка падает сразу.
|
||||
|
||||
Подход — **проверка, а не генерация**. `profile.ts`,
|
||||
`package/config/hy2xs.env` и `packageManager` в обоих `package.json` остаются
|
||||
обычными файлами, чтобы `bun test`, `tsc` и `go test` работали из чистого
|
||||
чекаута до запуска сборки. `verify_versions_contract` сверяет их с контрактом и
|
||||
роняет сборку при расхождении.
|
||||
|
||||
Контракт оркестратора сверяется не grep'ом по исходникам, а выводом
|
||||
[`orchestrator/tools/print-contract.ts`](../../orchestrator/tools/print-contract.ts):
|
||||
это доказывает, что в бинарь попало то же значение.
|
||||
|
||||
Версия админки приезжает в бинарь через ldflags
|
||||
(`-X 'hy2xs-admin/model/constant.Version=v${HY2XS_VERSION}'`) и проверяется
|
||||
запуском собранного `hy2xs-admin version`. Захардкоженной константы версии в
|
||||
Go-коде больше нет: она уже успела разъехаться с версией пакета.
|
||||
|
||||
Чего в `versions.env` нет намеренно: прикладных зависимостей (для них есть
|
||||
`pnpm-lock.yaml`, `bun.lock`, `go.sum`) и конкретной версии Hysteria (здесь
|
||||
только политика `HYSTERIA_CHANNEL`, результат резолва — в
|
||||
[`hysteria-lock.env`](hysteria-lock.env)).
|
||||
|
||||
## Версия Hysteria: разрешение и compatibility gate
|
||||
|
||||
@@ -81,11 +108,32 @@ Builder не хранит версию Hysteria вручную. По умолч
|
||||
1. канонический upstream — `HyNetworks/hysteria`;
|
||||
2. принимаются только стабильные релизы, без draft и prerelease;
|
||||
3. тег должен иметь вид `app/vX.Y.Z`;
|
||||
4. берётся ровно один артефакт `hysteria-linux-amd64`;
|
||||
4. берётся ровно один артефакт `hysteria-linux-amd64` и ровно один `hashes.txt`;
|
||||
5. URL используется в том виде, в каком его вернул upstream API, без пересборки строки;
|
||||
6. SHA-256 считается локально от скачанного файла;
|
||||
6. ожидаемый SHA-256 берётся из upstream `hashes.txt`, и скачанный бинарник сверяется с ним;
|
||||
7. разрешённые значения попадают в metadata пакета.
|
||||
|
||||
### Почему hashes.txt, а не локальный пересчёт
|
||||
|
||||
Раньше SHA-256 считался от уже скачанного файла. Это защищает target от
|
||||
последующей подмены, но не доказывает, что builder скачал именно ожидаемый
|
||||
upstream artifact: сумма фиксирует то, что пришло, каким бы оно ни было.
|
||||
|
||||
Формат ассета:
|
||||
|
||||
```text
|
||||
6493dfff…f94 build/hysteria-linux-amd64
|
||||
f24f63be…189 build/hysteria-linux-amd64-avx
|
||||
```
|
||||
|
||||
Сопоставление идёт по базовому имени и строго на равенство: `build/` — часть
|
||||
пути, а `hysteria-linux-amd64-avx` — другой артефакт, который не должен
|
||||
совпасть по префиксу. Разбор вынесен в `parseUpstreamHashes`
|
||||
([`hysteriaRelease.ts`](../../orchestrator/src/build/hysteriaRelease.ts)) и
|
||||
покрыт юнит-тестами.
|
||||
|
||||
Источник ожидаемой суммы фиксируется в metadata как `hysteria_sha_source`.
|
||||
|
||||
Сравнение версий числовое, поэтому `v2.9.10` считается новее `v2.9.2`.
|
||||
|
||||
После разрешения обязателен compatibility gate:
|
||||
@@ -114,9 +162,10 @@ BUILD FAILED: unsupported Hysteria stable v2.13.0
|
||||
|
||||
| Переменная | По умолчанию | Назначение |
|
||||
| --- | --- | --- |
|
||||
| `HYSTERIA_CHANNEL` | `stable` | `stable` — разрешить последнюю стабильную через upstream API; `pinned` — офлайн-сборка по `hysteria-lock.env` |
|
||||
| `HYSTERIA_CHANNEL` | из `versions.env` (`stable`) | `stable` — разрешить последнюю стабильную через upstream API; `pinned` — офлайн-сборка по `hysteria-lock.env` |
|
||||
| `HYSTERIA_VERSION_OVERRIDE` | пусто | Закрепить конкретную версию `vX.Y.Z` |
|
||||
| `HYSTERIA_COMPAT_GATE` | `true` | Compatibility gate; для release-сборок обязателен |
|
||||
| `HYSTERIA_VERIFY_UPSTREAM_HASHES` | `true` | Сверять артефакт с upstream `hashes.txt`; отключение — только break-glass |
|
||||
| `HYSTERIA_WRITE_LOCK` | `false` | Записать разрешённые значения обратно в `hysteria-lock.env` |
|
||||
| `HYSTERIA_GATE_PORT` | `34443` | UDP-порт для временного запуска Hysteria в gate |
|
||||
| `HYSTERIA_GATE_STATS_PORT` | `34712` | TCP-порт trafficStats в gate |
|
||||
@@ -151,6 +200,16 @@ BUN_FLAVOR=auto
|
||||
- если CPU поддерживает AVX2, используется `bun-linux-x64`;
|
||||
- если CPU не поддерживает AVX2, используется `bun-linux-x64-baseline`.
|
||||
|
||||
Поскольку артефакта два, одной контрольной суммы архитектурно недостаточно. В
|
||||
[`versions.env`](../../versions.env) зафиксированы обе:
|
||||
|
||||
```bash
|
||||
BUN_LINUX_X64_SHA256=<sha256>
|
||||
BUN_LINUX_X64_BASELINE_SHA256=<sha256>
|
||||
```
|
||||
|
||||
Ожидаемый digest выбирается уже **после** `select_bun_artifact()`.
|
||||
|
||||
Можно принудительно задать flavor:
|
||||
|
||||
```bash
|
||||
@@ -171,12 +230,10 @@ BUN_FLAVOR=x64-baseline ./tools/build/build.sh
|
||||
./tools/build/build.sh
|
||||
```
|
||||
|
||||
С явной версией и build id:
|
||||
Версия пакета берётся из `versions.env`, поэтому обычно нужен только build id:
|
||||
|
||||
```bash
|
||||
PACKAGE_VERSION=1.0.0 \
|
||||
BUILD_ID=prod-$(date -u +%Y%m%dT%H%M%SZ) \
|
||||
./tools/build/build.sh
|
||||
BUILD_ID=prod-$(date -u +%Y%m%dT%H%M%SZ) ./tools/build/build.sh
|
||||
```
|
||||
|
||||
## Проверка результата
|
||||
@@ -207,9 +264,13 @@ tar -xOzf dist/hy2xs-install-1.0.0.tar.gz hy2xs-install/metadata/package.env
|
||||
Обновлять и коммитить [`orchestrator/bun.lock`](../../orchestrator/bun.lock) следует только когда:
|
||||
|
||||
- изменился [`orchestrator/package.json`](../../orchestrator/package.json);
|
||||
- изменился `BUN_REQUIRED` в [`tools/build/lib/deps.sh`](lib/deps.sh);
|
||||
- изменился `BUN_VERSION` в [`versions.env`](../../versions.env);
|
||||
- зависимости оркестратора обновляются осознанно.
|
||||
|
||||
При смене `BUN_VERSION` нужно обновить и `packageManager` в
|
||||
`orchestrator/package.json`, и обе контрольные суммы Bun: иначе
|
||||
`verify_versions_contract` остановит сборку.
|
||||
|
||||
Production builder всегда выполняет:
|
||||
|
||||
```bash
|
||||
@@ -225,7 +286,7 @@ bun install --frozen-lockfile
|
||||
Правила управления пакетами frontend:
|
||||
|
||||
- [`apps/frontend/package.json`](../../apps/frontend/package.json) объявляет `"packageManager": "pnpm@9.15.9"`;
|
||||
- builder использует закреплённый pnpm `9.15.9` из [`PNPM_REQUIRED`](lib/deps.sh);
|
||||
- builder использует закреплённый pnpm из `PNPM_VERSION` в [`versions.env`](../../versions.env), и `verify_versions_contract` сверяет эти два значения;
|
||||
- production-путь установки frontend всегда:
|
||||
|
||||
```bash
|
||||
@@ -236,18 +297,21 @@ pnpm install --frozen-lockfile
|
||||
|
||||
## Полезные переменные
|
||||
|
||||
- `PACKAGE_VERSION=1.0.0`
|
||||
- `BUILD_ID=prod-$(date -u +%Y%m%dT%H%M%SZ)`
|
||||
- `BUN_FLAVOR=auto|x64|x64-baseline`
|
||||
- `FRONTEND_NODE_OLD_SPACE_SIZE=2048` (default memory limit for frontend build step)
|
||||
- `TOOLCHAIN_DIR=/custom/path/.toolchain`
|
||||
- `VERIFY_TOOLCHAIN_CHECKSUMS=true`
|
||||
- `VERSIONS_ENV_FILE=/custom/path/versions.env`
|
||||
|
||||
По умолчанию `VERIFY_TOOLCHAIN_CHECKSUMS=true` в [`tools/build/lib/deps.sh`](lib/deps.sh), поэтому для production-сборки обязательно передавать контрольные суммы:
|
||||
`PACKAGE_VERSION` берётся из `versions.env` (`HY2XS_VERSION`); переопределять
|
||||
его вручную нужно только для отладочных сборок, и `verify_versions_contract`
|
||||
такую сборку отклонит.
|
||||
|
||||
- `GO_ARCHIVE_SHA256=<sha256>`
|
||||
- `NODE_ARCHIVE_SHA256=<sha256>`
|
||||
- `BUN_ARCHIVE_SHA256=<sha256>`
|
||||
Контрольные суммы toolchain больше **не передаются через окружение**: они
|
||||
объявлены в `versions.env`. Раньше воспроизводимая сборка в чистой Debian-среде
|
||||
требовала предварительного знания четырёх SHA-256 и не запускалась одной
|
||||
командой.
|
||||
|
||||
## Политика памяти при сборке frontend
|
||||
|
||||
@@ -292,7 +356,7 @@ echo "bun_exit=$?"
|
||||
rm -rf .toolchain/bun .toolchain/bun-tmp
|
||||
rm -f .toolchain/downloads/bun-linux-x64-*.zip
|
||||
rm -f .toolchain/downloads/bun-linux-x64-baseline-*.zip
|
||||
PACKAGE_VERSION=1.0.0 ./tools/build/build.sh
|
||||
./tools/build/build.sh
|
||||
```
|
||||
|
||||
Проверить shell syntax:
|
||||
@@ -300,7 +364,10 @@ PACKAGE_VERSION=1.0.0 ./tools/build/build.sh
|
||||
```bash
|
||||
bash -n tools/build/build.sh
|
||||
bash -n tools/build/lib/common.sh
|
||||
bash -n tools/build/lib/versions.sh
|
||||
bash -n tools/build/lib/deps.sh
|
||||
bash -n tools/build/lib/hysteria.sh
|
||||
bash -n tools/build/lib/package.sh
|
||||
bash -n tools/build/lib/verify.sh
|
||||
bash -n tools/build/lib/acceptance.sh
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user