Helm Chart Generator Overview Generate production-ready Helm charts with deterministic scaffolding, standard helpers, reusable templates, and validation loops. Official Documentation: - Helm Docs - Main documentation - Chart Best Practices - Official best practices guide - Template Functions - Built-in functions - Sprig Functions - Extended function library When to Use This Skill | Use helm-generator | Use OTHER skill | |-------------------|-----------------| | Create new Helm charts | helm-validator : Validate/lint existing charts | | Generate Helm templates | k8s-yaml-generator : Raw K8s YA…

; then\n error_exit \"Chart name '${name}' is invalid. Must:\n - Start with a lowercase letter or number\n - Contain only lowercase letters, numbers, and hyphens\n - End with a lowercase letter or number\n - Not contain consecutive hyphens\nExamples: myapp, my-app, app1, my-cool-app\"\n fi\n\n # Check for consecutive hyphens\n if echo \"$name\" | grep -q '\\-\\-'; then\n error_exit \"Chart name '${name}' contains consecutive hyphens (not allowed)\"\n fi\n}\n\n# Function to validate port number\nvalidate_port() {\n local port=\"$1\"\n if ! [[ \"$port\" =~ ^[0-9]+$ ]] || [ \"$port\" -lt 1 ] || [ \"$port\" -gt 65535 ]; then\n error_exit \"Invalid port number '${port}'. Must be between 1 and 65535\"\n fi\n}\n\n# Function to validate image tag format\nvalidate_image_tag() {\n local tag=\"$1\"\n if [[ \"$tag\" =~ [[:space:]] ]]; then\n error_exit \"Image tag '${tag}' is invalid. Tags cannot contain spaces\"\n fi\n if [[ \"$tag\" == *\"@\"* ]]; then\n error_exit \"Image tag '${tag}' is invalid. Use --image \u003crepo>@\u003cdigest> for digest references\"\n fi\n}\n\n# Function to validate image digest format\nvalidate_image_digest() {\n local digest=\"$1\"\n local algorithm\n local hash_value\n local algorithm_lower\n\n if [[ \"$digest\" =~ [[:space:]] ]] || [[ \"$digest\" != *:* ]]; then\n error_exit \"Invalid image digest '${digest}'. Use format \u003calgorithm>:\u003chash> (e.g., sha256:\u003c64 hex>)\"\n fi\n\n algorithm=\"${digest%%:*}\"\n hash_value=\"${digest#*:}\"\n\n if [ -z \"$algorithm\" ] || [ -z \"$hash_value\" ]; then\n error_exit \"Invalid image digest '${digest}'. Algorithm and hash must both be non-empty\"\n fi\n\n algorithm_lower=\"$(printf '%s' \"$algorithm\" | tr '[:upper:]' '[:lower:]')\"\n\n case \"$algorithm_lower\" in\n sha256)\n if [[ ! \"$hash_value\" =~ ^[a-fA-F0-9]{64}$ ]]; then\n error_exit \"Invalid image digest '${digest}'. sha256 digests must be 64 hexadecimal characters\"\n fi\n ;;\n sha512)\n if [[ ! \"$hash_value\" =~ ^[a-fA-F0-9]{128}$ ]]; then\n error_exit \"Invalid image digest '${digest}'. sha512 digests must be 128 hexadecimal characters\"\n fi\n ;;\n *)\n error_exit \"Invalid image digest '${digest}'. Supported algorithms: sha256 or sha512\"\n ;;\n esac\n}\n\n# Return success when a repository reference appears to include a tag in its last segment.\nrepository_has_inline_tag() {\n local repository=\"$1\"\n local last_segment\n\n last_segment=\"${repository##*/}\"\n [[ \"$last_segment\" == *\":\"* ]]\n}\n\n# Normalize image digest algorithm to lowercase for consistent output in values.yaml.\nnormalize_image_digest() {\n local digest=\"$1\"\n local algorithm\n local hash_value\n\n algorithm=\"${digest%%:*}\"\n hash_value=\"${digest#*:}\"\n algorithm=\"$(printf '%s' \"$algorithm\" | tr '[:upper:]' '[:lower:]')\"\n\n printf '%s:%s' \"$algorithm\" \"$hash_value\"\n}\n\n# Return success when the provided image reference includes both a tag and a digest.\nimage_ref_has_tag_and_digest() {\n local image_ref=\"$1\"\n local repository_part\n\n repository_part=\"${image_ref%@*}\"\n repository_has_inline_tag \"$repository_part\"\n}\n\n# Function to validate image digest reference rules\nvalidate_digest_reference() {\n local image_ref=\"$1\"\n local digest=\"$2\"\n\n if image_ref_has_tag_and_digest \"$image_ref\"; then\n error_exit \"Image reference '${image_ref}' cannot include both tag and digest. Use either \u003crepository>:\u003ctag> or \u003crepository>@\u003cdigest>\"\n fi\n\n if [ \"$TAG_EXPLICIT\" = true ] || [ -n \"$IMAGE_TAG\" ]; then\n error_exit \"Do not combine --tag with digest image references. Remove --tag or provide an image without '@'\"\n fi\n\n validate_image_digest \"$digest\"\n}\n\n# Function to validate workload type\nvalidate_workload_type() {\n local type=\"$1\"\n case \"$type\" in\n deployment|statefulset|daemonset)\n return 0\n ;;\n *)\n error_exit \"Invalid workload type '${type}'. Must be: deployment, statefulset, or daemonset\"\n ;;\n esac\n}\n\n# Function to parse image references while supporting registry ports and digest references\nparse_image_reference() {\n local image_ref=\"$1\"\n local last_segment\n\n if [ -z \"$image_ref\" ]; then\n error_exit \"Image repository cannot be empty\"\n fi\n\n if [[ \"$image_ref\" =~ [[:space:]] ]]; then\n error_exit \"Image repository '${image_ref}' is invalid. It cannot contain spaces\"\n fi\n\n # Digest references have priority over tag parsing.\n if [[ \"$image_ref\" == *@* ]]; then\n IMAGE_REPO=\"${image_ref%@*}\"\n IMAGE_DIGEST=\"${image_ref##*@}\"\n\n if [ -z \"$IMAGE_REPO\" ] || [ -z \"$IMAGE_DIGEST\" ]; then\n error_exit \"Invalid digest image reference '${image_ref}'. Use format \u003crepository>@\u003calgorithm>:\u003chash>\"\n fi\n\n validate_digest_reference \"$image_ref\" \"$IMAGE_DIGEST\"\n IMAGE_DIGEST=\"$(normalize_image_digest \"$IMAGE_DIGEST\")\"\n return 0\n fi\n\n last_segment=\"${image_ref##*/}\"\n\n # If --tag is explicitly set, image must not include a tag already.\n if [ \"$TAG_EXPLICIT\" = true ] && [[ \"$last_segment\" == *\":\"* ]]; then\n error_exit \"--image appears to include a tag. Remove the tag from --image when using --tag\"\n fi\n\n # Auto-split \"\u003crepo>:\u003ctag>\" only when \":\" appears in the last path segment.\n # This preserves registry ports such as \"registry.example.com:5000/myapp\".\n if [ \"$TAG_EXPLICIT\" = false ] && [[ \"$last_segment\" == *\":\"* ]]; then\n IMAGE_TAG=\"${last_segment##*:}\"\n IMAGE_REPO=\"${image_ref%:*}\"\n\n if [ -z \"$IMAGE_REPO\" ] || [ -z \"$IMAGE_TAG\" ]; then\n error_exit \"Invalid tagged image reference '${image_ref}'\"\n fi\n\n validate_image_tag \"$IMAGE_TAG\"\n echo \" Note: Auto-split image into repository '${IMAGE_REPO}' and tag '${IMAGE_TAG}'\"\n return 0\n fi\n\n IMAGE_REPO=\"$image_ref\"\n return 0\n}\n\n# Function to show usage\nshow_usage() {\n cat \u003c\u003c EOF\nUsage: $0 \u003cchart-name> \u003coutput-directory> [options]\n\nArguments:\n chart-name Name of the Helm chart (must be DNS-1123 compliant)\n output-directory Directory where chart will be created\n\nOptions:\n --force Overwrite existing chart without prompting\n --image \u003crepo> Set image repository (default: nginx)\n Supports registry ports, tags, and digest refs:\n - registry:5000/app\n - registry:5000/app:1.2.3\n - registry:5000/app@sha256:...\n --tag \u003ctag> Set image tag (default: uses chart appVersion)\n --target-port \u003cnumber>\n Set container target port (default: 8080)\n --port \u003cnumber> Set service port (default: 80)\n --type \u003ctype> Set workload type: deployment, statefulset, daemonset (default: deployment)\n --with-templates Generate basic resource templates (deployment.yaml, service.yaml, etc.)\n --with-ingress Include ingress template (implies --with-templates)\n --with-hpa Include HPA template (implies --with-templates)\n -h, --help Show this help message\n\nExamples:\n $0 myapp ./charts\n $0 my-service ./charts --image myregistry/myapp --port 8080 --target-port 8080\n $0 my-service ./charts --image redis --tag 7-alpine\n $0 my-service ./charts --image localhost:5000/team/api:1.2.0\n $0 my-service ./charts --image ghcr.io/acme/api@sha256:abc123... --with-templates\n $0 my-db ./charts --type statefulset --force\n $0 myapp ./charts --with-templates --with-ingress\n\nChart name requirements (DNS-1123 subdomain):\n - Maximum 63 characters\n - Lowercase letters, numbers, and hyphens only\n - Must start and end with alphanumeric character\n - No consecutive hyphens\nEOF\n exit 0\n}\n\n# Parse arguments\nPOSITIONAL_ARGS=()\n\nwhile [[ $# -gt 0 ]]; do\n case $1 in\n -h|--help)\n show_usage\n ;;\n --force)\n FORCE=true\n shift\n ;;\n --image)\n require_option_arg \"$1\" \"${2:-}\"\n IMAGE_REPO=\"$2\"\n shift 2\n ;;\n --tag)\n require_option_arg \"$1\" \"${2:-}\"\n IMAGE_TAG=\"$2\"\n TAG_EXPLICIT=true\n shift 2\n ;;\n --port)\n require_option_arg \"$1\" \"${2:-}\"\n SERVICE_PORT=\"$2\"\n shift 2\n ;;\n --target-port)\n require_option_arg \"$1\" \"${2:-}\"\n TARGET_PORT=\"$2\"\n shift 2\n ;;\n --type)\n require_option_arg \"$1\" \"${2:-}\"\n WORKLOAD_TYPE=\"$2\"\n shift 2\n ;;\n --with-templates)\n WITH_TEMPLATES=true\n shift\n ;;\n --with-ingress)\n WITH_TEMPLATES=true\n WITH_INGRESS=true\n shift\n ;;\n --with-hpa)\n WITH_TEMPLATES=true\n WITH_HPA=true\n shift\n ;;\n -*)\n error_exit \"Unknown option: $1. Use --help for usage information.\"\n ;;\n *)\n POSITIONAL_ARGS+=(\"$1\")\n shift\n ;;\n esac\ndone\n\n# Restore positional parameters\nset -- \"${POSITIONAL_ARGS[@]}\"\n\n# Check required arguments\nif [ $# -lt 2 ]; then\n echo \"Usage: $0 \u003cchart-name> \u003coutput-directory> [options]\"\n echo \"Example: $0 myapp ./charts\"\n echo \"Use --help for more information\"\n exit 1\nfi\n\nCHART_NAME=\"$1\"\nOUTPUT_DIR=\"$2\"\nCHART_DIR=\"${OUTPUT_DIR}/${CHART_NAME}\"\n\n# Validate inputs\nvalidate_chart_name \"$CHART_NAME\"\nvalidate_port \"$SERVICE_PORT\"\nvalidate_port \"$TARGET_PORT\"\nvalidate_workload_type \"$WORKLOAD_TYPE\"\nparse_image_reference \"$IMAGE_REPO\"\n\n# Validate explicit tag if provided\nif [ -n \"$IMAGE_TAG\" ]; then\n validate_image_tag \"$IMAGE_TAG\"\nfi\n\n# Check if chart directory already exists\nif [ -d \"$CHART_DIR\" ]; then\n if [ \"$FORCE\" = true ]; then\n warn \"Overwriting existing chart at ${CHART_DIR}\"\n rm -rf \"$CHART_DIR\"\n else\n echo -e \"${YELLOW}Chart directory already exists: ${CHART_DIR}${NC}\"\n read -p \"Do you want to overwrite it? [y/N] \" -n 1 -r\n echo\n if [[ ! $REPLY =~ ^[Yy]$ ]]; then\n echo \"Aborted. Use --force to overwrite without prompting.\"\n exit 1\n fi\n rm -rf \"$CHART_DIR\"\n fi\nfi\n\nIMAGE_DISPLAY=\"$IMAGE_REPO\"\nif [ -n \"$IMAGE_DIGEST\" ]; then\n IMAGE_DISPLAY=\"${IMAGE_REPO}@${IMAGE_DIGEST}\"\nelif [ -n \"$IMAGE_TAG\" ]; then\n IMAGE_DISPLAY=\"${IMAGE_REPO}:${IMAGE_TAG}\"\nelse\n IMAGE_DISPLAY=\"${IMAGE_REPO}:\u003cchart appVersion>\"\nfi\n\necho \"Creating Helm chart structure for: ${CHART_NAME}\"\necho \" Output directory: ${CHART_DIR}\"\necho \" Image: ${IMAGE_DISPLAY}\"\necho \" Service port: ${SERVICE_PORT}\"\necho \" Target port: ${TARGET_PORT}\"\necho \" Workload type: ${WORKLOAD_TYPE}\"\n\n# Create directories\nmkdir -p \"${CHART_DIR}/templates\"\nmkdir -p \"${CHART_DIR}/charts\"\n\n# Create Chart.yaml\ncat > \"${CHART_DIR}/Chart.yaml\" \u003c\u003cEOF\napiVersion: v2\nname: ${CHART_NAME}\ndescription: A Helm chart for Kubernetes\ntype: application\n\n# This is the chart version. This version number should be incremented each time you make changes\n# to the chart and its templates, including the app version.\n# Versions are expected to follow Semantic Versioning (https://semver.org/)\nversion: 0.1.0\n\n# This is the version number of the application being deployed. This version number should be\n# incremented each time you make changes to the application. Versions are not expected to\n# follow Semantic Versioning. They should reflect the version the application is using.\nappVersion: \"1.0.0\"\nEOF\n\n# Create values.yaml with customized settings\ncat > \"${CHART_DIR}/values.yaml\" \u003c\u003cEOF\n# Default values for ${CHART_NAME}.\n# This is a YAML-formatted file.\n# Declare variables to be passed into your templates.\n\n# Workload type: ${WORKLOAD_TYPE}\nreplicaCount: 1\n\nimage:\n repository: ${IMAGE_REPO}\n pullPolicy: IfNotPresent\n # Overrides the image tag whose default is the chart appVersion (ignored when digest is set).\n tag: \"${IMAGE_TAG}\"\n # Image digest in format algorithm:hash (takes precedence over tag when set).\n digest: \"${IMAGE_DIGEST}\"\n\nimagePullSecrets: []\nnameOverride: \"\"\nfullnameOverride: \"\"\n\nserviceAccount:\n # Specifies whether a service account should be created\n create: true\n # Automatically mount a ServiceAccount's API credentials?\n automount: true\n # Annotations to add to the service account\n annotations: {}\n # The name of the service account to use.\n # If not set and create is true, a name is generated using the fullname template\n name: \"\"\n\npodAnnotations: {}\npodLabels: {}\n\npodSecurityContext:\n runAsNonRoot: true\n runAsUser: 1000\n fsGroup: 1000\n\nsecurityContext:\n allowPrivilegeEscalation: false\n capabilities:\n drop:\n - ALL\n readOnlyRootFilesystem: true\n\nservice:\n type: ClusterIP\n port: ${SERVICE_PORT}\n # -- Container port exposed by the workload\n targetPort: ${TARGET_PORT}\n # -- Port name used in service and container port definitions\n # Customize for non-HTTP services (e.g., redis, mysql, grpc)\n portName: http\n\n# -- Liveness probe configuration\nlivenessProbe:\n httpGet:\n path: /healthz\n port: http\n initialDelaySeconds: 30\n periodSeconds: 10\n\n# -- Readiness probe configuration\nreadinessProbe:\n httpGet:\n path: /ready\n port: http\n initialDelaySeconds: 5\n periodSeconds: 5\n\n# -- Startup probe configuration (for slow-starting containers)\nstartupProbe: {}\n # httpGet:\n # path: /healthz\n # port: http\n # initialDelaySeconds: 10\n # periodSeconds: 10\n # failureThreshold: 30\n\ningress:\n enabled: false\n className: \"\"\n annotations: {}\n # kubernetes.io/ingress.class: nginx\n # kubernetes.io/tls-acme: \"true\"\n hosts:\n - host: ${CHART_NAME}.local\n paths:\n - path: /\n pathType: ImplementationSpecific\n tls: []\n # - secretName: ${CHART_NAME}-tls\n # hosts:\n # - ${CHART_NAME}.local\n\nresources:\n limits:\n cpu: 100m\n memory: 128Mi\n requests:\n cpu: 100m\n memory: 128Mi\n\nautoscaling:\n enabled: false\n minReplicas: 1\n maxReplicas: 100\n targetCPUUtilizationPercentage: 80\n # targetMemoryUtilizationPercentage: 80\n\n# -- ConfigMap template toggle used by checksum annotations\nconfigMap:\n enabled: false\n data: {}\n\n# -- Secret template toggle used by checksum annotations\nsecret:\n enabled: false\n type: Opaque\n stringData: {}\nEOF\n\n# Add StatefulSet-specific values if workload type is statefulset\nif [ \"$WORKLOAD_TYPE\" = \"statefulset\" ]; then\n cat >> \"${CHART_DIR}/values.yaml\" \u003c\u003cEOF\n\n# StatefulSet specific configuration\npersistence:\n enabled: true\n storageClass: \"\"\n accessModes:\n - ReadWriteOnce\n size: 1Gi\n mountPath: /data\nEOF\nfi\n\n# Add DaemonSet-specific values if workload type is daemonset\nif [ \"$WORKLOAD_TYPE\" = \"daemonset\" ]; then\n cat >> \"${CHART_DIR}/values.yaml\" \u003c\u003cEOF\n\n# DaemonSet specific configuration\nupdateStrategy:\n type: RollingUpdate\n rollingUpdate:\n maxUnavailable: 1\n\n# Host networking (for node-level daemons)\nhostNetwork: false\nhostPID: false\nEOF\nfi\n\n# Add common trailing values\ncat >> \"${CHART_DIR}/values.yaml\" \u003c\u003cEOF\n\n# -- Environment variables\nenv: []\n# - name: ENV_VAR_NAME\n# value: \"value\"\n\n# -- Environment variables from ConfigMaps or Secrets\nenvFrom: []\n# - configMapRef:\n# name: my-configmap\n# - secretRef:\n# name: my-secret\n\n# -- Volume mounts\nvolumeMounts: []\n# - name: config\n# mountPath: /etc/config\n\n# -- Volumes\nvolumes: []\n# - name: config\n# configMap:\n# name: my-config\n\nnodeSelector: {}\n\ntolerations: []\n\naffinity: {}\nEOF\n\n# Create .helmignore\ncat > \"${CHART_DIR}/.helmignore\" \u003c\u003cEOF\n# Patterns to ignore when building packages.\n# This supports shell glob matching, relative path matching, and\n# negation (prefixed with !). Only one pattern per line.\n.DS_Store\n# Common VCS dirs\n.git/\n.gitignore\n.bzr/\n.bzrignore\n.hg/\n.hgignore\n.svn/\n# Common backup files\n*.swp\n*.bak\n*.tmp\n*.orig\n*~\n# Various IDEs\n.project\n.idea/\n*.tmproj\n.vscode/\n# CI/CD\n.github/\n.gitlab-ci.yml\n# Testing\ntest/\ntests/\n*.test\n# Documentation\nREADME.md\nCONTRIBUTING.md\nEOF\n\n# Create NOTES.txt\ncat > \"${CHART_DIR}/templates/NOTES.txt\" \u003c\u003cEOF\nThank you for installing {{ .Chart.Name }}.\n\nYour release is named {{ .Release.Name }}.\n\nTo get the status of your release:\n\n helm status {{ .Release.Name }} -n {{ .Release.Namespace }}\n\nTo connect to your service:\n{{- if and .Values.ingress (eq .Values.ingress.enabled true) }}\n{{- range \\$host := .Values.ingress.hosts }}\n {{- range .paths }}\n http{{ if \\$.Values.ingress.tls }}s{{ end }}://{{ \\$host.host }}{{ .path }}\n {{- end }}\n{{- end }}\n{{- else if contains \"NodePort\" .Values.service.type }}\n export NODE_PORT=\\$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath=\"{.spec.ports[0].nodePort}\" services {{ include \"${CHART_NAME}.fullname\" . }})\n export NODE_IP=\\$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath=\"{.items[0].status.addresses[0].address}\")\n echo \"Service available at: \\$NODE_IP:\\$NODE_PORT\"\n{{- else if contains \"LoadBalancer\" .Values.service.type }}\n NOTE: It may take a few minutes for the LoadBalancer IP to be available.\n kubectl get svc --namespace {{ .Release.Namespace }} -w {{ include \"${CHART_NAME}.fullname\" . }}\n\n export SERVICE_IP=\\$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include \"${CHART_NAME}.fullname\" . }} --template \"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}\")\n echo \"Service available at: \\$SERVICE_IP:{{ .Values.service.port }}\"\n{{- else if contains \"ClusterIP\" .Values.service.type }}\n kubectl port-forward --namespace {{ .Release.Namespace }} svc/{{ include \"${CHART_NAME}.fullname\" . }} {{ .Values.service.port }}:{{ .Values.service.port }}\n\n Then access via: localhost:{{ .Values.service.port }}\n{{- end }}\nEOF\n\n# Generate _helpers.tpl using the standard helpers script\nSCRIPT_DIR=\"$(cd \"$(dirname \"$0\")\" && pwd)\"\nif [ -f \"${SCRIPT_DIR}/generate_standard_helpers.sh\" ]; then\n bash \"${SCRIPT_DIR}/generate_standard_helpers.sh\" \"${CHART_NAME}\" \"${CHART_DIR}\" 2>/dev/null || true\nfi\n\n# Generate resource templates if requested\nif [ \"$WITH_TEMPLATES\" = true ]; then\n echo \" Generating resource templates...\"\n\n # Generate ServiceAccount template\n cat > \"${CHART_DIR}/templates/serviceaccount.yaml\" \u003c\u003cSAEOF\n{{- if .Values.serviceAccount.create -}}\napiVersion: v1\nkind: ServiceAccount\nmetadata:\n name: {{ include \"${CHART_NAME}.serviceAccountName\" . }}\n labels:\n {{- include \"${CHART_NAME}.labels\" . | nindent 4 }}\n {{- with .Values.serviceAccount.annotations }}\n annotations:\n {{- toYaml . | nindent 4 }}\n {{- end }}\nautomountServiceAccountToken: {{ .Values.serviceAccount.automount }}\n{{- end }}\nSAEOF\n\n # Generate Service template\n cat > \"${CHART_DIR}/templates/service.yaml\" \u003c\u003cSVCEOF\napiVersion: v1\nkind: Service\nmetadata:\n name: {{ include \"${CHART_NAME}.fullname\" . }}\n labels:\n {{- include \"${CHART_NAME}.labels\" . | nindent 4 }}\nspec:\n type: {{ .Values.service.type }}\n ports:\n - port: {{ .Values.service.port }}\n targetPort: {{ .Values.service.targetPort }}\n protocol: TCP\n name: {{ .Values.service.portName | default \"http\" }}\n selector:\n {{- include \"${CHART_NAME}.selectorLabels\" . | nindent 4 }}\nSVCEOF\n\n # Generate ConfigMap template used by optional checksum annotations\n cat > \"${CHART_DIR}/templates/configmap.yaml\" \u003c\u003cCMEOF\n{{- if .Values.configMap.enabled -}}\napiVersion: v1\nkind: ConfigMap\nmetadata:\n name: {{ include \"${CHART_NAME}.fullname\" . }}\n labels:\n {{- include \"${CHART_NAME}.labels\" . | nindent 4 }}\ndata:\n {{- toYaml .Values.configMap.data | nindent 2 }}\n{{- end }}\nCMEOF\n\n # Generate Secret template used by optional checksum annotations\n cat > \"${CHART_DIR}/templates/secret.yaml\" \u003c\u003cSECEOF\n{{- if .Values.secret.enabled -}}\napiVersion: v1\nkind: Secret\nmetadata:\n name: {{ include \"${CHART_NAME}.fullname\" . }}\n labels:\n {{- include \"${CHART_NAME}.labels\" . | nindent 4 }}\ntype: {{ .Values.secret.type | default \"Opaque\" }}\nstringData:\n {{- toYaml .Values.secret.stringData | nindent 2 }}\n{{- end }}\nSECEOF\n\n # Generate headless service for StatefulSet\n if [ \"$WORKLOAD_TYPE\" = \"statefulset\" ]; then\n cat > \"${CHART_DIR}/templates/service-headless.yaml\" \u003c\u003cHLSVCEOF\napiVersion: v1\nkind: Service\nmetadata:\n name: {{ include \"${CHART_NAME}.fullname\" . }}-headless\n labels:\n {{- include \"${CHART_NAME}.labels\" . | nindent 4 }}\nspec:\n type: ClusterIP\n clusterIP: None\n ports:\n - port: {{ .Values.service.port }}\n targetPort: {{ .Values.service.targetPort }}\n protocol: TCP\n name: {{ .Values.service.portName | default \"http\" }}\n selector:\n {{- include \"${CHART_NAME}.selectorLabels\" . | nindent 4 }}\nHLSVCEOF\n fi\n\n # Generate workload template based on type\n case \"$WORKLOAD_TYPE\" in\n deployment)\n cat > \"${CHART_DIR}/templates/deployment.yaml\" \u003c\u003cDEPEOF\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: {{ include \"${CHART_NAME}.fullname\" . }}\n labels:\n {{- include \"${CHART_NAME}.labels\" . | nindent 4 }}\nspec:\n {{- if not .Values.autoscaling.enabled }}\n replicas: {{ .Values.replicaCount }}\n {{- end }}\n selector:\n matchLabels:\n {{- include \"${CHART_NAME}.selectorLabels\" . | nindent 6 }}\n template:\n metadata:\n annotations:\n {{- with .Values.podAnnotations }}\n {{- toYaml . | nindent 8 }}\n {{- end }}\n {{- if .Values.configMap }}\n {{- if .Values.configMap.enabled }}\n checksum/config: {{ include (print \\$.Template.BasePath \"/configmap.yaml\") . | sha256sum }}\n {{- end }}\n {{- end }}\n {{- if .Values.secret }}\n {{- if .Values.secret.enabled }}\n checksum/secret: {{ include (print \\$.Template.BasePath \"/secret.yaml\") . | sha256sum }}\n {{- end }}\n {{- end }}\n labels:\n {{- include \"${CHART_NAME}.labels\" . | nindent 8 }}\n {{- with .Values.podLabels }}\n {{- toYaml . | nindent 8 }}\n {{- end }}\n spec:\n {{- with .Values.imagePullSecrets }}\n imagePullSecrets:\n {{- toYaml . | nindent 8 }}\n {{- end }}\n serviceAccountName: {{ include \"${CHART_NAME}.serviceAccountName\" . }}\n securityContext:\n {{- toYaml .Values.podSecurityContext | nindent 8 }}\n containers:\n - name: {{ .Chart.Name }}\n securityContext:\n {{- toYaml .Values.securityContext | nindent 12 }}\n {{- if .Values.image.digest }}\n image: \"{{ .Values.image.repository }}@{{ .Values.image.digest }}\"\n {{- else }}\n image: \"{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}\"\n {{- end }}\n imagePullPolicy: {{ .Values.image.pullPolicy }}\n ports:\n - name: {{ .Values.service.portName | default \"http\" }}\n containerPort: {{ .Values.service.targetPort }}\n protocol: TCP\n {{- with .Values.livenessProbe }}\n livenessProbe:\n {{- toYaml . | nindent 12 }}\n {{- end }}\n {{- with .Values.readinessProbe }}\n readinessProbe:\n {{- toYaml . | nindent 12 }}\n {{- end }}\n {{- with .Values.startupProbe }}\n startupProbe:\n {{- toYaml . | nindent 12 }}\n {{- end }}\n resources:\n {{- toYaml .Values.resources | nindent 12 }}\n {{- with .Values.env }}\n env:\n {{- toYaml . | nindent 12 }}\n {{- end }}\n {{- with .Values.envFrom }}\n envFrom:\n {{- toYaml . | nindent 12 }}\n {{- end }}\n {{- with .Values.volumeMounts }}\n volumeMounts:\n {{- toYaml . | nindent 12 }}\n {{- end }}\n {{- with .Values.volumes }}\n volumes:\n {{- toYaml . | nindent 8 }}\n {{- end }}\n {{- with .Values.nodeSelector }}\n nodeSelector:\n {{- toYaml . | nindent 8 }}\n {{- end }}\n {{- with .Values.affinity }}\n affinity:\n {{- toYaml . | nindent 8 }}\n {{- end }}\n {{- with .Values.tolerations }}\n tolerations:\n {{- toYaml . | nindent 8 }}\n {{- end }}\nDEPEOF\n ;;\n statefulset)\n cat > \"${CHART_DIR}/templates/statefulset.yaml\" \u003c\u003cSTSEOF\napiVersion: apps/v1\nkind: StatefulSet\nmetadata:\n name: {{ include \"${CHART_NAME}.fullname\" . }}\n labels:\n {{- include \"${CHART_NAME}.labels\" . | nindent 4 }}\nspec:\n serviceName: {{ include \"${CHART_NAME}.fullname\" . }}-headless\n replicas: {{ .Values.replicaCount }}\n selector:\n matchLabels:\n {{- include \"${CHART_NAME}.selectorLabels\" . | nindent 6 }}\n template:\n metadata:\n annotations:\n {{- with .Values.podAnnotations }}\n {{- toYaml . | nindent 8 }}\n {{- end }}\n {{- if .Values.configMap }}\n {{- if .Values.configMap.enabled }}\n checksum/config: {{ include (print \\$.Template.BasePath \"/configmap.yaml\") . | sha256sum }}\n {{- end }}\n {{- end }}\n {{- if .Values.secret }}\n {{- if .Values.secret.enabled }}\n checksum/secret: {{ include (print \\$.Template.BasePath \"/secret.yaml\") . | sha256sum }}\n {{- end }}\n {{- end }}\n labels:\n {{- include \"${CHART_NAME}.labels\" . | nindent 8 }}\n {{- with .Values.podLabels }}\n {{- toYaml . | nindent 8 }}\n {{- end }}\n spec:\n {{- with .Values.imagePullSecrets }}\n imagePullSecrets:\n {{- toYaml . | nindent 8 }}\n {{- end }}\n serviceAccountName: {{ include \"${CHART_NAME}.serviceAccountName\" . }}\n securityContext:\n {{- toYaml .Values.podSecurityContext | nindent 8 }}\n containers:\n - name: {{ .Chart.Name }}\n securityContext:\n {{- toYaml .Values.securityContext | nindent 12 }}\n {{- if .Values.image.digest }}\n image: \"{{ .Values.image.repository }}@{{ .Values.image.digest }}\"\n {{- else }}\n image: \"{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}\"\n {{- end }}\n imagePullPolicy: {{ .Values.image.pullPolicy }}\n ports:\n - name: {{ .Values.service.portName | default \"http\" }}\n containerPort: {{ .Values.service.targetPort }}\n protocol: TCP\n {{- with .Values.livenessProbe }}\n livenessProbe:\n {{- toYaml . | nindent 12 }}\n {{- end }}\n {{- with .Values.readinessProbe }}\n readinessProbe:\n {{- toYaml . | nindent 12 }}\n {{- end }}\n {{- with .Values.startupProbe }}\n startupProbe:\n {{- toYaml . | nindent 12 }}\n {{- end }}\n resources:\n {{- toYaml .Values.resources | nindent 12 }}\n {{- with .Values.env }}\n env:\n {{- toYaml . | nindent 12 }}\n {{- end }}\n {{- with .Values.envFrom }}\n envFrom:\n {{- toYaml . | nindent 12 }}\n {{- end }}\n volumeMounts:\n {{- if .Values.persistence.enabled }}\n - name: data\n mountPath: {{ .Values.persistence.mountPath }}\n {{- end }}\n {{- with .Values.volumeMounts }}\n {{- toYaml . | nindent 12 }}\n {{- end }}\n {{- with .Values.volumes }}\n volumes:\n {{- toYaml . | nindent 8 }}\n {{- end }}\n {{- with .Values.nodeSelector }}\n nodeSelector:\n {{- toYaml . | nindent 8 }}\n {{- end }}\n {{- with .Values.affinity }}\n affinity:\n {{- toYaml . | nindent 8 }}\n {{- end }}\n {{- with .Values.tolerations }}\n tolerations:\n {{- toYaml . | nindent 8 }}\n {{- end }}\n {{- if .Values.persistence.enabled }}\n volumeClaimTemplates:\n - metadata:\n name: data\n spec:\n accessModes:\n {{- range .Values.persistence.accessModes }}\n - {{ . | quote }}\n {{- end }}\n {{- if .Values.persistence.storageClass }}\n storageClassName: {{ .Values.persistence.storageClass | quote }}\n {{- end }}\n resources:\n requests:\n storage: {{ .Values.persistence.size | quote }}\n {{- end }}\nSTSEOF\n ;;\n daemonset)\n cat > \"${CHART_DIR}/templates/daemonset.yaml\" \u003c\u003cDSEOF\napiVersion: apps/v1\nkind: DaemonSet\nmetadata:\n name: {{ include \"${CHART_NAME}.fullname\" . }}\n labels:\n {{- include \"${CHART_NAME}.labels\" . | nindent 4 }}\nspec:\n selector:\n matchLabels:\n {{- include \"${CHART_NAME}.selectorLabels\" . | nindent 6 }}\n {{- with .Values.updateStrategy }}\n updateStrategy:\n {{- toYaml . | nindent 4 }}\n {{- end }}\n template:\n metadata:\n annotations:\n {{- with .Values.podAnnotations }}\n {{- toYaml . | nindent 8 }}\n {{- end }}\n {{- if .Values.configMap }}\n {{- if .Values.configMap.enabled }}\n checksum/config: {{ include (print \\$.Template.BasePath \"/configmap.yaml\") . | sha256sum }}\n {{- end }}\n {{- end }}\n {{- if .Values.secret }}\n {{- if .Values.secret.enabled }}\n checksum/secret: {{ include (print \\$.Template.BasePath \"/secret.yaml\") . | sha256sum }}\n {{- end }}\n {{- end }}\n labels:\n {{- include \"${CHART_NAME}.labels\" . | nindent 8 }}\n {{- with .Values.podLabels }}\n {{- toYaml . | nindent 8 }}\n {{- end }}\n spec:\n {{- with .Values.imagePullSecrets }}\n imagePullSecrets:\n {{- toYaml . | nindent 8 }}\n {{- end }}\n serviceAccountName: {{ include \"${CHART_NAME}.serviceAccountName\" . }}\n securityContext:\n {{- toYaml .Values.podSecurityContext | nindent 8 }}\n hostNetwork: {{ .Values.hostNetwork | default false }}\n {{- if .Values.hostPID }}\n hostPID: true\n {{- end }}\n containers:\n - name: {{ .Chart.Name }}\n securityContext:\n {{- toYaml .Values.securityContext | nindent 12 }}\n {{- if .Values.image.digest }}\n image: \"{{ .Values.image.repository }}@{{ .Values.image.digest }}\"\n {{- else }}\n image: \"{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}\"\n {{- end }}\n imagePullPolicy: {{ .Values.image.pullPolicy }}\n ports:\n - name: {{ .Values.service.portName | default \"http\" }}\n containerPort: {{ .Values.service.targetPort }}\n protocol: TCP\n {{- with .Values.livenessProbe }}\n livenessProbe:\n {{- toYaml . | nindent 12 }}\n {{- end }}\n {{- with .Values.readinessProbe }}\n readinessProbe:\n {{- toYaml . | nindent 12 }}\n {{- end }}\n {{- with .Values.startupProbe }}\n startupProbe:\n {{- toYaml . | nindent 12 }}\n {{- end }}\n resources:\n {{- toYaml .Values.resources | nindent 12 }}\n {{- with .Values.env }}\n env:\n {{- toYaml . | nindent 12 }}\n {{- end }}\n {{- with .Values.envFrom }}\n envFrom:\n {{- toYaml . | nindent 12 }}\n {{- end }}\n {{- with .Values.volumeMounts }}\n volumeMounts:\n {{- toYaml . | nindent 12 }}\n {{- end }}\n {{- with .Values.volumes }}\n volumes:\n {{- toYaml . | nindent 8 }}\n {{- end }}\n {{- with .Values.nodeSelector }}\n nodeSelector:\n {{- toYaml . | nindent 8 }}\n {{- end }}\n {{- with .Values.affinity }}\n affinity:\n {{- toYaml . | nindent 8 }}\n {{- end }}\n {{- with .Values.tolerations }}\n tolerations:\n {{- toYaml . | nindent 8 }}\n {{- end }}\nDSEOF\n ;;\n esac\n\n # Generate Ingress template if requested\n if [ \"$WITH_INGRESS\" = true ]; then\n cat > \"${CHART_DIR}/templates/ingress.yaml\" \u003c\u003c'INGEOF'\n{{- if .Values.ingress.enabled -}}\napiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n name: {{ include \"CHART_PLACEHOLDER.fullname\" . }}\n labels:\n {{- include \"CHART_PLACEHOLDER.labels\" . | nindent 4 }}\n {{- with .Values.ingress.annotations }}\n annotations:\n {{- toYaml . | nindent 4 }}\n {{- end }}\nspec:\n {{- if .Values.ingress.className }}\n ingressClassName: {{ .Values.ingress.className }}\n {{- end }}\n {{- if .Values.ingress.tls }}\n tls:\n {{- range .Values.ingress.tls }}\n - hosts:\n {{- range .hosts }}\n - {{ . | quote }}\n {{- end }}\n secretName: {{ .secretName }}\n {{- end }}\n {{- end }}\n rules:\n {{- range .Values.ingress.hosts }}\n - host: {{ .host | quote }}\n http:\n paths:\n {{- range .paths }}\n - path: {{ .path }}\n pathType: {{ .pathType }}\n backend:\n service:\n name: {{ include \"CHART_PLACEHOLDER.fullname\" $ }}\n port:\n number: {{ $.Values.service.port }}\n {{- end }}\n {{- end }}\n{{- end }}\nINGEOF\n sed -i.bak \"s/CHART_PLACEHOLDER/${CHART_NAME}/g\" \"${CHART_DIR}/templates/ingress.yaml\"\n rm -f \"${CHART_DIR}/templates/ingress.yaml.bak\"\n fi\n\n # Generate HPA template if requested (only for Deployment and StatefulSet)\n if [ \"$WITH_HPA\" = true ] && [ \"$WORKLOAD_TYPE\" != \"daemonset\" ]; then\n # Determine the workload kind for HPA\n HPA_KIND=\"Deployment\"\n if [ \"$WORKLOAD_TYPE\" = \"statefulset\" ]; then\n HPA_KIND=\"StatefulSet\"\n fi\n cat > \"${CHART_DIR}/templates/hpa.yaml\" \u003c\u003cHPAEOF\n{{- if .Values.autoscaling.enabled }}\napiVersion: autoscaling/v2\nkind: HorizontalPodAutoscaler\nmetadata:\n name: {{ include \"${CHART_NAME}.fullname\" . }}\n labels:\n {{- include \"${CHART_NAME}.labels\" . | nindent 4 }}\nspec:\n scaleTargetRef:\n apiVersion: apps/v1\n kind: ${HPA_KIND}\n name: {{ include \"${CHART_NAME}.fullname\" . }}\n minReplicas: {{ .Values.autoscaling.minReplicas }}\n maxReplicas: {{ .Values.autoscaling.maxReplicas }}\n metrics:\n {{- if .Values.autoscaling.targetCPUUtilizationPercentage }}\n - type: Resource\n resource:\n name: cpu\n target:\n type: Utilization\n averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}\n {{- end }}\n {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}\n - type: Resource\n resource:\n name: memory\n target:\n type: Utilization\n averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}\n {{- end }}\n{{- end }}\nHPAEOF\n elif [ \"$WITH_HPA\" = true ] && [ \"$WORKLOAD_TYPE\" = \"daemonset\" ]; then\n warn \"HPA is not applicable for DaemonSet workloads - skipping hpa.yaml\"\n fi\n echo \" ✅ Resource templates generated\"\nfi\n\necho \"\"\necho -e \"${GREEN}✅ Chart structure created successfully!${NC}\"\necho \"\"\necho \" 📁 ${CHART_DIR}/\"\necho \" ├── Chart.yaml\"\necho \" ├── values.yaml\"\necho \" ├── .helmignore\"\necho \" ├── templates/\"\nif [ -f \"${CHART_DIR}/templates/_helpers.tpl\" ]; then\n echo \" │ ├── _helpers.tpl\"\nfi\necho \" │ ├── NOTES.txt\"\nif [ \"$WITH_TEMPLATES\" = true ]; then\n echo \" │ ├── serviceaccount.yaml\"\n echo \" │ ├── service.yaml\"\n echo \" │ ├── configmap.yaml\"\n echo \" │ ├── secret.yaml\"\n if [ \"$WORKLOAD_TYPE\" = \"statefulset\" ]; then\n echo \" │ ├── service-headless.yaml\"\n echo \" │ ├── statefulset.yaml\"\n elif [ \"$WORKLOAD_TYPE\" = \"daemonset\" ]; then\n echo \" │ ├── daemonset.yaml\"\n else\n echo \" │ ├── deployment.yaml\"\n fi\n if [ \"$WITH_INGRESS\" = true ]; then\n echo \" │ ├── ingress.yaml\"\n fi\n if [ \"$WITH_HPA\" = true ] && [ \"$WORKLOAD_TYPE\" != \"daemonset\" ]; then\n echo \" │ └── hpa.yaml\"\n fi\nfi\necho \" └── charts/\"\necho \"\"\necho \"Next steps:\"\nif [ \"$WITH_TEMPLATES\" = true ]; then\n echo \"1. Customize values in ${CHART_DIR}/values.yaml\"\n echo \"2. Validate with: helm lint ${CHART_DIR}\"\n echo \"3. Test rendering: helm template test ${CHART_DIR}\"\nelse\n echo \"1. Generate templates or use --with-templates flag\"\n echo \"2. Customize values in ${CHART_DIR}/values.yaml\"\n echo \"3. Validate with: helm lint ${CHART_DIR}\"\nfi\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":38805,"content_sha256":"536e3654409e433dcab81fab239101d9d81c55e620bac0e45f79f6fb09e6e2e2"},{"filename":"scripts/generate_standard_helpers.sh","content":"#!/bin/bash\n\n# Script to generate standard Helm helpers (_helpers.tpl)\n# Usage: bash generate_standard_helpers.sh \u003cchart-name> \u003cchart-directory> [options]\n#\n# Options:\n# --force Overwrite existing _helpers.tpl without prompting\n\nset -e\n\n# Colors for output\nRED='\\033[0;31m'\nYELLOW='\\033[1;33m'\nNC='\\033[0m' # No Color\n\n# Default values\nFORCE=false\n\n# Function to print error and exit\nerror_exit() {\n echo -e \"${RED}ERROR: $1${NC}\" >&2\n exit 1\n}\n\n# Function to print warning\nwarn() {\n echo -e \"${YELLOW}WARNING: $1${NC}\" >&2\n}\n\n# Function to validate chart name (DNS-1123 subdomain)\nvalidate_chart_name() {\n local name=\"$1\"\n\n # Check if empty\n if [ -z \"$name\" ]; then\n error_exit \"Chart name cannot be empty\"\n fi\n\n # Check length (max 63 characters for DNS-1123)\n if [ ${#name} -gt 63 ]; then\n error_exit \"Chart name '${name}' exceeds 63 characters (DNS-1123 limit)\"\n fi\n\n # Check for valid characters (lowercase alphanumeric and hyphens)\n if ! echo \"$name\" | grep -qE '^[a-z0-9]([-a-z0-9]*[a-z0-9])?

Helm Chart Generator Overview Generate production-ready Helm charts with deterministic scaffolding, standard helpers, reusable templates, and validation loops. Official Documentation: - Helm Docs - Main documentation - Chart Best Practices - Official best practices guide - Template Functions - Built-in functions - Sprig Functions - Extended function library When to Use This Skill | Use helm-generator | Use OTHER skill | |-------------------|-----------------| | Create new Helm charts | helm-validator : Validate/lint existing charts | | Generate Helm templates | k8s-yaml-generator : Raw K8s YA…

; then\n error_exit \"Chart name '${name}' is invalid. Must:\n - Start with a lowercase letter or number\n - Contain only lowercase letters, numbers, and hyphens\n - End with a lowercase letter or number\nExamples: myapp, my-app, app1, my-cool-app\"\n fi\n\n # Check for consecutive hyphens\n if echo \"$name\" | grep -q '\\-\\-'; then\n error_exit \"Chart name '${name}' contains consecutive hyphens (not allowed)\"\n fi\n}\n\n# Function to show usage\nshow_usage() {\n cat \u003c\u003c EOF\nUsage: $0 \u003cchart-name> \u003cchart-directory> [options]\n\nArguments:\n chart-name Name of the Helm chart (must be DNS-1123 compliant)\n chart-directory Directory of the chart (must exist)\n\nOptions:\n --force Overwrite existing _helpers.tpl without prompting\n -h, --help Show this help message\n\nExamples:\n $0 myapp ./myapp\n $0 my-service ./charts/my-service --force\nEOF\n exit 0\n}\n\n# Parse arguments\nPOSITIONAL_ARGS=()\n\nwhile [[ $# -gt 0 ]]; do\n case $1 in\n -h|--help)\n show_usage\n ;;\n --force)\n FORCE=true\n shift\n ;;\n -*)\n error_exit \"Unknown option: $1. Use --help for usage information.\"\n ;;\n *)\n POSITIONAL_ARGS+=(\"$1\")\n shift\n ;;\n esac\ndone\n\n# Restore positional parameters\nset -- \"${POSITIONAL_ARGS[@]}\"\n\n# Check required arguments\nif [ $# -lt 2 ]; then\n echo \"Usage: $0 \u003cchart-name> \u003cchart-directory> [options]\"\n echo \"Example: $0 myapp ./myapp\"\n echo \"Use --help for more information\"\n exit 1\nfi\n\nCHART_NAME=\"$1\"\nCHART_DIR=\"$2\"\nHELPERS_FILE=\"${CHART_DIR}/templates/_helpers.tpl\"\n\n# Validate chart name\nvalidate_chart_name \"$CHART_NAME\"\n\n# Check if chart directory exists\nif [ ! -d \"$CHART_DIR\" ]; then\n error_exit \"Chart directory does not exist: ${CHART_DIR}\"\nfi\n\n# Check if _helpers.tpl already exists\nif [ -f \"$HELPERS_FILE\" ]; then\n if [ \"$FORCE\" = true ]; then\n warn \"Overwriting existing _helpers.tpl at ${HELPERS_FILE}\"\n else\n echo -e \"${YELLOW}File already exists: ${HELPERS_FILE}${NC}\"\n read -p \"Do you want to overwrite it? [y/N] \" -n 1 -r\n echo\n if [[ ! $REPLY =~ ^[Yy]$ ]]; then\n echo \"Aborted. Use --force to overwrite without prompting.\"\n exit 1\n fi\n fi\nfi\n\n# Ensure templates directory exists\nmkdir -p \"${CHART_DIR}/templates\"\n\n# Generate _helpers.tpl\ncat > \"${HELPERS_FILE}\" \u003c\u003cEOF\n{{/*\nExpand the name of the chart.\n*/}}\n{{- define \"${CHART_NAME}.name\" -}}\n{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix \"-\" }}\n{{- end }}\n\n{{/*\nCreate a default fully qualified app name.\n*/}}\n{{- define \"${CHART_NAME}.fullname\" -}}\n{{- if .Values.fullnameOverride }}\n{{- .Values.fullnameOverride | trunc 63 | trimSuffix \"-\" }}\n{{- else }}\n{{- \\$name := default .Chart.Name .Values.nameOverride }}\n{{- if contains \\$name .Release.Name }}\n{{- .Release.Name | trunc 63 | trimSuffix \"-\" }}\n{{- else }}\n{{- printf \"%s-%s\" .Release.Name \\$name | trunc 63 | trimSuffix \"-\" }}\n{{- end }}\n{{- end }}\n{{- end }}\n\n{{/*\nCreate chart name and version as used by the chart label.\n*/}}\n{{- define \"${CHART_NAME}.chart\" -}}\n{{- printf \"%s-%s\" .Chart.Name .Chart.Version | replace \"+\" \"_\" | trunc 63 | trimSuffix \"-\" }}\n{{- end }}\n\n{{/*\nCommon labels\n*/}}\n{{- define \"${CHART_NAME}.labels\" -}}\nhelm.sh/chart: {{ include \"${CHART_NAME}.chart\" . }}\n{{ include \"${CHART_NAME}.selectorLabels\" . }}\n{{- if .Chart.AppVersion }}\napp.kubernetes.io/version: {{ .Chart.AppVersion | quote }}\n{{- end }}\napp.kubernetes.io/managed-by: {{ .Release.Service }}\n{{- end }}\n\n{{/*\nSelector labels\n*/}}\n{{- define \"${CHART_NAME}.selectorLabels\" -}}\napp.kubernetes.io/name: {{ include \"${CHART_NAME}.name\" . }}\napp.kubernetes.io/instance: {{ .Release.Name }}\n{{- end }}\n\n{{/*\nCreate the name of the service account to use\n*/}}\n{{- define \"${CHART_NAME}.serviceAccountName\" -}}\n{{- if .Values.serviceAccount.create }}\n{{- default (include \"${CHART_NAME}.fullname\" .) .Values.serviceAccount.name }}\n{{- else }}\n{{- default \"default\" .Values.serviceAccount.name }}\n{{- end }}\n{{- end }}\nEOF\n\necho \"✅ Generated _helpers.tpl at ${HELPERS_FILE}\"\necho \" Chart name: ${CHART_NAME}\"\necho \" Standard helpers included:\"\necho \" - ${CHART_NAME}.name\"\necho \" - ${CHART_NAME}.fullname\"\necho \" - ${CHART_NAME}.chart\"\necho \" - ${CHART_NAME}.labels\"\necho \" - ${CHART_NAME}.selectorLabels\"\necho \" - ${CHART_NAME}.serviceAccountName\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":5526,"content_sha256":"f3edda3b8b2219fb00ccabd052be6fbbb007c1ea538194bee971c7b499a85f68"},{"filename":"tests/test_generate_chart_structure.sh","content":"#!/usr/bin/env bash\nset -euo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nSKILL_DIR=\"$(cd \"$SCRIPT_DIR/..\" && pwd)\"\nGENERATOR_SCRIPT=\"$SKILL_DIR/scripts/generate_chart_structure.sh\"\nSCHEMA_FILE=\"$SKILL_DIR/assets/values-schema-template.json\"\n\nTMP_DIR=\"$(mktemp -d)\"\ncleanup() {\n rm -rf \"$TMP_DIR\"\n}\ntrap cleanup EXIT\n\nPASS=0\nFAIL=0\n\npass() {\n echo \" PASS: $1\"\n PASS=$((PASS + 1))\n}\n\nfail() {\n echo \" FAIL: $1\"\n FAIL=$((FAIL + 1))\n}\n\nrun_expect_success() {\n local label=\"$1\"\n shift\n local log_file=\"$TMP_DIR/success_${PASS}_${FAIL}.log\"\n\n set +e\n \"$@\" >\"$log_file\" 2>&1\n local rc=$?\n set -e\n\n if [[ $rc -eq 0 ]]; then\n pass \"$label\"\n else\n fail \"$label (exit $rc)\"\n sed 's/^/ /' \"$log_file\"\n fi\n}\n\nrun_expect_failure() {\n local label=\"$1\"\n shift\n local log_file=\"$TMP_DIR/failure_${PASS}_${FAIL}.log\"\n\n set +e\n \"$@\" >\"$log_file\" 2>&1\n local rc=$?\n set -e\n\n if [[ $rc -ne 0 ]]; then\n pass \"$label\"\n else\n fail \"$label (unexpected success)\"\n fi\n}\n\nassert_file_contains_literal() {\n local label=\"$1\"\n local file_path=\"$2\"\n local expected_text=\"$3\"\n\n if grep -Fq -- \"$expected_text\" \"$file_path\"; then\n pass \"$label\"\n else\n fail \"$label (missing '$expected_text' in $file_path)\"\n fi\n}\n\nif ! command -v helm >/dev/null 2>&1; then\n echo \"FAIL: helm CLI is required for this regression suite\"\n exit 1\nfi\n\nif ! command -v python3 >/dev/null 2>&1; then\n echo \"FAIL: python3 is required for schema assertions\"\n exit 1\nfi\n\nVALID_SHA256_HASH=\"94a00394bc5a8ef503fb59db0a7d0ae9e1119866e8aee8ba40cd864cea69ea1a\"\nVALID_SHA512_HASH=\"\"\nfor _ in {1..128}; do\n VALID_SHA512_HASH=\"${VALID_SHA512_HASH}a\"\ndone\n\nCHART_ROOT=\"$TMP_DIR/charts\"\nmkdir -p \"$CHART_ROOT\"\n\necho \"[1] Digest validation\"\nrun_expect_failure \\\n \"reject empty sha256 hash\" \\\n bash \"$GENERATOR_SCRIPT\" invaliddigestempty \"$CHART_ROOT\" \\\n --image \"ghcr.io/acme/api@sha256:\" --force\n\nrun_expect_failure \\\n \"reject short sha256 hash\" \\\n bash \"$GENERATOR_SCRIPT\" invaliddigestshort \"$CHART_ROOT\" \\\n --image \"ghcr.io/acme/api@sha256:abcd\" --force\n\nrun_expect_failure \\\n \"reject non-hex sha256 hash\" \\\n bash \"$GENERATOR_SCRIPT\" invaliddigesthex \"$CHART_ROOT\" \\\n --image \"ghcr.io/acme/api@sha256:zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz\" --force\n\nrun_expect_failure \\\n \"reject unsupported digest algorithm\" \\\n bash \"$GENERATOR_SCRIPT\" invaliddigestalgo \"$CHART_ROOT\" \\\n --image \"ghcr.io/acme/api@md5:0123456789abcdef0123456789abcdef\" --force\n\nrun_expect_success \\\n \"accept valid sha256 digest\" \\\n bash \"$GENERATOR_SCRIPT\" digestoksha256 \"$CHART_ROOT\" \\\n --image \"ghcr.io/acme/api@sha256:${VALID_SHA256_HASH}\" --with-templates --force\n\nrun_expect_success \\\n \"accept valid sha512 digest\" \\\n bash \"$GENERATOR_SCRIPT\" digestoksha512 \"$CHART_ROOT\" \\\n --image \"ghcr.io/acme/api@sha512:${VALID_SHA512_HASH}\" --force\n\nSHA256_VALUES=\"$CHART_ROOT/digestoksha256/values.yaml\"\nSHA512_VALUES=\"$CHART_ROOT/digestoksha512/values.yaml\"\nassert_file_contains_literal \"sha256 digest written to values.yaml\" \"$SHA256_VALUES\" \" digest: \\\"sha256:${VALID_SHA256_HASH}\\\"\"\nassert_file_contains_literal \"sha512 digest written to values.yaml\" \"$SHA512_VALUES\" \" digest: \\\"sha512:${VALID_SHA512_HASH}\\\"\"\nassert_file_contains_literal \"digest chart keeps tag empty\" \"$SHA256_VALUES\" \" tag: \\\"\\\"\"\n\necho \"[2] Tag and digest conflict handling\"\nrun_expect_failure \\\n \"reject digest image with explicit --tag\" \\\n bash \"$GENERATOR_SCRIPT\" digesttagflag \"$CHART_ROOT\" \\\n --image \"ghcr.io/acme/api@sha256:${VALID_SHA256_HASH}\" --tag \"1.2.3\" --force\n\nrun_expect_failure \\\n \"reject image refs containing both tag and digest\" \\\n bash \"$GENERATOR_SCRIPT\" digesttaginline \"$CHART_ROOT\" \\\n --image \"ghcr.io/acme/api:1.2.3@sha256:${VALID_SHA256_HASH}\" --force\n\necho \"[3] Registry port parsing\"\nrun_expect_success \\\n \"parse registry port image without tag\" \\\n bash \"$GENERATOR_SCRIPT\" regportplain \"$CHART_ROOT\" \\\n --image \"registry.example.com:5000/team/api\" --force\n\nrun_expect_success \\\n \"parse registry port image with tag\" \\\n bash \"$GENERATOR_SCRIPT\" regporttag \"$CHART_ROOT\" \\\n --image \"registry.example.com:5000/team/api:1.2.0\" --force\n\nREGPORT_PLAIN_VALUES=\"$CHART_ROOT/regportplain/values.yaml\"\nREGPORT_TAG_VALUES=\"$CHART_ROOT/regporttag/values.yaml\"\nassert_file_contains_literal \"registry-port plain repository preserved\" \"$REGPORT_PLAIN_VALUES\" \" repository: registry.example.com:5000/team/api\"\nassert_file_contains_literal \"registry-port plain tag remains empty\" \"$REGPORT_PLAIN_VALUES\" \" tag: \\\"\\\"\"\nassert_file_contains_literal \"registry-port tagged repository auto-split\" \"$REGPORT_TAG_VALUES\" \" repository: registry.example.com:5000/team/api\"\nassert_file_contains_literal \"registry-port tagged value extracted\" \"$REGPORT_TAG_VALUES\" \" tag: \\\"1.2.0\\\"\"\n\necho \"[4] Helm render smoke tests\"\nrun_expect_success \\\n \"helm lint --strict generated digest chart\" \\\n helm lint --strict \"$CHART_ROOT/digestoksha256\"\n\nrun_expect_success \\\n \"helm template generated digest chart\" \\\n helm template test \"$CHART_ROOT/digestoksha256\"\n\necho \"[5] Schema guardrails\"\nSCHEMA_LOG=\"$TMP_DIR/schema_assertions.log\"\nset +e\npython3 - \"$SCHEMA_FILE\" >\"$SCHEMA_LOG\" 2>&1 \u003c\u003c'PY'\nimport json\nimport sys\n\nwith open(sys.argv[1], \"r\", encoding=\"utf-8\") as fh:\n schema = json.load(fh)\n\nconfig_map_data = schema[\"properties\"][\"configMap\"][\"properties\"][\"data\"]\nsecret_string_data = schema[\"properties\"][\"secret\"][\"properties\"][\"stringData\"]\n\nif config_map_data.get(\"additionalProperties\", {}).get(\"type\") != \"string\":\n raise SystemExit(\"configMap.data must enforce string values\")\n\nif secret_string_data.get(\"additionalProperties\", {}).get(\"type\") != \"string\":\n raise SystemExit(\"secret.stringData must enforce string values\")\nPY\nschema_rc=$?\nset -e\n\nif [[ $schema_rc -eq 0 ]]; then\n pass \"schema constrains ConfigMap/Secret maps to string values\"\nelse\n fail \"schema constrains ConfigMap/Secret maps to string values\"\n sed 's/^/ /' \"$SCHEMA_LOG\"\nfi\n\necho \"\"\necho \"Completed with PASS=$PASS FAIL=$FAIL\"\nif [[ $FAIL -ne 0 ]]; then\n exit 1\nfi\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":6079,"content_sha256":"fba6b4a21aafb51c4a0de88cbb560b8835f8ac0a2a29b867e3af0c7646126df4"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Helm Chart Generator","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Overview","type":"text"}]},{"type":"paragraph","content":[{"text":"Generate production-ready Helm charts with deterministic scaffolding, standard helpers, reusable templates, and validation loops.","type":"text"}]},{"type":"paragraph","content":[{"text":"Official Documentation:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Helm Docs","type":"text","marks":[{"type":"link","attrs":{"href":"https://helm.sh/docs/","title":null}}]},{"text":" - Main documentation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Chart Best Practices","type":"text","marks":[{"type":"link","attrs":{"href":"https://helm.sh/docs/chart_best_practices/","title":null}}]},{"text":" - Official best practices guide","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Template Functions","type":"text","marks":[{"type":"link","attrs":{"href":"https://helm.sh/docs/chart_template_guide/function_list/","title":null}}]},{"text":" - Built-in functions","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Sprig Functions","type":"text","marks":[{"type":"link","attrs":{"href":"http://masterminds.github.io/sprig/","title":null}}]},{"text":" - Extended function library","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When to Use This Skill","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Use helm-generator","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Use OTHER skill","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Create new Helm charts","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"helm-validator","type":"text","marks":[{"type":"strong"}]},{"text":": Validate/lint existing charts","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Generate Helm templates","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"k8s-yaml-generator","type":"text","marks":[{"type":"strong"}]},{"text":": Raw K8s YAML (no Helm)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Convert K8s manifests to Helm","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"k8s-debug","type":"text","marks":[{"type":"strong"}]},{"text":": Debug deployed resources","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Implement CRDs in Helm","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"k8s-yaml-validator","type":"text","marks":[{"type":"strong"}]},{"text":": Validate K8s manifests","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Trigger Phrases","type":"text"}]},{"type":"paragraph","content":[{"text":"Use this skill when prompts include phrases like:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"create Helm chart\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"scaffold Helm chart\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"generate Helm templates\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"convert manifests to Helm chart\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"build chart with Deployment/Service/Ingress\"","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Execution Flow","type":"text"}]},{"type":"paragraph","content":[{"text":"Follow these stages in order. Do not skip required stages.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Stage 1: Gather Requirements (Required)","type":"text"}]},{"type":"paragraph","content":[{"text":"Collect:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Scope: full chart, specific templates, or conversion from manifests","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Workload: ","type":"text"},{"text":"deployment","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"statefulset","type":"text","marks":[{"type":"code_inline"}]},{"text":", or ","type":"text"},{"text":"daemonset","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Image reference: repository, optional tag, or digest","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Ports: service port and container target port (separate values)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Runtime settings: resources, probes, autoscaling, ingress, storage","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Security: service account, security contexts, optional RBAC/network policies","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"request_user_input","type":"text","marks":[{"type":"code_inline"}]},{"text":" when critical fields are missing.","type":"text"}]},{"type":"paragraph","content":[{"text":"If ","type":"text"},{"text":"request_user_input","type":"text","marks":[{"type":"code_inline"}]},{"text":" is unavailable, ask in normal chat and continue with explicit assumptions.","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Missing Information","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Question to Ask","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Image repository/tag","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"What container image should be used? (e.g., nginx:1.25)\"","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Service port","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"What service port should be exposed?\"","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Container target port","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"What container port should traffic be forwarded to?\"","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Resource limits","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"What CPU/memory limits should be set? (e.g., 500m CPU, 512Mi memory)\"","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Probe endpoints","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"What health check endpoints does the app expose? (e.g., /health, /ready)\"","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Scaling requirements","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"Should autoscaling be enabled? If yes, min/max replicas and target CPU%?\"","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Workload type","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"What workload type: Deployment, StatefulSet, or DaemonSet?\"","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Storage requirements","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"Does the application need persistent storage? Size and access mode?\"","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Do not silently assume critical settings.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Stage 2: Lookup CRD Documentation (Only if CRDs Are In Scope)","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Try Context7 first:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"mcp__context7__resolve-library-id","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"mcp__context7__query-docs","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Fallback chain if Context7 is unavailable or incomplete:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Operator official docs (preferred)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"General web search","type":"text"}]}]}]}]}]},{"type":"paragraph","content":[{"text":"Also consult ","type":"text"},{"text":"references/crd_patterns.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" for example patterns.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Stage 3: Scaffold Chart Structure (Required)","type":"text"}]},{"type":"paragraph","content":[{"text":"Run:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash scripts/generate_chart_structure.sh \u003cchart-name> \u003coutput-directory> [options]","type":"text"}]},{"type":"paragraph","content":[{"text":"Options:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--image \u003crepo>","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Supports repo-only, tagged image, registry ports, and digest refs","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--port \u003cnumber>","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Service port (default: 80)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--target-port \u003cnumber>","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Container target port (default: 8080)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--type \u003ctype>","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Workload type: deployment, statefulset, daemonset (default: deployment)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--with-templates","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Generate resource templates (deployment.yaml, service.yaml, etc.)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--with-ingress","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Include ingress template","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--with-hpa","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Include HPA template","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--force","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Overwrite existing chart without prompting","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Image parsing behavior:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--image nginx:1.27","type":"text","marks":[{"type":"code_inline"}]},{"text":" -> repository ","type":"text"},{"text":"nginx","type":"text","marks":[{"type":"code_inline"}]},{"text":", tag ","type":"text"},{"text":"1.27","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--image registry.local:5000/team/app","type":"text","marks":[{"type":"code_inline"}]},{"text":" -> repository kept intact","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--image ghcr.io/org/app@sha256:...","type":"text","marks":[{"type":"code_inline"}]},{"text":" -> digest mode (no tag concatenation)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--tag","type":"text","marks":[{"type":"code_inline"}]},{"text":" cannot be combined with digest image references","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Idempotency and overwrite behavior:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"generate_chart_structure.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":": prompts before overwrite; ","type":"text"},{"text":"--force","type":"text","marks":[{"type":"code_inline"}]},{"text":" overwrites non-interactively.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"generate_standard_helpers.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":": prompts before replacing ","type":"text"},{"text":"templates/_helpers.tpl","type":"text","marks":[{"type":"code_inline"}]},{"text":"; ","type":"text"},{"text":"--force","type":"text","marks":[{"type":"code_inline"}]},{"text":" bypasses prompt.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Expected scaffold shape:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"mychart/\n Chart.yaml\n values.yaml\n templates/\n _helpers.tpl\n NOTES.txt\n serviceaccount.yaml\n service.yaml\n configmap.yaml\n secret.yaml\n deployment.yaml|statefulset.yaml|daemonset.yaml\n ingress.yaml (optional)\n hpa.yaml (optional)\n .helmignore","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Stage 4: Generate Standard Helpers","type":"text"}]},{"type":"paragraph","content":[{"text":"Run:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash scripts/generate_standard_helpers.sh \u003cchart-name> \u003cchart-directory>","type":"text"}]},{"type":"paragraph","content":[{"text":"Required helpers: ","type":"text"},{"text":"name","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"fullname","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"chart","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"labels","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"selectorLabels","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"serviceAccountName","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"Fallback:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If script execution is blocked, copy ","type":"text"},{"text":"assets/_helpers-template.tpl","type":"text","marks":[{"type":"code_inline"}]},{"text":" and replace ","type":"text"},{"text":"CHARTNAME","type":"text","marks":[{"type":"code_inline"}]},{"text":" with the chart name.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Stage 5: Consult References and Generate Templates (Required)","type":"text"}]},{"type":"paragraph","content":[{"text":"Consult relevant references once at this stage:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/resource_templates.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" for the resource patterns being generated","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/helm_template_functions.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" for templating function usage","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/crd_patterns.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" only when CRDs are in scope","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Example file-open commands:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"sed -n '1,220p' references/resource_templates.md\nsed -n '1,220p' references/helm_template_functions.md","type":"text"}]},{"type":"paragraph","content":[{"text":"Resource coverage from ","type":"text"},{"text":"references/resource_templates.md","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Workloads: Deployment, StatefulSet, DaemonSet, Job, CronJob","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Services: Service, Ingress","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Config: ConfigMap, Secret","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"RBAC: ServiceAccount, Role, RoleBinding, ClusterRole, ClusterRoleBinding","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Network: NetworkPolicy","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Autoscaling: HPA, PodDisruptionBudget","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Required template patterns:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"yaml"},"content":[{"text":"metadata:\n name: {{ include \"mychart.fullname\" . }}\n labels: {{- include \"mychart.labels\" . | nindent 4 }}\n\n{{- with .Values.nodeSelector }}\nnodeSelector: {{- toYaml . | nindent 2 }}\n{{- end }}\n\nannotations:\n {{- if and .Values.configMap .Values.configMap.enabled }}\n checksum/config: {{ include (print $.Template.BasePath \"/configmap.yaml\") . | sha256sum }}\n {{- end }}","type":"text"}]},{"type":"paragraph","content":[{"text":"Checksum annotations are required for workloads, but must be conditional and only reference generated templates (","type":"text"},{"text":"configmap.yaml","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"secret.yaml","type":"text","marks":[{"type":"code_inline"}]},{"text":").","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Stage 6: Create values.yaml","type":"text"}]},{"type":"paragraph","content":[{"text":"Structure guidelines:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Group related settings logically","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Document every value with ","type":"text"},{"text":"# --","type":"text","marks":[{"type":"code_inline"}]},{"text":" comments","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Provide sensible defaults","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Include security contexts, resource limits, probes","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Keep ","type":"text"},{"text":"service.port","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"service.targetPort","type":"text","marks":[{"type":"code_inline"}]},{"text":" separate and explicit","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Keep ","type":"text"},{"text":"configMap.enabled","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"secret.enabled","type":"text","marks":[{"type":"code_inline"}]},{"text":" aligned with generated templates","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"assets/values-schema-template.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" for JSON Schema validation.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Stage 7: Validate","type":"text"}]},{"type":"paragraph","content":[{"text":"Preferred path: run the ","type":"text"},{"text":"helm-validator","type":"text","marks":[{"type":"code_inline"}]},{"text":" skill.","type":"text"}]},{"type":"paragraph","content":[{"text":"If skill invocation is unavailable, run local commands directly:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"helm lint \u003cchart-dir>\nhelm template test \u003cchart-dir>","type":"text"}]},{"type":"paragraph","content":[{"text":"If ","type":"text"},{"text":"helm","type":"text","marks":[{"type":"code_inline"}]},{"text":" is unavailable, report the block clearly and perform partial checks:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"bash -n scripts/generate_chart_structure.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"bash -n scripts/generate_standard_helpers.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Verify generated files and key fields manually","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Re-run validation after any fixes.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Template Functions Quick Reference","type":"text"}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"references/helm_template_functions.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" for complete guide.","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Function","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Purpose","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Example","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"required","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Enforce required values","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"{{ required \"msg\" .Values.x }}","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"default","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Fallback value","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"{{ .Values.x | default 1 }}","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"quote","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Quote strings","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"{{ .Values.x | quote }}","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"include","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Use helpers","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"{{ include \"name\" . | nindent 4 }}","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"toYaml","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Convert to YAML","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"{{ toYaml .Values.x | nindent 2 }}","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"tpl","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Render as template","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"{{ tpl .Values.config . }}","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"nindent","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Newline + indent","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"{{- include \"x\" . | nindent 4 }}","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Working with CRDs","type":"text"}]},{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"references/crd_patterns.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" for complete examples.","type":"text"}]},{"type":"paragraph","content":[{"text":"Key points:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"CRDs you ship -> ","type":"text"},{"text":"crds/","type":"text","marks":[{"type":"code_inline"}]},{"text":" directory (not templated, not deleted on uninstall)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"CR instances -> ","type":"text"},{"text":"templates/","type":"text","marks":[{"type":"code_inline"}]},{"text":" directory (fully templated)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Always look up documentation for CRD spec requirements","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Document operator dependencies in Chart.yaml annotations","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Converting Manifests to Helm","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Parameterize:","type":"text","marks":[{"type":"strong"}]},{"text":" Names -> helpers, values -> ","type":"text"},{"text":"values.yaml","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Apply patterns:","type":"text","marks":[{"type":"strong"}]},{"text":" Labels, conditionals, ","type":"text"},{"text":"toYaml","type":"text","marks":[{"type":"code_inline"}]},{"text":" for complex objects","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Add helpers:","type":"text","marks":[{"type":"strong"}]},{"text":" Create ","type":"text"},{"text":"_helpers.tpl","type":"text","marks":[{"type":"code_inline"}]},{"text":" with standard helpers","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Validate:","type":"text","marks":[{"type":"strong"}]},{"text":" Run ","type":"text"},{"text":"helm-validator","type":"text","marks":[{"type":"code_inline"}]},{"text":" (or local lint/template fallback), then test with different values","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Error Handling","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Issue","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Solution","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Template syntax errors","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"helm template test \u003cchart-dir> --debug --show-only templates/\u003cfile>.yaml","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Undefined values","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"helm lint \u003cchart-dir> --strict","type":"text","marks":[{"type":"code_inline"}]},{"text":" and add ","type":"text"},{"text":"default","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"required","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Checksum include errors","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Ensure ","type":"text"},{"text":"templates/configmap.yaml","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"templates/secret.yaml","type":"text","marks":[{"type":"code_inline"}]},{"text":" exist and ","type":"text"},{"text":"configMap.enabled","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"secret.enabled","type":"text","marks":[{"type":"code_inline"}]},{"text":" are set correctly","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Port mismatch (Service vs container)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Set both ","type":"text"},{"text":"service.port","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"service.targetPort","type":"text","marks":[{"type":"code_inline"}]},{"text":", then re-run ","type":"text"},{"text":"helm template test \u003cchart-dir>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"CRD validation fails","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Verify apiVersion/spec fields with Context7 or operator docs, then re-render","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Script argument failures","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Run ","type":"text"},{"text":"bash scripts/generate_chart_structure.sh --help","type":"text","marks":[{"type":"code_inline"}]},{"text":" and pass required values for option flags","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Example Flows","type":"text"}]},{"type":"paragraph","content":[{"text":"Full scaffold with templates, ingress, HPA, and explicit port mapping:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash scripts/generate_chart_structure.sh webapp ./charts \\\n --image ghcr.io/acme/webapp:2.3.1 \\\n --port 80 \\\n --target-port 8080 \\\n --type deployment \\\n --with-templates \\\n --with-ingress \\\n --with-hpa","type":"text"}]},{"type":"paragraph","content":[{"text":"Digest-based image scaffold:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash scripts/generate_chart_structure.sh api ./charts \\\n --image ghcr.io/acme/api@sha256:0123456789abcdef \\\n --with-templates","type":"text"}]},{"type":"paragraph","content":[{"text":"Minimal scaffold without templates:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash scripts/generate_chart_structure.sh starter ./charts","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Scaffold Success Criteria","type":"text"}]},{"type":"paragraph","content":[{"text":"Mark complete only when all checks pass:","type":"text"}]},{"type":"checkbox_list","attrs":{"id":null},"content":[{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Chart.yaml","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"values.yaml","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":".helmignore","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"templates/NOTES.txt","type":"text","marks":[{"type":"code_inline"}]},{"text":", and ","type":"text"},{"text":"templates/_helpers.tpl","type":"text","marks":[{"type":"code_inline"}]},{"text":" exist","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"values.yaml","type":"text","marks":[{"type":"code_inline"}]},{"text":" contains explicit ","type":"text"},{"text":"service.port","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"service.targetPort","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"If ","type":"text"},{"text":"--with-templates","type":"text","marks":[{"type":"code_inline"}]},{"text":" was used, ","type":"text"},{"text":"serviceaccount.yaml","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"service.yaml","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"configmap.yaml","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"secret.yaml","type":"text","marks":[{"type":"code_inline"}]},{"text":", and one workload template exist","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Generated workload template uses conditional checksum annotations for config/secret","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Image rendering logic supports tag and digest modes","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Validation completed (","type":"text"},{"text":"helm-validator","type":"text","marks":[{"type":"code_inline"}]},{"text":" skill or local fallback commands) and outcomes reported","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Resources","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Scripts","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Script","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Usage","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"scripts/generate_chart_structure.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"bash scripts/generate_chart_structure.sh \u003cchart-name> \u003coutput-dir> [options]","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"scripts/generate_standard_helpers.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"bash scripts/generate_standard_helpers.sh \u003cchart-name> \u003cchart-dir> [--force]","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"References","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"File","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Content","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/helm_template_functions.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Complete template function guide","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/resource_templates.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"All K8s resource templates","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/crd_patterns.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"CRD patterns (cert-manager, Prometheus, Istio, ArgoCD)","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Assets","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"File","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Purpose","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"assets/_helpers-template.tpl","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Standard helpers template","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"assets/values-schema-template.json","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"JSON Schema for values validation","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Integration with helm-validator","type":"text"}]},{"type":"paragraph","content":[{"text":"After generating charts, invoke ","type":"text"},{"text":"helm-validator","type":"text","marks":[{"type":"code_inline"}]},{"text":" and close the loop:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Generate chart/templates","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Run ","type":"text"},{"text":"helm-validator","type":"text","marks":[{"type":"code_inline"}]},{"text":" (or local fallback commands)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Fix identified issues","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Re-validate until passing","type":"text"}]}]}]}]},"metadata":{"date":"2026-11-02","name":"helm-generator","author":"@skillopedia","source":{"stars":331,"repo_name":"cc-devops-skills","origin_url":"https://github.com/akin-ozer/cc-devops-skills/blob/HEAD/devops-skills-plugin/skills/helm-generator/SKILL.md","repo_owner":"akin-ozer","body_sha256":"71e7bf30488a8712faaf8e0b9576f349dafd3b56b4cdd5e776691f73a65f6df0","cluster_key":"9ec01990a6a51a089d1368edfc588ff61b700bc9e5212f63f15b3eecd2c93314","clean_bundle":{"format":"clean-skill-bundle-v1","source":"akin-ozer/cc-devops-skills/devops-skills-plugin/skills/helm-generator/SKILL.md","text_bytes":173113,"source_commit":"276af751e659315aaf56d3ad13d7c26f4e72e28a","skills_sh_rank":16850,"attachment_count":15,"text_attachments":15,"attachment_storage":"skill_attachments","binary_attachments":0},"cluster_size":1,"skill_md_path":"devops-skills-plugin/skills/helm-generator/SKILL.md","import_metadata":{"date":"2026-11-02","author":"@skillopedia","version":"v1","category":"devops-infrastructure","category_label":"DevOps"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"devops-infrastructure","import_tag":"skills-sh-top20k-2026-11","description":"Create, scaffold, or generate Helm charts, Chart.yaml, values.yaml, templates, helpers."}},"renderedAt":1793661425867}

Helm Chart Generator Overview Generate production-ready Helm charts with deterministic scaffolding, standard helpers, reusable templates, and validation loops. Official Documentation: - Helm Docs - Main documentation - Chart Best Practices - Official best practices guide - Template Functions - Built-in functions - Sprig Functions - Extended function library When to Use This Skill | Use helm-generator | Use OTHER skill | |-------------------|-----------------| | Create new Helm charts | helm-validator : Validate/lint existing charts | | Generate Helm templates | k8s-yaml-generator : Raw K8s YA…