name: cluster-kilic-chart description: Create a new Helm chart wrapper in the cluster/charts group on GitLab. Scaffolds the chart repository with upstream dependency, values, optional custom templates (ExternalSecret, Gateway API resources, CRDs), and all standard boilerplate. Always manually invoked. Do NOT use for workload deployments (/cluster-kilic-workload), ArgoCD workloads (/argocd-kilic-workload), or LB routing (/argocd-kilic-loadbalancer). interaction: chat disable-model-invocation: true argument-hint: "[chart-name] - e.g., 'goldilocks', 'velero', 'cilium-l2-announcement'"
system
Cluster Chart Creator
IMPORTANT: This skill creates a new Helm chart repository in the
cluster/chartsgroup on GitLab (gitlab.kilic.dev).
How Charts Work in This System
Each system component deployed via ArgoCD gets a Helm chart in cluster/charts/chart-<name>. Charts are either wrappers around upstream Helm charts or standalone custom-resource charts. ArgoCD pulls from these repos to deploy components to clusters.
The chart repos are thin — they pin upstream versions, provide default values, and optionally add custom templates for resources the upstream chart doesn't cover (ExternalSecrets, Gateway API resources, CRDs). Renovate handles automated upstream version bumps. Semantic-release handles chart versioning and publishing.
Chart Types
There are two main types, with variations based on additional resources needed:
| Type | Has Dependencies | Has Templates | Example |
|---|---|---|---|
| Pure wrapper | Yes | No | chart-goldilocks, chart-prometheus-operator |
| Wrapper + ExternalSecret | Yes | Yes | chart-external-dns, chart-velero |
| Wrapper + custom resources | Yes | Yes | chart-envoy-gateway |
| Standalone custom resources | No | Yes | chart-cilium-l2-announcement |
| Standalone CR + ExternalSecret | No | Yes | chart-cert-manager-letsencrypt-dns01-cloudflare |
Gather Requirements
Ask the user:
- Component name: What is being deployed? (e.g.,
velero,goldilocks,external-dns) - Upstream chart? Is there an upstream Helm chart to wrap? If yes, what's the repository URL and chart name?
- Secrets needed? Does it need Vault secrets via ExternalSecret?
- Custom resources? Does it need additional templates beyond the upstream chart? (e.g., GatewayClass, BackupStorageLocation, CiliumL2AnnouncementPolicy)
- Initial values: What default values should be set?
Research Phase (MANDATORY)
BEFORE writing any code:
- Find the upstream chart — Use web search or Context7 to find the Helm chart repository URL, chart name, and latest version
- Read a similar existing chart — Use GitLab MCP to browse
cluster/charts/chart-<similar>for reference. Pick the closest match by type:- Pure wrapper → read
chart-goldilocks - With ExternalSecret → read
chart-external-dnsorchart-velero - With custom resources → read
chart-envoy-gateway - Standalone CRs → read
chart-cilium-l2-announcement
- Pure wrapper → read
- Check upstream values — Understand what the upstream chart exposes so you can set sensible defaults
Repository Structure
Every chart repo follows this structure:
chart-<name>/
├── .gitignore
├── .gitlab-ci.yml
├── .helmignore
├── Chart.yaml
├── Taskfile.yml
├── package.json
├── release.config.mjs
├── renovate.json
├── values.yaml
└── templates/ # only if custom templates needed
├── external-secret.yaml
└── <custom-resource>.yaml
File Templates
Chart.yaml
With upstream dependency:
apiVersion: v2
name: chart-<name>
description: <brief description>
type: application
version: 1.0.0
dependencies:
- name: <upstream-chart-name>
version: <upstream-version>
repository: <upstream-repo-url>
# alias: <name> # only if upstream name differs from desired values key
versionis always1.0.0— semantic-release manages actual versioning via git tags- Use
aliaswhen the upstream chart name is unwieldy (e.g.,gateway-helmaliased toenvoy-gateway,kube-prometheus-stackaliased toprometheus-operator) - Repository can be HTTPS (
https://...) or OCI (oci://docker.io/...)
Without upstream dependency (standalone):
apiVersion: v2
name: chart-<name>
description: <brief description>
type: application
version: 1.0.0
values.yaml
Upstream defaults reference: When writing values for an upstream chart dependency, include a comment at the top of the dependency's values block with a link to the upstream chart's default values.yaml (GitHub raw link or documentation URL). This helps future maintainers understand what options are available.
# Upstream chart defaults: <link-to-upstream-values.yaml>
<dependency-name-or-alias>:
key: value
nested:
key: value
Find the link during the Research Phase — check the chart's GitHub/GitLab repository for the values.yaml file, or the chart's documentation page listing all configurable values.
For upstream wrappers, nest values under the dependency name (or alias).
For charts with ExternalSecret, add a top-level secrets: block:
secrets:
secretStoreRef:
kind: ClusterSecretStore
name: secret.vault.int.kilic.dev
<secret-name>:
key: "" # vault path — filled per-cluster via ApplicationSet values injection
For standalone charts, top-level values drive templates directly:
name: <resource-name>
# ... resource-specific values
.gitlab-ci.yml
Always identical:
stages:
- install
- lint
- publish
include:
- project: devops/pipelines
ref: helm@1.2.1
file:
- /helm/install.gitlab-ci.yml
- /helm/lint.gitlab-ci.yml
- project: devops/pipelines
ref: semantic-release@1.6.0
file:
- /semantic-release/publish.gitlab-ci.yml
Taskfile.yml
Always identical:
# https://taskfile.dev
version: "3"
includes:
helm:
taskfile: https://gitlab.kilic.dev/libraries/taskfiles/-/raw/main/Taskfile.helm-application.yml
flatten: true
renovate.json
Always identical:
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["local>renovate/renovate-config:default/default"],
"ignorePresets": []
}
package.json
Minimal — used by semantic-release:
{
"name": "chart-<name>"
}
release.config.mjs
Always identical:
export default {
branches: ['main'],
plugins: [
'@semantic-release/commit-analyzer',
'@semantic-release/release-notes-generator',
'@semantic-release/changelog',
[
'@semantic-release/git',
{
assets: ['CHANGELOG.md']
}
],
'@semantic-release/gitlab',
[
'@semantic-release/exec',
{
publishCmd: 'echo ${nextRelease.version} >> $TAGS_FILE'
}
]
]
}
.gitignore
Always identical:
charts/
dist/
.task/
.helmignore
Standard Helm ignore file.
ExternalSecret Template Patterns
Single secret:
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: {{ $.Release.Name }}
namespace: {{ $.Release.Namespace }}
spec:
secretStoreRef:
{{- .Values.secrets.secretStoreRef | toYaml | nindent 4 }}
target:
name: {{ $.Release.Name }}
deletionPolicy: Delete
dataFrom:
- extract:
conversionStrategy: Default
decodingStrategy: None
metadataPolicy: None
key: {{ .Values.secrets.<name>.key | quote }}
Multiple secrets (loop):
{{- range .Values.secrets.items }}
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: {{ .name | default $.Release.Name }}
namespace: {{ $.Release.Namespace }}
spec:
secretStoreRef:
{{- .secretStoreRef | default $.Values.secrets.secretStoreRef | toYaml | nindent 4 }}
target:
name: {{ .targetName | default .name | default $.Release.Name }}
deletionPolicy: Delete
dataFrom:
- extract:
conversionStrategy: Default
decodingStrategy: None
metadataPolicy: None
key: {{ .key | quote }}
{{- end }}
With corresponding values.yaml:
secrets:
secretStoreRef:
kind: ClusterSecretStore
name: secret.vault.int.kilic.dev
items:
- name: <secret-name>
key: "" # vault path
All secrets reference ClusterSecretStore named secret.vault.int.kilic.dev — this is the standard Vault integration point across all clusters.
Key Principles
- Use GitLab MCP to research existing chart patterns before creating
- Use web search / Context7 to find upstream chart repos and their values schema
- Chart version is always
1.0.0— semantic-release handles actual versioning - All boilerplate files are identical across repos — copy them exactly
- Values nest under dependency name (or alias) for upstream wrappers
- Secrets always use ExternalSecret + ClusterSecretStore (
secret.vault.int.kilic.dev) - Vault paths are left empty in defaults — filled per-cluster via ApplicationSet annotations injection
- Only add templates that are needed — pure wrappers have no
templates/directory at all
Checklist
- Identify upstream chart (name, version, repository URL) or confirm standalone
- Read a similar existing chart as reference
- Create
Chart.yamlwith correct dependency (or standalone) - Create
values.yamlwith sensible defaults nested under dependency name - If ExternalSecret needed: create
templates/external-secret.yamland addsecrets:block to values - If custom resources needed: create templates with proper Helm templating
- Create all boilerplate files (
.gitlab-ci.yml,Taskfile.yml,renovate.json,package.json,release.config.mjs,.gitignore,.helmignore) - Verify values structure matches upstream chart's expected format
- Match code style from the reference chart