Files
xboct-deploy/src/main.rs
T

99 lines
2.6 KiB
Rust

mod cli;
mod config;
mod deploy;
mod local;
mod ssh;
mod sync;
use std::process::ExitCode;
use anyhow::{Context, Result, bail};
use clap::Parser;
use colored::Colorize;
fn main() -> ExitCode {
#[cfg(windows)]
if enable_ansi_support::enable_ansi_support().is_err() {
colored::control::set_override(false);
}
let cli = cli::Cli::parse();
match run(&cli) {
Ok(()) => ExitCode::SUCCESS,
Err(err) => {
eprintln!("{} {err:#}", "error:".red().bold());
ExitCode::FAILURE
}
}
}
fn run(cli: &cli::Cli) -> Result<()> {
let path = config::resolve(cli.config.as_deref())?;
let cfg = config::load(&path)?;
if cli.list_servers {
println!("config: {}", path.display());
println!("configured servers:");
for (name, server) in &cfg.servers {
let mut line = format!(" - {name} {}", server.host);
if let Some(port) = server.port {
line.push_str(&format!(":{port}"));
}
if let Some(user) = &server.user {
line.push_str(&format!(" as {user}"));
}
println!("{line}");
}
return Ok(());
}
if cli.list {
println!("config: {}", path.display());
println!("configured projects:");
for name in cfg.projects.keys() {
println!(" - {name}");
}
return Ok(());
}
let name = if cli.pick {
match pick_project(&cfg)? {
Some(name) => name,
None => return Ok(()),
}
} else {
cli.project
.clone()
.expect("clap guarantees <PROJECT> or a mode flag")
};
let project = cfg
.projects
.get(&name)
.with_context(|| format!("project '{name}' not found in {}", path.display()))?;
deploy::deploy(&name, project, cli.dry_run)
}
fn pick_project(cfg: &config::Config) -> Result<Option<String>> {
use dialoguer::FuzzySelect;
use std::io::IsTerminal;
if !std::io::stdout().is_terminal() {
bail!("interactive picker needs a real terminal; use --list instead");
}
let names: Vec<&str> = cfg.projects.keys().map(String::as_str).collect();
match FuzzySelect::new()
.with_prompt("Pick a project")
.items(&names)
.default(0)
.interact()
{
Ok(idx) => Ok(Some(names[idx].to_string())),
Err(dialoguer::Error::IO(err)) if err.kind() == std::io::ErrorKind::Interrupted => {
println!("cancelled.");
Ok(None)
}
Err(err) => Err(err).context("project selection failed"),
}
}