Validation
“Git is the source of truth” only holds if what merges to main is actually
what Flux is about to apply. ./scripts/validate-manifests.sh is what keeps
that claim honest: it renders every Kustomize overlay and every HelmRelease
the same way Flux does, then gates the rendered result — not the source
tree — before a PR can merge. See CI Workflows
for the full pipeline this runs in and Commands
for how to run it locally.
Why render before validating
A raw HelmRelease or a Kustomize patch fragment is not a complete
Kubernetes object — it’s an input to one. Validating the source tree checks
almost nothing, because almost nothing in the source tree is a finished
manifest. Rendering first, then validating, is what makes the gate check the
thing that actually reaches the cluster:
gen-catalog.sh→.schemas/— builds a JSON Schema catalog from this repository’s own Crossplane XRDs (there is no public schema forcloud.ogenki.iokinds), plus the Envoy AI Gateway CRDs rendered from the exact chart version this repository pins.render-bundle.py→.bundle/— every top-level Kustomize overlay throughkustomize buildwith Flux’spostBuildsubstitutions applied, everyHelmReleasethroughhelm templatewith its ownspec.valuesandpostRenderers, standalone manifests copied verbatim.- Two gates on
.bundle/:flux schema validate(structure andx-kubernetes-validationsCEL rules), thenpolaris audit(workload best practices — privilege escalation, capabilities, resource limits, image tags).
The two properties that make this gate real
skipMissingSchemas: false. Verified directly in .fluxschema.yml:
validate:
schemaLocation: [ ./.schemas, ..., default, ecosystem ]
skipMissingSchemas: falseAn unknown Kind fails the build instead of being silently skipped. The
setup this replaced ran kubeconform with -ignore-missing-schemas, so any
cloud.ogenki.io claim — App, SQLInstance, EPI, every Crossplane
composition this platform ships — went unvalidated for the life of the
repository, and the CI run still reported green. Skipped: 0 is part of
what a passing run means here, not incidental detail.
Polaris audits the rendered bundle, not the source tree. Re-verified on
this branch by rendering the repository and counting both sides directly:
the tree has one raw Deployment manifest
(tooling/base/dagger-engine/deployment.yaml); the rendered bundle has
~69 controllers — 47 Deployment, 11 Job, 5 StatefulSet, 3
DaemonSet, 3 CronJob — everything else arrives as a HelmRelease and
only becomes a controller after helm template runs. A best-practices audit
pointed at the source tree would check one workload; pointed at the
rendered bundle, it checks what’s actually scheduled.
Requirements
flux ≥ 2.9 with the schema plugin (mise install && flux plugin install schema)
and Polaris 8.5.0 — preflight.sh hard-fails on a too-old client or a
missing plugin rather than silently falling back to whatever binary happens
to be first on PATH.