Skip to content

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

./new-app.sh <name> [namespace]

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 commented values.yaml, and templates/;
  • clusters/<cluster>/applications/templates/<name>.yaml — the Application CR, 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 build on 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.