IMPL 0004: Helm Chart for repo-guardian
Status: Completed Author: Donald Gifford Date: 2026-03-14
Objective
Replace the Kustomize-based deployment with a Helm chart that packages all repo-guardian Kubernetes resources, supports dev/prod/Tailscale scenarios via values, and integrates chart testing and release tooling.
Implements: DESIGN-0005
Scope
In Scope
- Helm chart with all templates (deployment, service, configmap, secret, etc.)
- Tailscale sidecar toggle via values
- helm-unittest test suite
- Makefile targets for helm operations
- CI workflows for chart linting, testing, and release
- OCI registry push support
- helm-docs generated README
Out of Scope
- Removing Kustomize overlays (future work after validation)
- Helm Operator / Flux HelmRelease CRDs
- Third-party subcharts (Prometheus, Grafana)
Implementation Phases
Each phase builds on the previous one. A phase is complete when all its tasks are checked off and its success criteria are met.
Phase 1: Chart Scaffold and Core Templates
Create the chart directory structure, Chart.yaml, values.yaml, and the core Kubernetes resource templates.
Tasks
- [x] Create
charts/repo-guardian/Chart.yaml(apiVersion v2, type application) - [x] Create
charts/repo-guardian/.helmignore - [x] Create
charts/repo-guardian/values.yamlwith helm-docs annotations covering all sections: replicaCount,image,imagePullSecrets,nameOverride,fullnameOverrideserviceAccount(create, annotations, name)podAnnotations,podLabels,podSecurityContext,securityContextservice(type, httpPort, metricsPort)resources,livenessProbe,readinessProbeconfig(appId, org, port, metricsPort, logLevel, dryRun, workerCount, queueSize, scheduleInterval, skipForks, skipArchived)secrets(create, existingSecret, webhookSecret, privateKey, privateKeyAsFile)templates(codeowners, dependabot, renovate)webhookIPAllowlist(enabled, failOpen, trustProxyHeaders)nodeSelector,tolerations,affinityextraEnv,extraVolumes,extraVolumeMounts- [x] Create
charts/repo-guardian/templates/_helpers.tpl(name, fullname, labels, selectorLabels, serviceAccountName, secretName) - [x] Create
charts/repo-guardian/templates/deployment.yaml(app container, all config/secrets env vars, webhookIPAllowlist env vars, volume mounts, probes, nodeSelector, tolerations, affinity, extraEnv, extraVolumes, extraVolumeMounts) - [x] Create
charts/repo-guardian/templates/service.yaml(ClusterIP, http + metrics ports) - [x] Create
charts/repo-guardian/templates/serviceaccount.yaml(conditional onserviceAccount.create) - [x] Create
charts/repo-guardian/templates/configmap.yaml(template file overrides for CODEOWNERS, dependabot, renovate) - [x] Create
charts/repo-guardian/templates/secret.yaml(conditional onsecrets.create, existingSecret reference when create=false, file mount and env var modes for private key) - [x] Create
charts/repo-guardian/templates/NOTES.txt(post-install instructions) - [x] Verify
helm lint charts/repo-guardianpasses - [x] Verify
helm template charts/repo-guardianrenders correctly
Success Criteria
helm lint charts/repo-guardianpasses with no errorshelm template repo-guardian charts/repo-guardianrenders all resources- Default values match current Kustomize base configuration
- Private key file mount and env var modes both render correctly
Phase 2: Tailscale Sidecar and ServiceMonitor
Add optional Tailscale Funnel sidecar injection and Prometheus ServiceMonitor.
Tasks
- [x] Add Tailscale sidecar container to
deployment.yaml(conditional ontailscale.enabled, usingtailscale.image,tailscale.hostname,tailscale.userspace,tailscale.authKeySecret) - [x] Create
charts/repo-guardian/templates/tailscale-configmap.yamlwith Funnel serve config (proxy 443 -> 127.0.0.1:8080, conditional ontailscale.enabled) - [x] Add Tailscale state emptyDir volume and serve-config volume mount
- [x] Create
charts/repo-guardian/templates/tailscale-rbac.yamlwith Role + RoleBinding (conditional ontailscale.enabledandtailscale.rbac.create) - [x] Set
TRUST_PROXY_HEADERS=trueandWEBHOOK_IP_ALLOWLIST_FAIL_OPEN=trueautomatically when Tailscale is enabled - [x] Create
charts/repo-guardian/templates/servicemonitor.yaml(conditional onserviceMonitor.enabled, withserviceMonitor.intervalandserviceMonitor.labels) - [x] Verify Tailscale sidecar renders with
helm template --set tailscale.enabled=true - [x] Verify ServiceMonitor renders with
helm template --set serviceMonitor.enabled=true
Success Criteria
- Tailscale sidecar only appears when
tailscale.enabled=true - RBAC resources only appear when
tailscale.rbac.create=true - ServiceMonitor only appears when
serviceMonitor.enabled=true helm lintstill passes with Tailscale and ServiceMonitor enabled
Phase 3: helm-unittest Test Suite
Write unit tests for all templates to validate rendering logic.
Tasks
- [x] Create
charts/repo-guardian/tests/deployment_test.yaml(replica count, image tag, env vars, volume mounts, Tailscale sidecar, probes) - [x] Create
charts/repo-guardian/tests/service_test.yaml(port mappings, service type) - [x] Create
charts/repo-guardian/tests/configmap_test.yaml(template content, custom overrides) - [x] Create
charts/repo-guardian/tests/secret_test.yaml(create vs existing, file mount vs env var) - [x] Create
charts/repo-guardian/tests/serviceaccount_test.yaml(conditional creation, annotations) - [x] Create
charts/repo-guardian/tests/servicemonitor_test.yaml(conditional creation, interval, labels) - [x] Run
helm unittest charts/repo-guardianand verify all tests pass
Success Criteria
- All helm-unittest tests pass
- Tests cover conditional rendering (enabled/disabled toggles)
- Tests cover both private key modes (file mount and env var)
- Tests cover Tailscale sidecar injection
Phase 4: CI Values and chart-testing Config
Set up chart-testing configuration and CI values for kind cluster installs.
Tasks
- [x] Create
charts/repo-guardian/ci/ci-values.yaml(busybox image, disabled probes, test secrets) - [x] Create
ct.yaml(chart-dirs, target-branch, lint-conf) - [x] Create
charts/.yamllint.yml(chart-specific yamllint config) - [x] Verify
ct lint --config ct.yaml --allpasses
Success Criteria
ct lint --config ct.yaml --allpassesci-values.yamlallows chart installation without real GitHub credentials
Phase 5: Makefile Targets
Add helm-related targets to the existing single Makefile (per DESIGN-0005 Q4 decision — no modular split).
Tasks
- [x] Add
helm-linttarget - [x] Add
helm-templatetarget (default values) - [x] Add
helm-template-citarget (CI values) - [x] Add
helm-packagetarget - [x] Add
helm-unittesttarget - [x] Add
helm-testtarget (lint + unittest) - [x] Add
helm-ct-linttarget - [x] Add
helm-ct-list-changedtarget - [x] Add
helm-ct-installtarget - [x] Add
helm-docstarget - [x] Add
helm-diff-checktarget - [x] Add
helm-cr-packagetarget - [x] Add
helm-pushtarget (OCI registry) - [x] Verify all targets work locally
Success Criteria
- All Makefile targets execute without errors
make helm-testruns lint and unittest end-to-end
Phase 6: helm-docs README Generation
Generate the chart README automatically from values annotations.
Tasks
- [x] Create
charts/repo-guardian/README.md.gotmpl(optional, for custom README structure) - [x] Run
helm-docsand verify generated README - [x] Verify all values are documented in the generated table
Success Criteria
helm-docsgenerates a complete README with all values documented- README includes chart description, usage instructions, and values table
Phase 7: CI Workflows
Add GitHub Actions workflows for chart linting, testing, and release.
Tasks
- [x] Add helm-unittest job to
.github/workflows/ci.yml - [x] Add helm chart-testing job to
.github/workflows/ci.yml(ct lint + ct install) - [x] Add helm lint step to existing lint job in CI
- [x] Create
.github/workflows/chart-release.yml(chart-releaser to GitHub Pages) - [x] Add OCI registry push job to chart-release workflow (conditional on
HELM_OCI_REGISTRYvar)
Success Criteria
- CI runs helm-unittest on every push
- CI runs ct lint on chart changes
- Chart release workflow publishes to GitHub Pages
- OCI push job is skipped when
HELM_OCI_REGISTRYis not set
Phase 8: Documentation and Cleanup
Update project documentation and verify everything is consistent.
Tasks
- [x] Update
README.mdwith Helm deployment instructions - [x] Mark
deploy/Kustomize overlays as deprecated in docs - [x] Update
CLAUDE.mdwith Helm chart architecture notes - [x] Verify
make cistill passes (existing Go CI unaffected) - [x] Verify
make helm-testpasses
Success Criteria
- README includes Helm install/upgrade instructions
- Kustomize deprecation is documented
- All CI checks pass (Go + Helm)
Open Questions
All resolved.
-
~~Schedule interval in values.yaml~~: Resolved. First-class field. Add
config.scheduleIntervaltovalues.yaml(default168h). It's one of the most common per-environment overrides. -
~~Tailscale state persistence~~: Resolved. Keep
emptyDironly for now. DeferkubeSecret-backed persistent state to a future iteration. -
~~Extra env vars and volumes~~: Resolved. Yes, include
extraEnv,extraVolumes, andextraVolumeMountsescape hatches. Easier to add now than retrofit later. -
~~Namespace~~: Resolved. Follow Helm convention. No default namespace in values or templates. Users pass
--namespaceat install time. -
~~
appVersiontracking~~: Resolved. Manual bump. Chart version and appVersion are independent release artifacts. BumpappVersioninChart.yamlwhen referencing a new app release.
File Changes
| File | Action | Description |
|---|---|---|
charts/repo-guardian/Chart.yaml |
Create | Chart metadata |
charts/repo-guardian/.helmignore |
Create | Helm ignore patterns |
charts/repo-guardian/values.yaml |
Create | Default values with helm-docs annotations |
charts/repo-guardian/templates/_helpers.tpl |
Create | Template helpers |
charts/repo-guardian/templates/deployment.yaml |
Create | Deployment with optional Tailscale sidecar |
charts/repo-guardian/templates/service.yaml |
Create | ClusterIP service |
charts/repo-guardian/templates/serviceaccount.yaml |
Create | Optional ServiceAccount |
charts/repo-guardian/templates/configmap.yaml |
Create | Template file overrides |
charts/repo-guardian/templates/secret.yaml |
Create | GitHub App credentials |
charts/repo-guardian/templates/servicemonitor.yaml |
Create | Optional Prometheus ServiceMonitor |
charts/repo-guardian/templates/NOTES.txt |
Create | Post-install notes |
charts/repo-guardian/tests/*.yaml |
Create | helm-unittest test cases |
charts/repo-guardian/ci/ci-values.yaml |
Create | CI install values |
ct.yaml |
Create | chart-testing config |
charts/.yamllint.yml |
Create | Chart yamllint config |
Makefile |
Modify | Add helm targets |
.github/workflows/ci.yml |
Modify | Add helm-unittest and ct jobs |
.github/workflows/chart-release.yml |
Create | Chart release workflow |
README.md |
Modify | Add Helm deployment docs |
CLAUDE.md |
Modify | Add Helm chart notes |
Testing Plan
- [x] helm-unittest for all templates (conditional rendering, values overrides)
- [x]
ct lintfor YAML and Chart.yaml validation - [ ]
ct installin kind cluster with CI values (requires kind cluster) - [x]
helm templatewith default and custom values - [ ]
helm difffor upgrade preview validation (requires deployed release)
Dependencies
- Helm 3.19+ (managed via mise)
- helm-unittest plugin
- helm-cr, helm-ct, helm-diff, helm-docs (managed via mise)
- GitHub Pages branch (
gh-pages) for chart-releaser - ECR repository for OCI push (optional, production only)