ai-superpower/.ai/instructions/skills/helm.instructions.md
moilanik a85c7dc0fb feat: add clean-code, clean-architecture and helm skills; fix .gitignore
New skill files:
- clean-code.instructions.md — naming, functions, classes, error handling,
  formatting, DRY/KISS/SOLID, Helm YAML conventions
- clean-architecture.instructions.md — dependency rule, layers, boundaries,
  SOLID foundation, Helm as outermost layer
- helm.instructions.md — resource ownership, values hygiene, required vs
  defaults, umbrella chart pattern, two-file values layering, KISS principle,
  hook ordering, config files pattern, dependency caching, template testing

Register all three in ai-root-instructions.md skills list and routing table.

Remove .ai from .gitignore — .ai/ is the product in this repo and must be
tracked; the apply.sh skip-by-marker mechanism prevents changes to other repos.
2026-03-04 09:54:07 +02:00

219 lines
6.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Helm Chart Development Guidelines
## 🔍 Before Creating Anything New
**ALWAYS search the workspace for existing implementations first.**
Before writing a new template, config, or resource:
1. Search the workspace for similar existing files
2. If found, migrate/adapt it — don't invent a new structure
3. Only create from scratch if nothing exists
---
## 🏠 Resource Ownership
**Every Kubernetes resource must have exactly one owner. Never duplicate.**
- If a chart creates a resource (e.g. Traefik creates IngressClass), don't create it elsewhere
- Before adding a template for a resource, verify no other chart/subchart already manages it
```
❌ Wrong: subchart A creates IngressClass AND subchart B creates IngressClass
✅ Right: one chart owns IngressClass, others do not touch it
```
---
## 🧹 values.yaml Hygiene
**values.yaml must only contain values that templates actually use.**
Before adding a value:
- Verify at least one template references `.Values.<key>`
- If no template uses it → don't add it
- Empty string defaults (`key: ""`) that only serve as documentation are forbidden — use README instead
**Umbrella chart values.yaml:**
- Contains only subchart defaults that override upstream chart defaults
- All environment-specific values belong in the deployment repo
- Must NOT contain empty placeholders
**Subchart values.yaml:**
- Contains only values the subchart's own templates reference
- Required values (validated with `required`) must NOT have empty defaults — absence should trigger the error
---
## ✅ required vs defaults
Use `required` for values that MUST come from the deployer. Do NOT put empty defaults in values.yaml for these.
```yaml
# Good: fails loudly at install if not provided
name: {{ required "ingress.className is required" .Values.ingress.className }}
# Bad: silently passes empty string, fails later in obscure way
name: {{ .Values.ingress.className | default "" }}
```
---
## 📦 Subchart Dependency Conditions
Use `condition:` in `Chart.yaml` to enable/disable components. Use flat booleans, not nested `.enabled`:
```yaml
# Chart.yaml
dependencies:
- name: keycloak
repository: "file://../components/keycloak"
version: "0.2.0"
condition: components.keycloak
# values (Good)
components:
keycloak: true
traefik: false
# values (Avoid)
keycloak:
enabled: true
```
---
## ⏱️ Hook Ordering
Resources that depend on other resources must use `helm.sh/hook` with `hook-weight` to ensure correct install order.
Established ordering convention:
- `hook-weight: "10"` — infrastructure prerequisites (e.g. ClusterIssuer, IngressClass)
- `hook-weight: "15"` — resources that depend on prerequisites (e.g. Certificate)
```yaml
metadata:
annotations:
"helm.sh/hook": post-install,post-upgrade
"helm.sh/hook-weight": "10"
```
---
## 📄 Configuration Files over values.yaml
**Prefer `files/` + ConfigMap over defining configuration in values.yaml.**
When a component needs configuration (CASC, realm config, ini files, etc.):
1. Put the config file in `files/` directory
2. Create a ConfigMap that reads it with `.AsConfig` + `tpl`
3. Use `{{ .Values.global.xxx }}` expressions inside the config file for dynamic values
```yaml
# templates/configmap.yaml
kind: ConfigMap
apiVersion: v1
metadata:
name: my-component-config
data: {{ (tpl (.Files.Glob "files/*").AsConfig . ) | nindent 2 }}
```
```yaml
# files/config.yaml — contains Helm template expressions
server:
url: https://{{ .Values.global.domain }}
env: {{ .Values.global.environment }}
```
**Why:** Config files are readable, diffable, and version-controlled as real files.
Large configs in values.yaml become unmaintainable and hard to review.
**When `.AsConfig` + `tpl` is required:**
- Config file contains `{{ }}` template expressions → always use `.AsConfig` + `tpl`
- Config file contains special characters (`*`, `{`, `}`) → `.AsConfig` handles escaping safely
```yaml
# Never use plain Files.Get on files with template expressions or special chars:
{{ tpl (.Files.Get "files/config.json") . }} # ❌ breaks on * characters
# Always use AsConfig:
{{ tpl (.Files.Glob "files/config.json").AsConfig . }} # ✅
```
---
## 🔄 Dependency Caching
After editing any subchart template or values, cached charts in `charts/` will be stale:
```bash
rm -rf charts/ Chart.lock
helm dependency update .
```
Always rebuild before `helm template` testing when subchart files have changed.
---
## 🎂 Umbrella Chart Pattern
The standard structure is an umbrella chart that integrates multiple component charts.
```
my-umbrella/
├── Chart.yaml ← declares component charts as dependencies
├── values.yaml ← static baseline: shared across all installations
├── templates/ ← only cross-cutting resources that no subchart owns
└── charts/ ← built by helm dependency update
```
**Two-file values layering:**
| File | Where it lives | Purpose |
|---|---|---|
| `values.yaml` | umbrella chart repo | static baseline, same across all IAC installations |
| `values.<installation>.yaml` | IAC / deployment repo | installation-specific overrides and required parameters |
- `values.yaml` contains defaults valid for every installation; it never contains installation-specific data
- `values.<installation>.yaml` provides what that specific installation requires — credentials, hostnames, sizing, feature flags
- Required values (no sensible default) use `required` in templates and have **no entry** in `values.yaml` — their absence triggers a clear error at install time
---
## ⚡ KISS — Start Minimal, Add Only When Asked
**Do not add knobs, flags, or options that arent needed right now.**
- Build the minimum that meets the stated functional requirement
- Do not anticipate future needs with extra values, conditions, or template logic
- Complexity is easy to add; it is hard to remove once deployed
- AI may **suggest** what might be needed later — but must not implement it without explicit instruction
```yaml
# ❌ Wrong: adding a flag “just in case”
features:
enableFutureThing: false # not needed yet, adding anyway
# ✅ Right: dont add it. If its needed, the developer will ask.
```
**When in doubt, leave it out.** A value that exists but is never used is noise. A template condition that is never toggled is dead code. Raise the question with the developer instead of silently implementing it.
---
## 🧪 Testing Templates
Standard verification command:
```bash
helm template <release-name> . \
--set global.key=value \
... \
2>&1 | grep "^kind:" | sort | uniq -c
```
Verify:
- Expected resource kinds appear
- No `Error:` lines
- `required` validation fires when values are missing