//! Hero Runner - Command Execution Runner //! //! This runner executes commands from job payloads. //! Unlike script-based runners, it directly executes commands from the job payload. use hero_runner::runner_trait::spawn_runner; use clap::Parser; use log::info; use tokio::sync::mpsc; use std::sync::Arc; mod executor; use executor::HeroExecutor; #[derive(Parser, Debug)] #[command(author, version, about = "Hero Runner - Command execution runner", long_about = None)] struct Args { /// Runner ID runner_id: String, /// Redis URL #[arg(short = 'r', long, default_value = "redis://localhost:6379")] redis_url: String, } #[tokio::main] async fn main() -> Result<(), Box> { // Initialize logging env_logger::init(); let args = Args::parse(); info!("Starting Hero Command Runner with ID: {}", args.runner_id); info!("Redis URL: {}", args.redis_url); // Create shutdown channel let (shutdown_tx, shutdown_rx) = mpsc::channel::<()>(1); // Setup signal handling for graceful shutdown let shutdown_tx_clone = shutdown_tx.clone(); tokio::spawn(async move { tokio::signal::ctrl_c().await.expect("Failed to listen for ctrl+c"); info!("Received Ctrl+C, initiating shutdown..."); let _ = shutdown_tx_clone.send(()).await; }); // Create executor let executor = HeroExecutor::new( args.runner_id.clone(), args.redis_url.clone(), ); // Wrap in Arc for the runner trait let executor = Arc::new(executor); // Spawn the runner using the trait method let runner_handle = spawn_runner(executor, shutdown_rx); info!("Hero runner '{}' is now running", args.runner_id); // Wait for runner to finish (shutdown is handled by the runner itself) runner_handle.await??; info!("Hero runner '{}' shutdown complete", args.runner_id); Ok(()) }