syncable_cli/analyzer/kubelint/parser/
helm.rs1use crate::analyzer::kubelint::context::Object;
4use crate::analyzer::kubelint::parser::yaml;
5use std::path::Path;
6use std::process::Command;
7
8pub fn render_helm_chart(
13 chart_path: &Path,
14 values: Option<&Path>,
15) -> Result<Vec<Object>, HelmError> {
16 if !is_helm_available() {
18 return Err(HelmError::HelmNotFound);
19 }
20
21 let mut cmd = Command::new("helm");
23 cmd.arg("template")
24 .arg("release-name") .arg(chart_path);
26
27 if let Some(values_path) = values {
29 cmd.arg("-f").arg(values_path);
30 }
31
32 let output = cmd
34 .output()
35 .map_err(|e| HelmError::RenderError(e.to_string()))?;
36
37 if !output.status.success() {
38 let stderr = String::from_utf8_lossy(&output.stderr);
39 return Err(HelmError::RenderError(stderr.to_string()));
40 }
41
42 let yaml_content = String::from_utf8_lossy(&output.stdout);
44 yaml::parse_yaml_with_path(&yaml_content, chart_path)
45 .map_err(|e| HelmError::RenderError(e.to_string()))
46}
47
48pub fn render_helm_chart_with_values(
50 chart_path: &Path,
51 values_files: &[&Path],
52 set_values: &[(&str, &str)],
53) -> Result<Vec<Object>, HelmError> {
54 if !is_helm_available() {
55 return Err(HelmError::HelmNotFound);
56 }
57
58 let mut cmd = Command::new("helm");
59 cmd.arg("template").arg("release-name").arg(chart_path);
60
61 for values_path in values_files {
63 cmd.arg("-f").arg(values_path);
64 }
65
66 for (key, value) in set_values {
68 cmd.arg("--set").arg(format!("{}={}", key, value));
69 }
70
71 let output = cmd
72 .output()
73 .map_err(|e| HelmError::RenderError(e.to_string()))?;
74
75 if !output.status.success() {
76 let stderr = String::from_utf8_lossy(&output.stderr);
77 return Err(HelmError::RenderError(stderr.to_string()));
78 }
79
80 let yaml_content = String::from_utf8_lossy(&output.stdout);
81 yaml::parse_yaml_with_path(&yaml_content, chart_path)
82 .map_err(|e| HelmError::RenderError(e.to_string()))
83}
84
85pub fn is_helm_chart(path: &Path) -> bool {
87 path.join("Chart.yaml").exists() || path.join("Chart.yml").exists()
88}
89
90pub fn is_helm_available() -> bool {
92 Command::new("helm")
93 .arg("version")
94 .arg("--short")
95 .output()
96 .map(|o| o.status.success())
97 .unwrap_or(false)
98}
99
100pub fn helm_version() -> Option<String> {
102 Command::new("helm")
103 .arg("version")
104 .arg("--short")
105 .output()
106 .ok()
107 .filter(|o| o.status.success())
108 .map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
109}
110
111#[derive(Debug, Clone)]
113pub enum HelmError {
114 HelmNotFound,
116 ChartError(String),
118 RenderError(String),
120}
121
122impl std::fmt::Display for HelmError {
123 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124 match self {
125 Self::HelmNotFound => write!(f, "helm binary not found in PATH"),
126 Self::ChartError(msg) => write!(f, "Chart error: {}", msg),
127 Self::RenderError(msg) => write!(f, "Render error: {}", msg),
128 }
129 }
130}
131
132impl std::error::Error for HelmError {}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 #[test]
139 fn test_is_helm_chart_detection() {
140 let temp_dir = std::env::temp_dir();
142 assert!(!is_helm_chart(&temp_dir)); }
144
145 #[test]
146 fn test_helm_availability() {
147 let _available = is_helm_available();
149 }
150}