Maintainer automation for the helm-charts mono-repo. Five independent sub-systems collaborate through a small set of file-level contracts; nothing in here touches a live cluster.
scripts/
├─ upgrade-sync/ # canonical-body propagation for charts/*/upgrade.sh
├─ check-version/ # drift detection + auto-bump PR orchestration
├─ changelog/ # Chart.yaml annotation -> charts/*/CHANGELOG.md mirror
├─ validate/ # kubeconform skip gate + CR schema vendoring
├─ shell-guards/ # bash/zsh divergence checks that `zsh -n` cannot see
└─ lib/ # shared helpers sourced by the scripts above (NOT executed)
| Sub-system | Entry point | What it does | When it runs |
|---|---|---|---|
upgrade-sync/ |
sync.sh |
Propagates the canonical body of each per-chart upgrade.sh from a single template under templates/. Catches body drift between charts. |
On every PR that touches charts/*/upgrade.sh or templates/. Run via make sync-check. |
check-version/ |
check-version.sh |
Iterates every chart's upgrade.sh --dry-run --json, classifies drift (uptodate / drift / blocked / no-image / error), and (with --apply) opens one PR per drifted chart. |
Weekly via .github/workflows/check-versions.yml, or manual make version-check / make version-apply. |
changelog/ |
sync-changelog.sh |
Renders per-chart CHANGELOG.md from Chart.yaml's annotations.artifacthub.io/changes (Keep a Changelog format). Idempotent. |
After upgrade.sh resets the annotation; via make changelog CHART=<name> or make changelog-all. |
validate/ |
check-skips.sh |
Fails make validate when kubeconform skips a resource whose schema it could not resolve, unless the kind is declared in allowed-skips.txt. vendor-crd-schema.sh converts an upstream CRD into a repo-local schema under schemas/ so the kind validates instead of skipping. |
check-skips.sh on every make validate (and thus make ci). vendor-crd-schema.sh manually, when a new CR kind has no published schema. |
shell-guards/ |
shell-guards.sh |
Flags the three bash/zsh divergences that parse cleanly under both shells and then behave differently: read -p, a bare local NAME re-declared inside a loop, and a shell function piped into head -N. Each shipped undetected until someone ran the script under zsh. |
Last stage of make shell-lint, over scripts/**/*.sh + charts/*/upgrade.sh; on every push via shell-lint.yml. |
upstream registry ┌──────────────────────────────────┐
(Docker Hub / GHCR / Elastic API / GitHub) │ scripts/check-version/ │
│ │ for chart in charts/*: │
│ tracked by │ run upgrade.sh --dry-run │
▼ │ --json │
charts/<name>/upgrade.sh │ classify drift │
▲ │ on --apply: open one auto-PR │
│ canonical body │ per drifted chart │
│ propagated by └──────────────────────────────────┘
scripts/upgrade-sync/sync.sh │
│ │ runs as part of --apply
│ ▼
scripts/upgrade-sync/templates/ ┌──────────────────────────────────┐
├─ chart-appversion.sh │ charts/<name>/upgrade.sh: │
└─ helm-charts/external-tracked.sh │ reset Chart.yaml annotation │
│ artifacthub.io/changes │
└──────────────────────────────────┘
│
│ rendered by
▼
┌──────────────────────────────────┐
│ scripts/changelog/ │
│ prepend section to │
│ charts/<name>/CHANGELOG.md │
└──────────────────────────────────┘
| Producer | Artifact | Consumer | Documented in |
|---|---|---|---|
templates/*.sh |
# === BEGIN/END CANONICAL BODY === markers |
scripts/upgrade-sync/sync.sh |
upgrade-sync/README.md |
charts/<name>/upgrade.sh --dry-run --json |
JSON record (schema helm-charts.upgrade.dryrun.v1) |
scripts/check-version/check-version.sh |
upgrade-sync/README.md § Dry-run JSON contract |
charts/<name>/upgrade.sh |
Chart.yaml.annotations."artifacthub.io/changes" (RESET semantics) |
scripts/changelog/sync-changelog.sh |
changelog/README.md |
When changing one side of any of these contracts, change the other side in the
same PR and re-run make sync-check + make version-check to verify.
All four sub-systems are exposed through the top-level Makefile:
| Target | Wraps |
|---|---|
make sync-check |
scripts/upgrade-sync/sync.sh --check |
make sync-apply |
scripts/upgrade-sync/sync.sh --apply |
make sync-list |
scripts/upgrade-sync/sync.sh --list |
make sync-status |
scripts/upgrade-sync/sync.sh --status |
make version-check |
scripts/check-version/check-version.sh --check |
make version-apply |
scripts/check-version/check-version.sh --apply |
make changelog CHART=<name> |
scripts/changelog/sync-changelog.sh charts/<name> |
make changelog-all |
scripts/changelog/sync-changelog.sh --all |
make validate |
kubeconform per chart, piped into scripts/validate/check-skips.sh <chart>. Schema lookup order: kubeconform default store → local schemas/ → datree CRDs-catalog, cached in .kubeconform-cache/. |
make shell-lint |
bash -n + zsh -n on every scripts/**/*.sh and charts/*/upgrade.sh; shellcheck --severity=error (if installed) on scripts/**/*.sh only — charts/*/upgrade.sh is the sync output of the templates and is checked at the template level. Warning/info-level shellcheck output is shown advisory-only. STRICT=1 make shell-lint requires shellcheck. |
Shared by all four sub-systems:
bash≥ 4 (Homebrew bash on macOS) orzsh≥ 5 — scripts are written for both shells; the shebang is#!/usr/bin/env bash, so direct execution uses bash, butzsh path/to/script.shworks equivalentlypython3(used by every script for JSON / YAML manipulation)git
Additional, per sub-system:
helm≥ 3.16 (upgrade-synctemplate smoke-test,check-versionindirectly viamake ci)chart-testing+kubeconform(check-version --applyrunsmake ciper chart)gh(check-version --applywithout--no-pr)shellcheck(optional;make shell-lintruns it when available, andSTRICT=1 make shell-lintrequires it for CI)yqv4 (mikefarah/yq) — required bychangelog/sync-changelog.shfor readingannotations.artifacthub.io/changes. The other sub-systems do not depend on it; their YAML touches go through inlinepython3.PyYAML— required only byvalidate/vendor-crd-schema.sh, a manual maintainer action. The CI-facingvalidate/check-skips.shparses kubeconform JSON with the stdlib and needs nothing extra.
- upgrade-sync/README.md — canonical-body markers, available templates, the publisher↔consumer flow with
kuberntes-infra, and the--dry-run --jsonschema. - check-version/README.md — drift status codes, GitHub Actions wiring, sibling-chart ordering, and PR creation.
- changelog/README.md — Chart.yaml annotation RESET semantics and the Keep a Changelog mapping.
- validate/README.md — why
-ignore-missing-schemasneeds a gate, the schema resolution order, theallowed-skips.txtformat, and how to vendor a CRD schema.
Sourced (NOT executed) by every script above. Provides:
| Helper | Purpose |
|---|---|
log / info / warn / die |
stderr logging with consistent prefixes — keeps stdout reserved for machine-readable output |
init_tmp_cleanup + mktemp_tracked |
mktemp variants that auto-clean on EXIT, protecting against SIGTERM / set -e leaks between mktemp and the success-path mv |
require_command <cmd> [hint] |
abort with a clear message + install hint when a CLI is missing |
require_yq_v4 |
yq presence + version 4.x guard (used by sync-changelog.sh) |
Idempotent — guarded against double-sourcing. Does NOT call set -euo pipefail and does NOT install a trap unconditionally; callers decide their own strictness and opt into cleanup via init_tmp_cleanup.