feat(ci): проверка values HelmRelease по схеме чарта #204

Closed
hermes wants to merge 3 commits from feat/ci-values-schema into main
Collaborator

Superseded by #205.

This PR carried a repository-local checker for HelmRelease values (first python, then shell). Both variants are dropped in favour of a ready-made tool: the CI gate becomes the community flux-local action, which inflates the cluster and validates every release against its chart schema, so no script of our own is needed.

The only thing that blocked flux-local was two releases whose chart comes from a git repository (harbor, vault), and that is being solved at the source: #205 mirrors those charts into our own Harbor as OCI artifacts, after which the gate covers all releases without exceptions.

No action needed on the branch; feat/ci-values-schema can be deleted.

Superseded by #205. This PR carried a repository-local checker for HelmRelease values (first python, then shell). Both variants are dropped in favour of a ready-made tool: the CI gate becomes the community flux-local action, which inflates the cluster and validates every release against its chart schema, so no script of our own is needed. The only thing that blocked flux-local was two releases whose chart comes from a git repository (harbor, vault), and that is being solved at the source: #205 mirrors those charts into our own Harbor as OCI artifacts, after which the gate covers all releases without exceptions. No action needed on the branch; feat/ci-values-schema can be deleted.
feat(ci): проверка values HelmRelease по схеме чарта
All checks were successful
Flate / Flate - Filter (pull_request) Successful in 3s
Flate / Flate (pull_request) Has been skipped
Flate / Values - Schema (pull_request) Has been skipped
Flate / Flate - Success (pull_request) Successful in 3s
baa427b460
## Что было

