build: закрыть обходы релизного гейта тестов и проверять весь граф npm
Два гейта сборки проверяли не то, что обещали.
1. pnpm audit проверял production-подграф вместо всего lock-графа.
Гейт запускался с --prod под обоснованием «devDependencies в артефакт не
попадают». Для frontend build tooling это неверно по существу: vite и
rollup действительно не копируются на production-сервер как node_modules,
но они ИСПОЛНЯЮТСЯ на build-машине, читают наши исходники и порождают тот
самый production-бандл, который уезжает в артефакт.
Это не гипотеза: DOM clobbering в Rollup затрагивал именно генерируемый
бандл, и `pnpm audit --prod` его не показывал — по всему графу тот же
прогон дал 33 предупреждения против нуля. Критерий приёмки №47 в docs/11
формулировал «по всему графу» правильно ещё до того, как это стало правдой
в коде.
На текущем lock-файле полный граф на пороге high чист.
2. SKIP_TESTS позволял собрать production-артефакт без тестов.
Переменная была описана как «аварийное отключение тестов; для
release-сборок недопустимо». Недопустимость держалась исключительно на этой
фразе: ни metadata, ни финальная приёмка архива не проверяли, что тесты
запускались. То есть
SKIP_TESTS=true ./tools/build/build.sh
доходила до конца и выдавала обычный tarball с build_profile=production и
dependency_security_gate=true — артефакт, по которому невозможно отличить
проверенную сборку от непроверенной.
Глушила она при этом не только тесты: под тем же флагом пропускались
`tsc --noEmit` для оркестратора и `go vet` для админки, то есть проверка
типов и статический анализ того самого кода, который уезжает в production.
Выбран тот же строгий вариант, что уже принят для проверки зависимостей:
обхода нет. Готовый пакет объявляет tests_gate=true в metadata, и это
утверждение опирается на результат — обе функции прогона выставляют свой
флаг только после успешного завершения, а write_metadata отказывается
писать метаданные, если хотя бы один не подтверждён.
Приёмка закрепляет оба инварианта: --prod не может вернуться в гейт, SKIP_TESTS
не может вернуться ни в один модуль сборки и ни в README/docs, tests_gate=true
обязателен в metadata, а утверждение о прогоне обязано следовать за прогоном.
This commit is contained in:
@@ -152,7 +152,20 @@ bundle из кода, который не проходит проверку ти
|
||||
| Проверка | Что покрывает | Порог |
|
||||
| --- | --- | --- |
|
||||
| `govulncheck ./...` | Go-граф **и stdlib**, с анализом достижимости: уязвимость считается только при наличии пути вызова из нашего кода | любая вызываемая |
|
||||
| `pnpm audit --prod` | production-зависимости frontend, без анализа достижимости | `PNPM_AUDIT_LEVEL` |
|
||||
| `pnpm audit` | **весь** lock-граф frontend, включая build tooling, без анализа достижимости | `PNPM_AUDIT_LEVEL` |
|
||||
|
||||
Про «весь граф» отдельно, потому что здесь стояло `--prod` с обоснованием
|
||||
«devDependencies в артефакт не попадают».
|
||||
|
||||
Для frontend build tooling это обоснование неверно по существу. `vite` и
|
||||
`rollup` действительно не копируются на production-сервер как `node_modules`.
|
||||
Но они **исполняются на build-машине, читают наши исходники и порождают тот
|
||||
самый production-бандл**, который уезжает в артефакт. Уязвимость в них — это
|
||||
уязвимость в том, что мы выпускаем.
|
||||
|
||||
Это не гипотеза: DOM clobbering в Rollup затрагивал именно генерируемый бандл, а
|
||||
проверка по одному production-подграфу его не показывала. По всему графу тот же
|
||||
прогон дал 33 предупреждения против нуля.
|
||||
|
||||
Версия `govulncheck` пиньтся в `versions.env`, а база уязвимостей подтягивается
|
||||
на каждом запуске: пин инструмента не должен превращаться в пин знаний о мире.
|
||||
@@ -180,6 +193,35 @@ bundle из кода, который не проходит проверку ти
|
||||
локальной работы обходить нечего: `go test ./...`, `govulncheck ./...` и
|
||||
`pnpm audit` запускаются напрямую и tarball не создают.
|
||||
|
||||
### Тесты и типы
|
||||
|
||||
Та же политика и по той же причине. Аварийного выхода у этого шага **нет**:
|
||||
переменной, отключающей тесты, не существует.
|
||||
|
||||
Проверяется на трёх участках:
|
||||
|
||||
| Шаг сборки | Что запускается |
|
||||
| --- | --- |
|
||||
| `run_orchestrator_tests` | `bun x tsc --noEmit`, `bun test` |
|
||||
| `bundle_ui` | `pnpm run typecheck` (`vue-tsc --noEmit`) до сборки bundle |
|
||||
| `run_admin_tests` | `go vet ./...`, `go test ./...` |
|
||||
|
||||
Готовый пакет объявляет об этом полем `tests_gate=true` в
|
||||
`metadata/package.env` — так же, как `dependency_security_gate` и
|
||||
`hysteria_compat_gate`. Значение у поля ровно одно, потому что не бывает
|
||||
пакета, собранного с пропущенными тестами: обе функции прогона выставляют свой
|
||||
флаг **после** успешного завершения, а `write_metadata` отказывается писать
|
||||
метаданные, если хотя бы один из них не выставлен. То есть поле остаётся
|
||||
утверждением о результате, а не переключателем.
|
||||
|
||||
Здесь существовала переменная, описанная как «аварийное отключение тестов; для
|
||||
release-сборок недопустимо». Недопустимость держалась исключительно на этой
|
||||
фразе: ни metadata, ни финальная приёмка архива не проверяли, что тесты
|
||||
запускались, поэтому сборка с ней доходила до конца и выдавала внешне
|
||||
неотличимый production-tarball. Глушила она при этом не только тесты, но и
|
||||
`tsc --noEmit` с `go vet` — то есть проверку типов и статический анализ того
|
||||
самого кода, который уезжает в production. История — в `CHANGELOG.md`.
|
||||
|
||||
### Проверка, а не генерация
|
||||
|
||||
`profile.ts`, `package/config/hy2xs.env` и `packageManager` в двух `package.json`
|
||||
|
||||
@@ -534,6 +534,31 @@ metadata. Подробности в [docs/02](02-build-layer-and-package.md); з
|
||||
трактовать как «уязвимостей нет». По той же причине недоступность реестра npm
|
||||
для `pnpm audit` — это отказ проверки, а не её отрицательный результат.
|
||||
|
||||
`pnpm audit` проверяет **весь** lock-граф frontend, а не production-подграф:
|
||||
build tooling исполняется на build-машине и порождает production-бандл, поэтому
|
||||
уязвимость в нём уезжает в артефакт. Приёмка сборки следит, чтобы `--prod` не
|
||||
вернулся в гейт.
|
||||
|
||||
## A11a. Обязательные тесты (build)
|
||||
|
||||
Гейт тестов устроен так же, как гейт зависимостей: аварийного выхода нет,
|
||||
результат виден по готовому артефакту.
|
||||
|
||||
| Шаг сборки | Что запускается |
|
||||
| --- | --- |
|
||||
| `run_orchestrator_tests` | `bun x tsc --noEmit`, `bun test` |
|
||||
| `bundle_ui` | `pnpm run typecheck` до сборки bundle |
|
||||
| `run_admin_tests` | `go vet ./...`, `go test ./...` |
|
||||
|
||||
Приёмка проверяет:
|
||||
|
||||
- отключающей тесты переменной нет ни в одном модуле сборки, ни в README/docs
|
||||
(место для истории — `CHANGELOG.md`);
|
||||
- `metadata/package.env` содержит `tests_gate=true`;
|
||||
- утверждение о прогоне выставляется **после** самого прогона, а не до него;
|
||||
- `write_metadata` отказывается писать метаданные, если хотя бы один из двух
|
||||
прогонов не подтверждён.
|
||||
|
||||
## A12. Приёмка проверяет код, а не упоминания
|
||||
|
||||
Два контракта приёмки на снимке до этого патча **гарантированно роняли сборку на
|
||||
@@ -953,7 +978,7 @@ hy2xs-orchestrator doctor
|
||||
37. отключённый `bootstrap-admin-peer` остаётся отключённым после перезапуска
|
||||
38. резервная копия с `includeSecrets=true` завершается ошибкой целиком, если секрет хотя бы одного пира недоступен
|
||||
39. админка не генерирует `HYSTERIA2_TRAFFIC_STATS_SECRET` сама: пустой env при пустой базе — отказ старта
|
||||
40. проверка зависимостей на уязвимости не имеет обходов ни в сборке, ни в документации
|
||||
40. проверка зависимостей на уязвимости не имеет обходов ни в сборке, ни в документации, и покрывает весь lock-граф frontend
|
||||
41. `apps/go.mod` объявляет `toolchain`, совпадающий с `GO_VERSION` из `versions.env`
|
||||
42. `tools/dev/doctor.sh` / `doctor.ps1` показывают расхождение среды разработки с `versions.env`
|
||||
43. маршруты-алиасы `/:id/client-url` и `/:id/qr` удалены и не входят в публичный API v1
|
||||
@@ -967,3 +992,4 @@ hy2xs-orchestrator doctor
|
||||
51. невозможность записать состояние отказа не отменяет откат: восстановление выполняется, в журнале остаётся отметка о неудавшейся записи
|
||||
52. `install-state.json` пишется одним писателем, атомарно и с `fsync` файла и каталога: после потери питания на диске лежит либо прежний полный документ, либо новый полный
|
||||
53. ownership-флаг маркера установки взводится **до** записи, поэтому отказ на `chown` не даёт `fatal_pre_apply` при уже созданном файле
|
||||
54. тесты и проверка типов не имеют обходов ни в сборке, ни в документации; `metadata/package.env` содержит `tests_gate=true`, и это утверждение опирается на фактический прогон
|
||||
|
||||
Reference in New Issue
Block a user