use std::{
env, fs,
path::{Path, PathBuf},
};
use kora_lib::Config;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let current_dir = env::current_dir()?;
let project_root = find_project_root(¤t_dir)?;
let config_path = project_root.join("kora.toml");
if !config_path.exists() {
eprintln!("Error: kora.toml not found at {config_path:?}");
eprintln!("Please ensure you're in a Kora project directory");
std::process::exit(1);
}
let config = Config::load_config(&config_path)?;
let metrics = &config.metrics;
println!("Reading configuration from kora.toml:");
println!(" Enabled: {}", metrics.enabled);
println!(" Endpoint: {}", metrics.endpoint);
println!(" Port: {}", metrics.port);
println!(" Scrape Interval: {}s", metrics.scrape_interval);
println!();
let metrics_dir = project_root.join("crates/lib/src/metrics");
let mut updated_files = 0;
if update_prometheus_yml(
&metrics_dir,
metrics.port,
&metrics.endpoint,
metrics.scrape_interval,
)? {
updated_files += 1;
}
if update_docker_compose(&metrics_dir)? {
updated_files += 1;
}
if update_grafana_datasources(&metrics_dir)? {
updated_files += 1;
}
if updated_files > 0 {
println!("✅ Updated {updated_files} configuration file(s)");
} else {
println!("ℹ️ All configuration files are already up to date");
}
Ok(())
}
fn find_project_root(start_dir: &Path) -> Result<PathBuf, Box<dyn std::error::Error>> {
let mut current = start_dir.to_path_buf();
loop {
if current.join("kora.toml").exists() {
return Ok(current);
}
let cargo_toml = current.join("Cargo.toml");
if cargo_toml.exists() {
let content = fs::read_to_string(&cargo_toml)?;
if content.contains("[workspace]") {
return Ok(current);
}
}
if !current.pop() {
return Err(
"Could not find project root (no kora.toml or workspace Cargo.toml found)".into()
);
}
}
}
fn update_prometheus_yml(
metrics_dir: &Path,
port: u16,
endpoint: &str,
scrape_interval: u64,
) -> Result<bool, Box<dyn std::error::Error>> {
let file_path = metrics_dir.join("prometheus.yml");
if !file_path.exists() {
println!("⚠️ prometheus.yml not found at {}", file_path.display());
return Ok(false);
}
println!("Updating prometheus.yml...");
let content = fs::read_to_string(&file_path)?;
let mut updated_content = content.clone();
let mut changes_made = false;
if let Ok(regex) = regex::Regex::new(r"scrape_interval:\s*\d+s") {
let new_content =
regex.replace_all(&updated_content, &format!("scrape_interval: {scrape_interval}s"));
if new_content != updated_content {
updated_content = new_content.to_string();
changes_made = true;
}
}
if let Ok(regex) = regex::Regex::new(r"evaluation_interval:\s*\d+s") {
let new_content = regex
.replace_all(&updated_content, &format!("evaluation_interval: {scrape_interval}s"));
if new_content != updated_content {
updated_content = new_content.to_string();
changes_made = true;
}
}
if let Ok(regex) = regex::Regex::new(r#""(kora|host\.docker\.internal):\d+""#) {
let new_content =
regex.replace_all(&updated_content, &format!("\"host.docker.internal:{port}\""));
if new_content != updated_content {
updated_content = new_content.to_string();
changes_made = true;
}
}
if let Ok(regex) = regex::Regex::new(r#"metrics_path:\s*"[^"]*""#) {
let new_content =
regex.replace_all(&updated_content, &format!("metrics_path: \"{endpoint}\""));
if new_content != updated_content {
updated_content = new_content.to_string();
changes_made = true;
}
}
if changes_made {
fs::write(&file_path, updated_content)?;
println!(" ✓ Updated Kora target: host.docker.internal:{port}");
println!(" ✓ Updated endpoint: {endpoint}");
println!(" ✓ Updated intervals: {scrape_interval}s");
} else {
println!(" ℹ️ Already up to date");
}
Ok(changes_made)
}
fn update_docker_compose(metrics_dir: &Path) -> Result<bool, Box<dyn std::error::Error>> {
let file_path = metrics_dir.join("docker-compose.metrics.yml");
if !file_path.exists() {
println!("⚠️ docker-compose.metrics.yml not found at {}", file_path.display());
return Ok(false);
}
println!("docker-compose.metrics.yml exists (no updates needed)");
Ok(false)
}
fn update_grafana_datasources(metrics_dir: &Path) -> Result<bool, Box<dyn std::error::Error>> {
let file_path = metrics_dir.join("grafana/provisioning/datasources/datasources.yml");
if !file_path.exists() {
println!("⚠️ Grafana datasources.yml not found at {}", file_path.display());
return Ok(false);
}
println!("Updating Grafana datasources.yml...");
let content = fs::read_to_string(&file_path)?;
let mut updated_content = content.clone();
let mut changes_made = false;
if let Ok(regex) = regex::Regex::new(r"url:\s*http://[^:\s]+:9090") {
let new_content = regex.replace_all(&updated_content, "url: http://prometheus:9090");
if new_content != updated_content {
updated_content = new_content.to_string();
changes_made = true;
}
}
if changes_made {
fs::write(&file_path, updated_content)?;
println!(" ✓ Updated Prometheus URL to http://prometheus:9090");
} else {
println!(" ℹ️ Already up to date");
}
Ok(changes_made)
}