pub mod app;
pub mod commands;
pub mod error;
pub mod features;
pub mod handlers;
pub mod router;
pub use handlers::*;
use crate::cli::app::AppContext;
use crate::cli::error::CliResult;
use crate::commands::curl;
use crate::commands::generate_systemd::{run_generate_systemd, GenerateSystemdArgs};
use crate::commands::redeploy_v2::run_redeploy_v2;
use crate::commands::{
install_package, list_services, open_global_config, run_config, run_config_secret_delete,
run_config_secret_set, run_config_secret_show, run_init, run_login, run_redeploy,
run_redeploy_service, run_service_command, run_setup, run_version_command,
run_version_release_command, show_service_help, VersionReleaseOptions,
};
use crate::commands::{run_diag, run_nginx};
use crate::config::sync_versioning_files_registry;
use crate::logging::{init_logger, log_error, log_info, log_success, log_warn};
use clap::Parser;
use commands::Cli;
pub async fn run() -> CliResult<()> {
let cli: Cli = Cli::parse();
let debug: bool = cli.debug;
if let Err(e) = init_logger(debug).await {
let _ = log_error(
"system",
"Failed to initialize logger",
Some(&e.to_string()),
)
.await;
}
if let Err(e) = sync_versioning_files_registry() {
let _ = log_warn("config", "Failed to sync versioning registry", Some(&e)).await;
}
let mut ctx = AppContext::new(debug);
router::dispatch(cli, &mut ctx).await
}
pub(super) async fn handle_init(debug: bool) -> CliResult<()> {
if let Err(e) = run_init(debug).await {
let _ = log_error("init", "Init failed", Some(&e)).await;
return Err(e.into());
}
Ok(())
}
pub(super) async fn handle_setup(debug: bool) -> CliResult<()> {
if let Err(e) = run_setup(debug).await {
let _ = log_error("setup", "Setup failed", Some(&e)).await;
}
Ok(())
}
pub(super) async fn handle_redeploy(service_name: Option<String>, debug: bool) -> CliResult<()> {
if let Some(name) = service_name {
if let Err(e) = run_redeploy_service(&name, debug).await {
let _ = log_error("redeploy", "Service redeploy failed", Some(&e)).await;
}
} else if let Err(e) = run_redeploy().await {
let _ = log_error("redeploy", "Redeploy failed", Some(&e)).await;
}
Ok(())
}
pub(super) async fn handle_redeploy_v2(cmd: commands::RedeployV2Cmd, debug: bool) -> CliResult<()> {
let _ = log_info("redeploy_v2", "Starting remote redeploy process", None).await;
match run_redeploy_v2(cmd.password, cmd.username, cmd.host, cmd.project_dir, debug).await {
Ok(()) => Ok(()),
Err(e) => {
let _ = log_error("redeploy_v2", "Remote redeploy failed", Some(&e)).await;
Err(e.into())
}
}
}
pub(super) async fn handle_config(cmd: commands::ConfigCmd, debug: bool) -> CliResult<()> {
let commands::ConfigCmd {
project,
no_open,
provider,
} = cmd;
if provider.is_some() && (project || no_open) {
return Err(
"`xbp config <provider> ...` cannot be combined with `--project` or `--no-open`."
.into(),
);
}
if let Some(provider_cmd) = provider {
match provider_cmd {
commands::ConfigProviderCmd::Openrouter(subcmd) => match subcmd.action {
commands::ConfigSecretAction::SetKey { key } => {
if let Err(e) = run_config_secret_set("openrouter", key).await {
let _ = log_error("config", "Failed to set OpenRouter key", Some(&e)).await;
return Err(e.into());
}
}
commands::ConfigSecretAction::DeleteKey => {
if let Err(e) = run_config_secret_delete("openrouter").await {
let _ =
log_error("config", "Failed to delete OpenRouter key", Some(&e)).await;
return Err(e.into());
}
}
commands::ConfigSecretAction::Show { raw } => {
if let Err(e) = run_config_secret_show("openrouter", raw).await {
let _ =
log_error("config", "Failed to show OpenRouter key", Some(&e)).await;
return Err(e.into());
}
}
},
commands::ConfigProviderCmd::Github(subcmd) => match subcmd.action {
commands::ConfigSecretAction::SetKey { key } => {
if let Err(e) = run_config_secret_set("github", key).await {
let _ = log_error("config", "Failed to set GitHub token", Some(&e)).await;
return Err(e.into());
}
}
commands::ConfigSecretAction::DeleteKey => {
if let Err(e) = run_config_secret_delete("github").await {
let _ =
log_error("config", "Failed to delete GitHub token", Some(&e)).await;
return Err(e.into());
}
}
commands::ConfigSecretAction::Show { raw } => {
if let Err(e) = run_config_secret_show("github", raw).await {
let _ = log_error("config", "Failed to show GitHub token", Some(&e)).await;
return Err(e.into());
}
}
},
}
} else if project {
let _ = run_config(debug).await;
} else if let Err(e) = open_global_config(no_open).await {
let _ = log_error("config", "Failed to open global config", Some(&e)).await;
}
Ok(())
}
pub(super) async fn handle_install(package: String, debug: bool) -> CliResult<()> {
if package.is_empty() || package == "--help" || package == "help" {
return install_package("", debug).await.map_err(Into::into);
}
let install_msg: String = format!("Installing package: {}", package);
let _ = log_info("install", &install_msg, None).await;
match install_package(&package, debug).await {
Ok(()) => {
let success_msg = format!("Successfully installed: {}", package);
let _ = log_success("install", &success_msg, None).await;
Ok(())
}
Err(e) => Err(e.into()),
}
}
pub(super) async fn handle_curl(cmd: commands::CurlCmd, debug: bool) -> CliResult<()> {
let url = cmd
.url
.unwrap_or_else(|| "https://example.com/api".to_string());
if let Err(e) = curl::run_curl(&url, cmd.no_timeout, debug).await {
let _ = log_error("curl", "Curl command failed", Some(&e)).await;
}
Ok(())
}
pub(super) async fn handle_services(debug: bool) -> CliResult<()> {
if let Err(e) = list_services(debug).await {
let _ = log_error("services", "Failed to list services", Some(&e)).await;
}
Ok(())
}
pub(super) async fn handle_service(
command: Option<String>,
service_name: Option<String>,
debug: bool,
) -> CliResult<()> {
if let Some(cmd) = command {
if cmd == "--help" || cmd == "help" {
if let Some(name) = service_name {
if let Err(e) = show_service_help(&name).await {
let _ = log_error("service", "Failed to show service help", Some(&e)).await;
}
} else {
println!("Usage: xbp service <command> <service-name>");
println!("Commands: build, install, start, dev");
println!("Example: xbp service build zeus");
println!("For help on a specific service: xbp service --help <service-name>");
}
} else if let Some(name) = service_name {
if let Err(e) = run_service_command(&cmd, &name, debug).await {
let _ = log_error(
"service",
&format!("Service command '{}' failed", cmd),
Some(&e),
)
.await;
}
} else {
let _ = log_error("service", "Service name required", None).await;
}
} else {
println!("Usage: xbp service <command> <service-name>");
println!("Commands: build, install, start, dev");
println!("Example: xbp service build zeus");
}
Ok(())
}
pub(super) async fn handle_nginx(cmd: commands::NginxSubCommand, debug: bool) -> CliResult<()> {
if let Err(e) = run_nginx(cmd, debug).await {
let _ = log_error("nginx", "Nginx command failed", Some(&e.to_string())).await;
}
Ok(())
}
pub(super) async fn handle_diag(cmd: commands::DiagCmd, debug: bool) -> CliResult<()> {
if let Err(e) = run_diag(cmd, debug).await {
let _ = log_error("diag", "Diag command failed", Some(&e.to_string())).await;
}
Ok(())
}
pub(super) async fn handle_generate(cmd: commands::GenerateCmd, debug: bool) -> CliResult<()> {
match cmd.command {
commands::GenerateSubCommand::Systemd(subcmd) => {
let args = GenerateSystemdArgs {
output_dir: subcmd.output_dir,
service: subcmd.service,
api: subcmd.api,
};
if let Err(e) = run_generate_systemd(args, debug).await {
let _ = log_error(
"generate-systemd",
"Failed to generate systemd units",
Some(&e),
)
.await;
}
}
}
Ok(())
}
pub(super) async fn handle_done(cmd: commands::DoneCmd, _debug: bool) -> CliResult<()> {
if let Err(e) = crate::commands::run_done(
cmd.root,
cmd.since,
cmd.output,
cmd.no_ai,
cmd.recursive,
cmd.exclude,
)
.await
{
let _ = log_error("done", "Done command failed", Some(&e)).await;
return Err(e.into());
}
Ok(())
}
pub(super) async fn handle_login() -> CliResult<()> {
if let Err(e) = run_login().await {
let _ = log_error("login", "Login failed", Some(&e)).await;
return Err(e.into());
}
Ok(())
}
pub(super) async fn handle_version(cmd: commands::VersionCmd, debug: bool) -> CliResult<()> {
let commands::VersionCmd {
target,
git,
command,
} = cmd;
if command.is_some() && (target.is_some() || git) {
return Err(
"`xbp version release` cannot be combined with `--git` or positional targets.".into(),
);
}
if let Some(subcommand) = command {
match subcommand {
commands::VersionSubCommand::Release(release_cmd) => {
let options = VersionReleaseOptions {
explicit_version: release_cmd.version,
allow_dirty: release_cmd.allow_dirty,
title: release_cmd.title,
notes: release_cmd.notes,
notes_file: release_cmd.notes_file,
draft: release_cmd.draft,
prerelease: release_cmd.prerelease,
};
if let Err(e) = run_version_release_command(options).await {
let _ = log_error("version", "Version release failed", Some(&e)).await;
return Err(e.into());
}
return Ok(());
}
}
}
if let Err(e) = run_version_command(target, git, debug).await {
let _ = log_error("version", "Version command failed", Some(&e)).await;
return Err(e.into());
}
Ok(())
}