Local development
Three levels of validation, cheapest first. The pipeline runs all three; doing them locally is how you avoid finding out in a PR.
1. Render and lint
helm lint --strict <chart>
helm template <chart> --set ingress.domain=example.com --set ingress.clusterIssuer=letsencrypt
Required values vary per chart, so the render usually needs more --set flags than these two — minecraft-router needs mappings, samba needs shares and a storage class, freesailarr needs media claims. The fastest way to find out is to run it: required() fails with the values path in the message.
A shortcut that keeps you honest: render with the chart's own CI values, which are a realistic input rather than the defaults.
helm template <chart> -f ci/<chart>/test-values.yaml
If the chart declares a dependency, every helm command exits 1 until charts/ is filled:
ci/helm-dependencies.sh <chart>
2. Run the repo's own checks
ci/run-quantity-render-test.sh <chart> # no argument = every chart
ci/run-trivy-config.sh <chart>
ci/run-install-test.sh <chart> # needs a cluster, see below
ci/run-install-test.sh --list-excluded # the excluded charts and their reasons
The quantity render test is worth running on any chart you touched the resources helper in — it is the one check that covers every chart on every PR, so a regression there is found by someone else's PR otherwise.
3. Runtime verification in k3d
Rendering proves the manifests are well-formed. It does not prove the workload starts, the probes pass, or the hardening is survivable — and in this repo those are the parts that break.
k3d cluster create <name>
ci/run-install-test.sh <chart>
k3d cluster delete <name>
The script does the rest: it applies the stub OnePasswordItem CRD and the chart's fixtures, installs with ci/<chart>/test-values.yaml, and waits for every Deployment and StatefulSet to roll out.
Doing it by hand is the same four steps:
kubectl apply -f ci/common/— the schema-less stub CRD forOnePasswordItemkubectl apply -f ci/<chart>/fixtures.yaml— dummy secrets, throwaway postgres/redis, hostPath PVshelm install <release> <chart> -f ci/<chart>/test-values.yaml- watch pod readiness and events, then read the container log
Note what the stub CRD does and does not do. It accepts OnePasswordItem objects so the render applies; it does not materialize any Secret. The fixtures supply the Kubernetes Secrets under the names the chart expects. A chart that works in CI therefore proves its wiring, not its 1Password paths.
What CI does that your machine does not
kubectlis pinned in the pipeline to stay within one minor of the k3s image. Locally, a client far from the API server changes what the test means — checkkubectl versionbefore trusting a surprising result.- The install-test job is containerised, which is why the workflow rewrites the kubeconfig server address. A local k3d cluster on your own Docker host needs none of that.
- Timeouts differ per chart via
ci/<chart>/settings.env(INSTALL_TIMEOUT).freesailarrneeds 1200s — ten containers, several GB of images and a real Wireguard handshake.
What cannot be verified locally
- The publish step. It uploads to the private registry; there is no dry run. The pipeline is the first place a packaging mistake shows.
- 1Password resolution. Charts render
OnePasswordItemCRs, and only a cluster with the operator turns them into Secrets. A wrongop://path renders fine and fails as a pod inCreateContainerConfigError. - Anything about the real cluster's storage. RWX, storage classes and
fsGroupbehaviour differ between k3d's local-path provisioner and the production cluster. A chart that mounts an existing claim is only half-tested by CI.
Tooling you need
| Tool | Why |
|---|---|
helm |
render, lint, package |
k3d + docker |
throwaway clusters |
kubectl |
everything after the install |
python3 |
the quantity render test reads values.schema.json |
tea |
issues and PRs — see Forge and tooling. The Coder workspace has no op, so tea is called directly and acts as the bot account |
| a working commit signer | branch protection requires signed commits; the Coder workspace signs through coder-git-ssh-sign without a 1Password step |
helm-charts wiki
How we work
How it is built
How charts are written
Charts
- apache-tika
- bentopdf
- bulwark
- calibre-web-automated
- convertx
- cyberchef
- freesailarr
- garage
- gotenberg
- homeassistant
- keycloak
- matrix-synapse
- minecraft-router
- minecraft-server
- outline
- paperless-ngx
- patchmon
- rallly
- rss-bridge
- samba
- satisfactory
- searxng
- stalwart
- sure
- teamspeak-server
- template-chart
- urlaubsverwaltung
- valkey
- voucher-vault
- ytdlp-web-player
- zipline