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`
|
||||
|
||||
Reference in New Issue
Block a user