feat: add interactive pick project flow
This commit is contained in:
+4
-1
@@ -6,7 +6,7 @@ use clap::{ArgGroup, Parser};
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(name = "xboctploy", version, about)]
|
||||
#[command(arg_required_else_help = true)]
|
||||
#[command(group = ArgGroup::new("target").required(true).args(["project", "list", "list_servers"]))]
|
||||
#[command(group = ArgGroup::new("target").required(true).args(["project", "list", "list_servers", "pick"]))]
|
||||
pub struct Cli {
|
||||
/// Project name from deploy.toml
|
||||
pub project: Option<String>,
|
||||
@@ -22,4 +22,7 @@ pub struct Cli {
|
||||
/// List configured servers and exit
|
||||
#[arg(long)]
|
||||
pub list_servers: bool,
|
||||
/// Interactively pick a project to deploy (fuzzy search)
|
||||
#[arg(long)]
|
||||
pub pick: bool,
|
||||
}
|
||||
|
||||
+36
-7
@@ -7,7 +7,7 @@ mod sync;
|
||||
|
||||
use std::process::ExitCode;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use anyhow::{Context, Result, bail};
|
||||
use clap::Parser;
|
||||
use colored::Colorize;
|
||||
|
||||
@@ -56,14 +56,43 @@ fn run(cli: &cli::Cli) -> Result<()> {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let name = cli
|
||||
.project
|
||||
.as_deref()
|
||||
.expect("clap guarantees <PROJECT> or --list");
|
||||
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)
|
||||
.get(&name)
|
||||
.with_context(|| format!("project '{name}' not found in {}", path.display()))?;
|
||||
|
||||
deploy::deploy(name, project, cli.dry_run)
|
||||
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"),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user