#!/usr/bin/env python3 """ Helm Chart Scaffolding Script Generates production-ready Helm charts with best practices """ import os import shutil import argparse from pathlib import Path from typing import Dict, List, Optional def replace_placeholder(content: str, chart_name: str) -> str: """Replace CHARTNAME placeholder with actual chart name""" return content.replace("CHARTNAME", chart_name) def create_chart_structure( chart_name: str, output_dir: str, workload_type: str = "deployment", include_ingress: bool = False, include_hpa: bool = False, include_configmap: bool = False, ) -> None: """ Create Helm chart directory structure and files Args: chart_name: Name of the Helm chart output_dir: Directory where chart will be created workload_type: Type of workload (deployment, statefulset, job, cronjob) include_ingress: Whether to include Ingress resource include_hpa: Whether to include HorizontalPodAutoscaler include_configmap: Whether to include ConfigMap """ # Get templates directory script_dir = Path(__file__).parent.parent templates_dir = script_dir / "assets" / "templates" # Create chart directory chart_dir = Path(output_dir) / chart_name chart_dir.mkdir(parents=True, exist_ok=True) # Create templates subdirectory templates_output_dir = chart_dir / "templates" templates_output_dir.mkdir(exist_ok=True) print(f"šŸ“¦ Creating Helm chart: {chart_name}") print(f"šŸ“‚ Output directory: {chart_dir}") # Copy Chart.yaml chart_yaml_src = templates_dir / "Chart.yaml" chart_yaml_dst = chart_dir / "Chart.yaml" with open(chart_yaml_src, 'r') as f: content = replace_placeholder(f.read(), chart_name) with open(chart_yaml_dst, 'w') as f: f.write(content) print("āœ… Created Chart.yaml") # Copy values.yaml values_yaml_src = templates_dir / "values.yaml" values_yaml_dst = chart_dir / "values.yaml" shutil.copy2(values_yaml_src, values_yaml_dst) print("āœ… Created values.yaml") # Copy .helmignore helmignore_src = templates_dir / ".helmignore" helmignore_dst = chart_dir / ".helmignore" shutil.copy2(helmignore_src, helmignore_dst) print("āœ… Created .helmignore") # Copy _helpers.tpl helpers_src = templates_dir / "_helpers.tpl" helpers_dst = templates_output_dir / "_helpers.tpl" with open(helpers_src, 'r') as f: content = replace_placeholder(f.read(), chart_name) with open(helpers_dst, 'w') as f: f.write(content) print("āœ… Created templates/_helpers.tpl") # Copy NOTES.txt notes_src = templates_dir / "NOTES.txt" notes_dst = templates_output_dir / "NOTES.txt" with open(notes_src, 'r') as f: content = replace_placeholder(f.read(), chart_name) with open(notes_dst, 'w') as f: f.write(content) print("āœ… Created templates/NOTES.txt") # Copy workload type template workload_templates = { "deployment": "deployment/deployment.yaml", "statefulset": "statefulset/statefulset.yaml", "job": "job/job.yaml", "cronjob": "cronjob/cronjob.yaml", } workload_src = templates_dir / workload_templates[workload_type] workload_dst = templates_output_dir / f"{workload_type}.yaml" with open(workload_src, 'r') as f: content = replace_placeholder(f.read(), chart_name) with open(workload_dst, 'w') as f: f.write(content) print(f"āœ… Created templates/{workload_type}.yaml") # Copy Service (not for jobs) if workload_type in ["deployment", "statefulset"]: service_src = templates_dir / "service" / "service.yaml" service_dst = templates_output_dir / "service.yaml" with open(service_src, 'r') as f: content = replace_placeholder(f.read(), chart_name) with open(service_dst, 'w') as f: f.write(content) print("āœ… Created templates/service.yaml") # Copy ServiceAccount sa_src = templates_dir / "rbac" / "serviceaccount.yaml" sa_dst = templates_output_dir / "serviceaccount.yaml" with open(sa_src, 'r') as f: content = replace_placeholder(f.read(), chart_name) with open(sa_dst, 'w') as f: f.write(content) print("āœ… Created templates/serviceaccount.yaml") # Optionally copy Ingress if include_ingress: ingress_src = templates_dir / "ingress" / "ingress.yaml" ingress_dst = templates_output_dir / "ingress.yaml" with open(ingress_src, 'r') as f: content = replace_placeholder(f.read(), chart_name) with open(ingress_dst, 'w') as f: f.write(content) print("āœ… Created templates/ingress.yaml") # Optionally copy HPA (only for deployment) if include_hpa and workload_type == "deployment": hpa_src = templates_dir / "hpa" / "hpa.yaml" hpa_dst = templates_output_dir / "hpa.yaml" with open(hpa_src, 'r') as f: content = replace_placeholder(f.read(), chart_name) with open(hpa_dst, 'w') as f: f.write(content) print("āœ… Created templates/hpa.yaml") # Optionally copy ConfigMap if include_configmap: cm_src = templates_dir / "configmap" / "configmap.yaml" cm_dst = templates_output_dir / "configmap.yaml" with open(cm_src, 'r') as f: content = replace_placeholder(f.read(), chart_name) with open(cm_dst, 'w') as f: f.write(content) print("āœ… Created templates/configmap.yaml") print(f"\nšŸŽ‰ Chart '{chart_name}' created successfully at {chart_dir}") print(f"\nNext steps:") print(f"1. cd {chart_dir}") print(f"2. Edit values.yaml to configure your application") print(f"3. Run: helm lint .") print(f"4. Run: helm install {chart_name} .") def main(): parser = argparse.ArgumentParser( description="Generate production-ready Helm charts", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Examples: # Create basic deployment chart %(prog)s my-app -o ./charts # Create statefulset with ingress %(prog)s my-db -o ./charts -t statefulset --ingress # Create job with configmap %(prog)s my-job -o ./charts -t job --configmap # Create deployment with HPA %(prog)s my-api -o ./charts --hpa """ ) parser.add_argument( "chart_name", help="Name of the Helm chart to create" ) parser.add_argument( "-o", "--output", default=".", help="Output directory (default: current directory)" ) parser.add_argument( "-t", "--type", choices=["deployment", "statefulset", "job", "cronjob"], default="deployment", help="Workload type (default: deployment)" ) parser.add_argument( "--ingress", action="store_true", help="Include Ingress resource" ) parser.add_argument( "--hpa", action="store_true", help="Include HorizontalPodAutoscaler (deployment only)" ) parser.add_argument( "--configmap", action="store_true", help="Include ConfigMap resource" ) args = parser.parse_args() create_chart_structure( chart_name=args.chart_name, output_dir=args.output, workload_type=args.type, include_ingress=args.ingress, include_hpa=args.hpa, include_configmap=args.configmap, ) if __name__ == "__main__": main()