Some Helm charts never finish deploying, and your GitOps engine will not tell you which.
idem is short for idempotent, and that is the whole test: it checks whether
your Helm charts are idempotent under the GitOps engine you run, by rendering a chart more than
once, comparing the results, and naming the objects that will never settle under ArgoCD, under
Flux, or under plain Helm, with the config that stops it.
$ idem ./examples/churning-chart
examples/churning-chart/templates/main.yaml
Secret/churning-chart-secret .data.password silent, no checksum
argocd CHURNS on every re-render, at least daily, and without cluster access
flux CHURNS on every chart or values change
helm CHURNS on every `helm upgrade`
No `lookup` anywhere in this chart, so nothing can stabilise this value.
That is a chart defect rather than an ArgoCD limitation. Worth reporting
upstream, and pinning the value meanwhile.
1 of 1 chart will churn under ArgoCD.
helm 4.2.4 · 3 rounds
Add to your ArgoCD Application to stop the churn:
spec:
ignoreDifferences:
- kind: Secret
name: churning-chart-secret
jsonPointers: [/data/password]
syncPolicy:
syncOptions: [RespectIgnoreDifferences=true]The report has three parts. What differs, where silent, no checksum means nothing will
restart and nothing will alert. What your engine does about it, which comes out as three
different answers because the three engines render under different conditions. And a block you
can paste. Flux gets spec.driftDetection.ignore instead, on different paths, because the two
engines evaluate them against different shapes.
This is an extremely common idiom, and it is the one that motivated idem:
password: {{ .Values.auth.password
| default (lookup "v1" "Secret" .Release.Namespace "creds").data.password
| default (randAlphaNum 32) }}
Under helm install, lookup finds the existing Secret and the password is stable. Under
ArgoCD, lookup returns {}, because the repo-server has no cluster access, so the third
branch fires and a new password is generated every time the manifests are rendered again.
What it costs depends on what else references that Secret. A workload with a checksum/
annotation rolls its pods on every sync, which is at least visible. A workload without one keeps
running while its Secret drifts away from the password the database still expects, and
nothing restarts and nothing alerts.
argocd app diff will not catch it either. From its own docs:
Kubernetes Secrets are ignored from this diff.
The objects where this bug lives are the ones ArgoCD will not show you.
brew install pcanilho/tap/idemOr grab a binary from the latest release, or build it yourself with Go 1.27 or newer:
go install github.com/pcanilho/idem@latestidem shells out to whatever helm is on your PATH, the same way ArgoCD's repo-server does.
idem doctor and --context additionally need kubectl. Nothing else.
idem ./chartsPoint it at a directory and it finds every chart under it. A chart with nothing wrong costs two lines:
$ idem ./examples/stable-chart
✓ stable-chart renders consistently under ArgoCD.
helm 4.2.4 · 3 roundsMost runs look like that, which is why findings never fail a run on their own. --strict is
what turns them into a non-zero exit, and it is opt-in for exactly that reason.
It also takes a single chart, or one you have not adopted yet. The ./examples/... charts ship
in this repository, so clone it to run them exactly as shown:
idem ./charts/my-app # one chart
idem myapp --repo https://charts.example.com
idem oci://registry.example.com/charts/myappA step in your workflow, with --strict so a finding fails the build:
# .github/workflows/charts.yml
- uses: pcanilho/[email protected]
with:
args: ./charts --strictFindings arrive as annotations on the run, and inline in Files Changed wherever idem can
place a line, because the action defaults to -o github. That needs no token and no extra
permissions. An observed finding names the file but not a line — idem will not guess one — so
GitHub shows it inline only when the file's first line is in the diff, and in the Checks tab
otherwise.
The same check before the commit is made, in .pre-commit-config.yaml:
repos:
- repo: https://github.com/pcanilho/idem
rev: v0.2.0
hooks:
- id: idemIt fires only when a staged file changes a render: Chart.yaml, any values*.yaml, or anything
under templates/.
--new-from-merge-base main reports only what your branch changed, so the pipeline is green from
day one. docs/ci.md has the rest of both.
| Code | Meaning |
|---|---|
0 |
Ran fine. Findings are printed but not fatal. |
1 |
Findings, or a release idem could not build, and you passed --strict. |
2 |
A chart could not be rendered, or idem itself failed. Always fatal. |
docs/usage.md: every flag, the output formats, what each engine does, andidem diff.docs/ci.md: the GitHub action, pull-request annotations and comments, the--new-from-merge-baseratchet, and the pre-commit hook.docs/gitops.md: whatidemreads from your Application or HelmRelease, andidem doctorfor churn that has already happened.docs/limits.md: what it cannot see, and how it compares to other tools.docs/design.md: the reasoning, the evidence, and the placesidemcan itself be wrong.- CONTRIBUTING.md: how the codebase works. Bug reports are most useful with a
chart that reproduces the problem; something the size of
examples/churning-chartbeats any description. - SECURITY.md: what
idemdoes and does not touch.
Apache 2.0. See LICENSE.