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.
This commit is contained in:
parent
172bfa78e9
commit
a85c7dc0fb
@ -1,6 +1,6 @@
|
|||||||
# AI Assistant Guidelines
|
# AI Assistant Guidelines
|
||||||
|
|
||||||
**Updated**: 2026-03-02
|
**Updated**: 2026-03-04
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -57,6 +57,10 @@ This main file references specialized instruction files. **Load relevant files b
|
|||||||
- [Mermaid](instructions/skills/mermaid.instructions.md) - Diagram types, color contrast rules, sizing
|
- [Mermaid](instructions/skills/mermaid.instructions.md) - Diagram types, color contrast rules, sizing
|
||||||
- [Analysis](instructions/skills/analysis.instructions.md) - Where to write analysis, tmp/ convention, full-pass writing allowed
|
- [Analysis](instructions/skills/analysis.instructions.md) - Where to write analysis, tmp/ convention, full-pass writing allowed
|
||||||
|
|
||||||
|
- [Helm](instructions/skills/helm.instructions.md) - Resource ownership, values hygiene, required vs defaults, dependency caching
|
||||||
|
- [Clean Code](instructions/skills/clean-code.instructions.md) - Naming, functions, classes, error handling, formatting, DRY/KISS/SOLID, Helm YAML
|
||||||
|
- [Clean Architecture](instructions/skills/clean-architecture.instructions.md) - Dependency rule, layers, boundaries, SOLID, Helm as outermost layer
|
||||||
|
|
||||||
### constraints/ — Load When Needed
|
### constraints/ — Load When Needed
|
||||||
- [Agent Capabilities](instructions/constraints/agent-capabilities.instructions.md) - AI limitations, user responsibilities, debugging workflows
|
- [Agent Capabilities](instructions/constraints/agent-capabilities.instructions.md) - AI limitations, user responsibilities, debugging workflows
|
||||||
- [Kubernetes Access](instructions/constraints/kubernetes-access.instructions.md) - kubectl/helm restrictions, port-forwarding patterns
|
- [Kubernetes Access](instructions/constraints/kubernetes-access.instructions.md) - kubectl/helm restrictions, port-forwarding patterns
|
||||||
@ -77,11 +81,15 @@ User asks for analysis/comparison → instructions/skills/analysis.instructio
|
|||||||
User asks about project → instructions/behavior/project-context.instructions.md → docs/ai-context.md
|
User asks about project → instructions/behavior/project-context.instructions.md → docs/ai-context.md
|
||||||
User needs to debug cluster → instructions/constraints/agent-capabilities.instructions.md + instructions/constraints/kubernetes-access.instructions.md
|
User needs to debug cluster → instructions/constraints/agent-capabilities.instructions.md + instructions/constraints/kubernetes-access.instructions.md
|
||||||
User reports pod issues → instructions/constraints/container-limitations.instructions.md
|
User reports pod issues → instructions/constraints/container-limitations.instructions.md
|
||||||
|
User works with Helm charts → instructions/skills/helm.instructions.md
|
||||||
|
User writes or reviews code → instructions/skills/clean-code.instructions.md
|
||||||
|
User designs a system or service → instructions/skills/clean-architecture.instructions.md
|
||||||
|
User creates Helm charts or YAML → instructions/skills/clean-code.instructions.md + instructions/skills/helm.instructions.md + instructions/skills/clean-architecture.instructions.md
|
||||||
Always active → instructions/behavior/core-principles.instructions.md
|
Always active → instructions/behavior/core-principles.instructions.md
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Last Updated**: 2026-03-02
|
**Last Updated**: 2026-03-04
|
||||||
**Maintained By**: Project Owner
|
**Maintained By**: Project Owner
|
||||||
**AI Assistants**: Follow these guidelines strictly - no exceptions
|
**AI Assistants**: Follow these guidelines strictly - no exceptions
|
||||||
|
|||||||
84
.ai/instructions/skills/clean-architecture.instructions.md
Normal file
84
.ai/instructions/skills/clean-architecture.instructions.md
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
# Clean Architecture
|
||||||
|
|
||||||
|
Based on Robert C. Martin's *Clean Architecture*. Apply these principles when designing systems, services, and infrastructure — including Helm chart structure and Kubernetes deployments.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## The Dependency Rule
|
||||||
|
|
||||||
|
**Source code dependencies point only inward.** Inner layers define interfaces; outer layers implement them.
|
||||||
|
|
||||||
|
```
|
||||||
|
[ Frameworks & Drivers ] ← outermost: UI, DB, APIs, Helm, K8s
|
||||||
|
[ Interface Adapters ] ← controllers, presenters, gateways
|
||||||
|
[ Use Cases ] ← application business rules
|
||||||
|
[ Entities ] ← enterprise business rules (innermost)
|
||||||
|
```
|
||||||
|
|
||||||
|
- Inner layers know nothing about outer layers
|
||||||
|
- Data crossing a boundary is always in the form the inner layer prefers — never a raw DB row or HTTP request object
|
||||||
|
- If an outer layer changes (swap MySQL for Postgres, REST for gRPC), the inner layers are untouched
|
||||||
|
|
||||||
|
## Layers
|
||||||
|
|
||||||
|
### Entities — Core Business Rules
|
||||||
|
- Enterprise-wide business objects with their validation and invariants
|
||||||
|
- Pure domain models: no framework imports, no DB annotations, no HTTP concepts
|
||||||
|
- Stable — change only if fundamental business rules change
|
||||||
|
|
||||||
|
### Use Cases — Application Business Rules
|
||||||
|
- Orchestrate entities to fulfil one actor's goal: `TransferMoneyUseCase`, `ProvisionClusterUseCase`
|
||||||
|
- Define input/output via interfaces owned by this layer — no knowledge of controllers or DB implementations
|
||||||
|
- One use case = one reason to exist
|
||||||
|
|
||||||
|
### Interface Adapters
|
||||||
|
- Convert data between use cases and the outside world
|
||||||
|
- Controllers translate HTTP/CLI input → use case input models
|
||||||
|
- Gateways implement repository interfaces defined by use cases
|
||||||
|
- Presenters format use case output → UI/API response format
|
||||||
|
|
||||||
|
### Frameworks & Drivers
|
||||||
|
- The outermost ring: web frameworks, ORMs, message brokers, Kubernetes, Helm
|
||||||
|
- These are details — they plug into the system via the adapters; the core doesn't know they exist
|
||||||
|
|
||||||
|
## SOLID as the Foundation
|
||||||
|
|
||||||
|
- **SRP** — one module, one actor, one reason to change
|
||||||
|
- **OCP** — extend behaviour through new implementations, not modification of existing code
|
||||||
|
- **LSP** — subtypes must honour the contracts of their interfaces
|
||||||
|
- **ISP** — prefer narrow, focused interfaces over fat ones
|
||||||
|
- **DIP** — high-level policy depends on abstractions; low-level detail implements them
|
||||||
|
|
||||||
|
## Boundaries
|
||||||
|
|
||||||
|
- Boundaries are where the power lives — they isolate components so that one side can change without affecting the other
|
||||||
|
- Define boundaries at the most volatile points: DB, UI, external services
|
||||||
|
- Cross a boundary only via an interface; never pass a concrete type across
|
||||||
|
|
||||||
|
## What This Means in Practice
|
||||||
|
|
||||||
|
- Business rules have no imports from frameworks, ORMs, or infrastructure libraries
|
||||||
|
- Tests of use cases and entities need no running database, no HTTP server, no cluster
|
||||||
|
- Swapping the database or the delivery mechanism (REST → gRPC, monolith → microservices) touches only the outer rings
|
||||||
|
- "Screaming architecture" — the top-level structure should reflect the use cases, not the framework: `invoice/`, `user/`, `payment/` not `controllers/`, `models/`, `views/`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## In Helm and Kubernetes
|
||||||
|
|
||||||
|
Clean Architecture applies to how infrastructure is organised, not just application code.
|
||||||
|
|
||||||
|
**Helm charts are the outermost layer — they are the delivery mechanism, not the system.**
|
||||||
|
|
||||||
|
- A chart should deploy exactly one service or one cohesive unit — not a bundle of unrelated things
|
||||||
|
- Business rules belong in application config (`values.yaml` keys mapped to env vars); they must not be hardcoded in templates
|
||||||
|
- Separate concerns across charts: one chart for the app, separate charts (or dependencies) for databases, queues, and shared infrastructure
|
||||||
|
- A chart change (resource limits, replica count, ingress rules) must never require touching application business logic
|
||||||
|
- Use `_helpers.tpl` as the adapter layer — it translates chart-level abstractions into Kubernetes primitives; templates themselves stay thin
|
||||||
|
- Environments (`values-dev.yaml`, `values-prod.yaml`) are driver-layer configuration — they override defaults without touching templates
|
||||||
|
- Test charts with `helm template` + schema validation; business logic is tested independently of the chart
|
||||||
|
|
||||||
|
**Dependency Rule for Helm:**
|
||||||
|
- App charts depend on library charts, not the reverse
|
||||||
|
- Library charts define interfaces (helpers, common labels); app charts implement them
|
||||||
|
- Avoid circular chart dependencies — they indicate a missing layer or a misplaced responsibility
|
||||||
78
.ai/instructions/skills/clean-code.instructions.md
Normal file
78
.ai/instructions/skills/clean-code.instructions.md
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
# Clean Code
|
||||||
|
|
||||||
|
Based on Robert C. Martin's *Clean Code*. Apply these principles to all code and configuration, including Helm charts, YAML, and infrastructure-as-code.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Naming
|
||||||
|
|
||||||
|
- Use intention-revealing names: `daysSinceLastPayment` not `d`
|
||||||
|
- Don't lie with names: `accounts` not `accountList` if it isn't a list
|
||||||
|
- Pronounceable, searchable names — avoid encodings like `a2dp`, `strName`
|
||||||
|
- One word per concept across the codebase: pick `fetch`, `get`, or `retrieve` — not all three
|
||||||
|
- Names should read like prose: `if user.isEligibleForRefund()` not `if user.check()`
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|
||||||
|
- **Do one thing** — if you can extract a meaningful function from it, it's doing two things
|
||||||
|
- **One level of abstraction per function** — don't mix high-level orchestration with low-level detail in the same function
|
||||||
|
- Keep short — aim for under 20 lines; use extract method aggressively
|
||||||
|
- 0–2 arguments preferred; use a parameter object when more are needed
|
||||||
|
- **No side effects** — a function named `checkPassword` must not also start a session
|
||||||
|
- No flag arguments: `delete(file, true)` means the function already does two things — split it
|
||||||
|
|
||||||
|
## Classes
|
||||||
|
|
||||||
|
- **Single Responsibility Principle** — one reason to change
|
||||||
|
- Small and cohesive — methods should use the class's fields, not act as utilities
|
||||||
|
- Hide internals: public API simple, private implementation complex
|
||||||
|
- No god classes — prefer `InvoiceCalculator` over `BusinessLogic`
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
- Throw exceptions, don't return error codes — let callers handle what they understand
|
||||||
|
- Descriptive messages: `throw new UnauthorizedAccess("Admin role required for this action")`
|
||||||
|
- Define custom exceptions close to where they're used
|
||||||
|
- Clean up resources: use try/finally or RAII patterns; never leak
|
||||||
|
|
||||||
|
## Comments
|
||||||
|
|
||||||
|
- Good code needs no comments — if you need to explain *what*, rename instead
|
||||||
|
- Comments explain *why*, not *what*: acceptable for non-obvious decisions, legal notices
|
||||||
|
- TODOs are temporary; remove before merging
|
||||||
|
- Never commit commented-out code
|
||||||
|
|
||||||
|
## Formatting
|
||||||
|
|
||||||
|
- Related code stays close — declarations near first use
|
||||||
|
- Consistent indentation throughout the codebase — agree once, automate with linting
|
||||||
|
- No deep nesting — if you're 4 levels deep, extract a function
|
||||||
|
- Team standard beats personal preference every time
|
||||||
|
|
||||||
|
## General Principles
|
||||||
|
|
||||||
|
- **DRY** — every piece of knowledge has one representation; duplication is a maintenance liability
|
||||||
|
- **KISS** — the simplest solution that works; complexity must earn its place
|
||||||
|
- **Boy Scout Rule** — leave the code cleaner than you found it; small improvements compound
|
||||||
|
- **SOLID** — especially SRP (above) and Open-Closed: open for extension, closed for modification
|
||||||
|
- **Boundaries** — wrap third-party code in adapters; don't let external APIs bleed through the codebase
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
- Tests must be **FAST, Independent, Repeatable, Self-Validating**
|
||||||
|
- One concept per test — a test that asserts many things is a test that hides failures
|
||||||
|
- Keep test code as clean as production code — it will be maintained just as long
|
||||||
|
- Write code testable from day one; retrofitting testability is expensive
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## In Helm Charts and YAML
|
||||||
|
|
||||||
|
The same principles apply to configuration:
|
||||||
|
|
||||||
|
- **Naming**: value keys should be intention-revealing — `replicaCount` not `rc`, `service.port` not `p`
|
||||||
|
- **Single responsibility**: one chart does one thing; don't bundle unrelated services
|
||||||
|
- **No magic values**: named values in `values.yaml`, never hardcoded in templates
|
||||||
|
- **DRY**: use template helpers (`_helpers.tpl`) to avoid repeating label blocks and name patterns
|
||||||
|
- **Comments**: explain *why* a value is set a certain way, not what it does — the YAML speaks for itself
|
||||||
|
- **Defaults that make sense**: a default of `false` or `""` that always needs overriding is not a useful default
|
||||||
218
.ai/instructions/skills/helm.instructions.md
Normal file
218
.ai/instructions/skills/helm.instructions.md
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
# 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 aren’t 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: don’t add it. If it’s 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
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,2 @@
|
|||||||
.ai-superpower.version
|
.ai-superpower.version
|
||||||
tmp/
|
tmp/
|
||||||
.ai
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user