build: закрепить новые инварианты приёмкой и документацией

verify_versions_contract получил сверку API namespace. Путь machine-auth
записывается в /etc/hysteria/config.yaml и в post-install.env, то есть по нему
Hysteria обращается к админке. Пока строка была продублирована в шаблонах,
smoke, тестах, приёмке и e2e, расхождение обнаруживалось только на живом
сервере. Теперь Go-константы, API_BASE фронтенда и оба шаблона сверяются
против значений, скомпилированных в оркестратор.

Приёмка проверяет, что:
  - fatal_pre_apply недостижим после записи install-state;
  - каждый ownership-флаг взводится раньше своего шага;
  - у read-only фазы нет универсального раннера, через который можно
    проскользнуть;
  - инвариант публичного endpoint живёт в preflight и не обращается к внешним
    сервисам определения IP;
  - purge-v0.sh и clean-host описывают одну границу;
  - секреты не попадают в персистентный файл экспорта;
  - импорт пиров валидируется так же строго, как их создание;
  - удалённые exportConfig/importConfig не вернулись.

Захардкоженная схема =2 в приёмке заменена на значение из versions.env: при
переходе на schema 3 пришлось бы помнить ещё и про эту строку.

Документация: контракт раннеров и ownership в 08, инвариант публичного
endpoint в 08/09/12/13 и README, сетевая идентичность панели и удалённые
export/import в 04, сценарии D1 (отказ сразу после PHASE 0) и D2 (устаревший
DNS после смены IPv4) в 11, версии package.json как не-версия продукта в 02.
This commit is contained in:
2026-08-27 20:50:03 +05:00
parent a88268b0cd
commit 086b5d6624
15 changed files with 740 additions and 37 deletions
+54
View File
@@ -164,11 +164,64 @@ verify_orchestrator_contract() {
HY2XS_TARGET_ARCH)
expect_equal "orchestrator target arch" "$value" "$HY2XS_TARGET_ARCH"
;;
HY2XS_ADMIN_API_BASE)
HY2XS_ADMIN_API_BASE="$value"
;;
HY2XS_HYSTERIA_MACHINE_AUTH_PATH)
HY2XS_HYSTERIA_MACHINE_AUTH_PATH="$value"
;;
*)
fail "versions contract: unexpected orchestrator contract line: $line"
;;
esac
done <<<"$output"
[ -n "${HY2XS_ADMIN_API_BASE:-}" ] \
|| fail "versions contract: orchestrator did not report HY2XS_ADMIN_API_BASE"
[ -n "${HY2XS_HYSTERIA_MACHINE_AUTH_PATH:-}" ] \
|| fail "versions contract: orchestrator did not report HY2XS_HYSTERIA_MACHINE_AUTH_PATH"
export HY2XS_ADMIN_API_BASE HY2XS_HYSTERIA_MACHINE_AUTH_PATH
}
# API namespace — это runtime-контракт между тремя компонентами.
#
# Путь machine-auth уезжает в /etc/hysteria/config.yaml и в post-install.env,
# то есть по нему Hysteria обращается к админке. Пока строка была размазана по
# шаблонам, smoke, тестам и e2e, любое расхождение обнаруживалось только на
# живом сервере. Здесь она сверяется во всех местах сразу — против константы,
# скомпилированной в оркестратор.
verify_api_namespace_contract() {
local go_auth_path go_api_base
go_auth_path="$(grep -Eo 'HysteriaMachineAuthPath[[:space:]]*=[[:space:]]*"[^"]*"' apps/model/constant/api.go \
| head -n1 | sed -E 's/.*"([^"]*)"/\1/')"
go_api_base="$(grep -Eo 'AdminAPIBase[[:space:]]*=[[:space:]]*"[^"]*"' apps/model/constant/api.go \
| head -n1 | sed -E 's/.*"([^"]*)"/\1/')"
expect_equal "apps constant.HysteriaMachineAuthPath" "$go_auth_path" "$HY2XS_HYSTERIA_MACHINE_AUTH_PATH"
expect_equal "apps constant.AdminAPIBase" "$go_api_base" "$HY2XS_ADMIN_API_BASE"
local frontend_base
frontend_base="$(grep -Eo 'const API_BASE = "[^"]*"' apps/frontend/src/utils/request.ts \
| head -n1 | sed -E 's/.*"([^"]*)"/\1/')"
expect_equal "frontend API_BASE" "$frontend_base" "$HY2XS_ADMIN_API_BASE"
grep -qF "${HY2XS_HYSTERIA_MACHINE_AUTH_PATH}?access_token={{HYSTERIA_API_SECRET}}" \
package/templates/hysteria/config.yaml.tpl \
|| fail "versions contract: hysteria template does not use ${HY2XS_HYSTERIA_MACHINE_AUTH_PATH}"
grep -qF "${HY2XS_HYSTERIA_MACHINE_AUTH_PATH}?access_token={{HYSTERIA_API_SECRET}}" \
package/templates/env/post-install.env.tpl \
|| fail "versions contract: post-install env template does not use ${HY2XS_HYSTERIA_MACHINE_AUTH_PATH}"
# Старое пространство имён не имеет права вернуться ни в один компонент.
# Историческое имя допустимо только в docs/14-legacy-cleanup.md и в
# legacy-маркерах clean-host: там это имя чужого артефакта, а не наше.
local legacy_hits
legacy_hits="$(grep -rlF '/hui' \
apps/model apps/router apps/controller apps/service apps/frontend/src \
orchestrator/src orchestrator/test package/templates tools/test 2>/dev/null || true)"
[ -z "$legacy_hits" ] \
|| fail "versions contract: legacy /hui namespace came back in: $legacy_hits"
}
verify_go_toolchain_contract() {
@@ -206,6 +259,7 @@ verify_versions_contract() {
"$HY2XS_CONFIG_SCHEMA_VERSION"
verify_orchestrator_contract
verify_api_namespace_contract
verify_go_toolchain_contract
expect_equal "build host OS" "$HY2XS_BUILD_OS" "debian"