20.09.2026 смёржили PR #70, и реконсиляция упала на `Helm upgrade failed ... values don't
meet the specifications of the schema(s)`: в values app-template 5.2.1 стоял ключ `optional`
внутри `valueFrom.secretKeyRef`, которого схема чарта не знает. Пайплайн был зелёным, потому
что `flate test all` проверяет сборку kustomize и полноту объектов, схему чарта не спрашивает
(issue #72). Ошибка видна только в кластере, в статусе HelmRelease и событиях namespace.

## Что правит PR

- `scripts/check-helmrelease-values.py` — предполётная проверка значений по схеме их чарта.
  Обходит дерево, собирает HelmRelease и их источники (`chartRef` -> OCIRepository),
  достаёт чарт из реестра анонимно (challenge -> token -> манифест -> слой чарта), берёт из
  чарта `values.schema.json` и `values.yaml`, считает coalesced-значения так же, как это делает
  Helm, и валидирует их схемой. Источник ищется по имени из `chartRef`, уточняется по
  namespace и по расположению в дереве: `app-template` объявлен один раз в `flux-system`,
  собственные источники повторяются по именам между namespace.
  Непроверяемые релизы (чарт из git, чарт без `values.schema.json`) печатаются строками `skip`
  и причиной — «не проверено» не должно выглядеть как «проверено». Чарт качается один раз
  на прогон (кэш каталога `--cache`): 25 источников на весь репозиторий.
  `--self-test` доказывает, что гейт имеет силу: фикстура, где лежит ключ `optional` из PR #70.
- `.github/workflows/flate.yaml` — джоба `Values - Schema`. Место выбрано не случайно: мёрж
  держит агрегатор `Flate - Success`, поэтому джоба в этом workflow блокирует мёрж, тогда как
  чек отдельного workflow — нет. Джоба создаёт venv: системный python в образе задачи
  помечен externally-managed (PEP 668), при этом PyYAML и jsonschema нужны только ей.
- `AGENTS.md` — команда локального прогона, требование к гейту (self-test) и обновлённый список
  контекстов CI.

## Проверено

- Релизы на `main` (c424978): `26` прошли схему, ошибок `0`, пропущено `14` (11 чартов схему
  не объявляют вовсе, `harbor` и `vault` — чарт из git). Проверка заведена «зелёной»:
  текущее состояние репозитория схему проходит уже сейчас.
- `--self-test`: фикстура, где лежит ключ `optional` из PR #70, даёт код 1 и сообщение
  `controllers.self-test.containers.app.env: {... 'optional': True} is not valid under any of the
  given schemas`; та же фикстура без этого ключа даёт код 0. Это ровно критерий готовности
  из issue #72, только не в кластере, но в чеке.
- `flate test all -p ./kubernetes/flux/cluster` — `154 passed` за 8 секунд (прежняя цифра в
  `AGENTS.md`, 117, была от прошлой ревизии репозитория; обновил её здесь же).
- `zizmor --offline .github/workflows/flate.yaml` — findings нет.
- Чекер неоднозначных символов Forgejo — чисто по обеим локалям (`_default`, `ru`).

## После мёржа

- Правок вне git нет: ни Vault, ни кластера, ни DNS.
- Защита ветки: если контексты перечислены поимённо, добавить `Flate / Values - Schema`.
  Агрегатор `Flate - Success` блокирует мёрж и без этой правки.

## Откат

Revert коммита: джоба исчезает, workflow возвращается к прежнему виду, скрипт остаётся
неиспользуемым.
ci(flate): правка скрипта проверки тоже запускает проверку
All checks were successful
Flate / Flate - Filter (pull_request) Successful in 5s
Flate / Flate (pull_request) Has been skipped
Flate / Values - Schema (pull_request) Successful in 1m8s
Flate / Flate - Success (pull_request) Successful in 3s
596c42ee68
Проверка схемы стоит на своём наборе путей: `kubernetes/**` плюс `scripts/**`. Без второго
шаблона PR, который правит сам скрипт проверки, не запускал бы её вовсе — фильтр смотрел
только на `kubernetes/**`, и новая джоба пропускалась (`changed-files == []`). Джоба `Flate`
остаётся на прежнем наборе, поэтому правка скриптов её не будит.
ci(flate): проверка значений на shell и helm, без питона
All checks were successful
Flate / Flate - Filter (pull_request) Successful in 5s
Flate / Flate (pull_request) Has been skipped
Flate / Values - Schema (pull_request) Successful in 48s
Flate / Flate - Success (pull_request) Successful in 3s
143339aebe
## Что было

Проверку схемы values в этом PR сначала вёз python-скрипт (PyYAML + jsonschema в venv).
Питон в CI не приветствуется, поэтому проверка переписана на shell: значения спрашивает
`helm`, он в образе задачи; поля из HelmRelease достаёт `yq`, который ставится экшеном
`dcarbone/install-yq-action` (версия та же, что в `.mise/config.toml`). Питон-скрипт удалён,
заодно ушли venv и pip.

Заодно выяснилось, что `flate` этот класс не ловит: прогон на v0.6.5 при выключенных кэшах
рендера пропускает лишний ключ `optional: true` внутри `valueFrom.secretKeyRef` (ровно класс
issue #72) — ключ доезжает до рендера при exit 0; значение вне enum (`strategy: NotAStrategy`)
тоже проходит. Поэтому гейт и держится на helm: в helm-controller та же схема и тот же текст
отказа.

## Что правит PR

- `scripts/check-helmrelease-values.py` удалён, вместо него `scripts/check-helmrelease-values.sh`:
  обход дерева, поиск источника чарта по `chartRef` (OCIRepository -> url и тег), один pull чарта
  на прогон, `yq` вырезает `spec.values`, `helm template` валидирует значения схемой чарта.
  Источник уточняется по namespace и по расположению в дереве: `app-template` объявлен один раз
  в `flux-system`, при этом собственные источники приложений повторяются по именам между
  namespace (`hindsight` в ai и в grachevko). Плейсхолдеры Flux (`${VAR}` и `$VAR`) заменяются
  заглушкой до helm — Flux подставляет их раньше, поэтому без этой замены схема видит
  `git.${SECRET_DOMAIN}` вместо имени хоста. Непроверяемые релизы (чарт из git, чарт без
  `values.schema.json`) печатаются строками `skip` и причиной.
- `.github/workflows/flate.yaml` — джоба `Values - Schema` теперь состоит из чекаута, установки
  `yq` экшеном и запуска скрипта. Место выбрано не случайно: мёрж держит агрегатор
  `Flate - Success`, поэтому джоба в этом workflow блокирует мёрж, тогда как чек отдельного
  workflow — нет.
- Фильтр получил второй выход: проверка значений поднимается на `kubernetes/**` и `scripts/**`,
  то есть правка самого скрипта тоже прогоняет проверку. Джоба `Flate` осталась на `kubernetes/**`.
- `AGENTS.md` — команда локального прогона, обновлённый список контекстов CI и число прогонов
  flate (117 было от прошлой ревизии репозитория, сейчас 154).

## Проверено

- Полный прогон на репозитории: `26` релизов прошли схему, `0` ошибок, `14` пропущено
  (11 чартов схему не объявляют, `harbor` и `vault` — чарт из git), код возврата `0`.
  Числа те же, что давал питон-скрипт до переписывания.
- Гейт доказан негативно: при подложенном в `tandoor` ключе `optional: true` прогон даёт код 1
  и текст helm `values don't meet the specifications of the schema(s) in the following chart(s):
  app-template - at '/controllers/tandoor/containers/app/env/SOCIALACCOUNT_PROVIDERS': additional
  properties 'valueFrom' not allowed`; без этого ключа — код 0.
- `flate test all -p ./kubernetes/flux/cluster` — `154 passed`.
- `zizmor --offline .github/workflows/flate.yaml` — findings нет.
- Чекер неоднозначных символов Forgejo — чисто по обеим локалям (`_default`, `ru`).

## После мёржа

- Правок вне git нет: ни Vault, ни кластера, ни DNS.
- Защита ветки: если контексты перечислены поимённо, добавить `Flate / Values - Schema`.
  Агрегатор `Flate - Success` блокирует мёрж и без этой правки.

## Откат

Revert коммита: джоба исчезает, workflow возвращается к прежнему виду, скрипт остаётся
неиспользуемым.
hermes closed this pull request 2026-09-24 16:52:17 +00:00
Some checks are pending
Flate / Flate - Filter (pull_request) Successful in 5s
Flate / Flate (pull_request) Has been skipped
Flate / Values - Schema (pull_request) Successful in 48s
Flate / Flate - Success (pull_request) Successful in 3s
Required
Details
LLM Review / LLM Review - Success (pull_request)
Required

Pull request closed

Sign in to join this conversation.
No reviewers
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
grachevko/home-ops!204
No description provided.