Repository tooling¶
Each GitOps repository carries three small helper scripts at its root. They turn the repetitive parts of working on the charts into one command each, and — importantly — they run locally, with no CI dependency, so you can prove a change is sound before you ever push it.
The three cover the natural order of a change: scaffold a new application, lint what you wrote, and diff what it will actually do to the cluster.
./new-app.sh # scaffold a new application (chart + Argo CD Application CR)
./validate.sh # lint every chart (does it parse and lint?)
./helm-diff.sh # render changed charts and show the rendered-Kubernetes diff (what changes?)
validate.sh and helm-diff.sh are complementary: the first proves a chart lints, the second
proves you know what will actually change on the cluster when Argo CD syncs it. Run both before
opening a pull request.
new-app.sh — scaffold a new application¶
Adding an application is otherwise a multi-file ritual — a chart directory plus an Argo CD
Application custom resource under clusters/<cluster>/applications/templates/ — and it is easy to
get a path or a name subtly wrong. new-app.sh creates both from the repo's existing pattern:
applications/<name>/—Chart.yaml, a commentedvalues.yaml, andtemplates/;clusters/<cluster>/applications/templates/<name>.yaml— theApplicationCR, wired to the cluster globals like every other app.
<name> must be a lowercase DNS-1123 label; the namespace defaults to <name>. The script refuses
to overwrite anything that already exists, and prints the next steps — fill in the chart, then lint
and diff it.
Namespaces are not auto-created
Applications in these repos deliberately do not carry CreateNamespace=true. The
destination namespace must be registered by the platform namespaces application first, or the
sync will fail. new-app.sh reminds you of this in its output.
validate.sh — lint every chart¶
./validate.sh # yamllint + helm lint, all charts
./validate.sh --kubelinter # + KubeLinter
./validate.sh --kubeconform # + Kubeconform
./validate.sh applications/<app>/Chart.yaml # a single chart
validate.sh discovers every Chart.yaml in the repo and validates each one individually with
yamllint and helm lint, with opt-in passes for KubeLinter and Kubeconform. It is the fast,
first-line check that a change is well-formed.
helm-diff.sh — see what will actually change¶
./helm-diff.sh [BASE_REF] # BASE_REF defaults to origin/main
./helm-diff.sh --check # gate mode: exit non-zero if any chart shows a diff
For every applications/<app>/ chart that changed on your branch, helm-diff.sh renders the chart
both at the merge-base and in your working tree, then shows the difference between the two
rendered manifests. That is the question that matters at review time — not "did the template
change?" but "what Kubernetes objects change, and how?".
It uses dyff for a semantic YAML diff when available, and falls
back to git diff when it is not. A few behaviours are worth knowing:
- It runs
helm dependency buildon both sides, because chart dependencies are fetched at sync time and are not committed. - It guards against the empty-render false pass: a chart that renders nothing (for example because a value file was missing) would otherwise look like "no change". The script asserts non-empty output on both sides before trusting a clean diff.
- The helmfile-rendered application (the openDesk wrapper) is skipped with a pointer — diff that
one separately with
helmfile template, old versus new.
The values-lint.yaml convention¶
Both validate.sh and helm-diff.sh render each chart standalone, outside the app-of-apps that
normally injects the cluster-wide globals. A chart whose templates read .Values.global.* therefore
needs a small dummy stub, values-lint.yaml, sitting next to its values.yaml, so it renders on its
own. If you add a chart that references globals, add that stub — otherwise the render fails with a
missing-value error. new-app.sh notes this in its scaffolded values.yaml.
Requirements¶
helm and yq are required; dyff is optional but recommended (semantic diffs are far easier to
read than line diffs). The scripts are plain Bash and self-contained — pass --help to any of them
for the inline usage.