feat: add interactive pick project flow
This commit is contained in:
Generated
+110
@@ -346,6 +346,18 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "console"
|
||||||
|
version = "0.16.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4fe5f465a4f6fee88fad41b85d990f84c835335e85b5d9e6e63e0d06d28cba7c"
|
||||||
|
dependencies = [
|
||||||
|
"encode_unicode",
|
||||||
|
"libc",
|
||||||
|
"unicode-width",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const-oid"
|
name = "const-oid"
|
||||||
version = "0.10.2"
|
version = "0.10.2"
|
||||||
@@ -524,6 +536,19 @@ dependencies = [
|
|||||||
"cipher",
|
"cipher",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dialoguer"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "25f104b501bf2364e78d0d3974cbc774f738f5865306ed128e1e0d7499c0ad96"
|
||||||
|
dependencies = [
|
||||||
|
"console",
|
||||||
|
"fuzzy-matcher",
|
||||||
|
"shell-words",
|
||||||
|
"tempfile",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.11.3"
|
version = "0.11.3"
|
||||||
@@ -629,6 +654,12 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encode_unicode"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "enum_dispatch"
|
name = "enum_dispatch"
|
||||||
version = "0.3.13"
|
version = "0.3.13"
|
||||||
@@ -647,6 +678,22 @@ version = "1.0.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.3.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fastrand"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ff"
|
name = "ff"
|
||||||
version = "0.14.0"
|
version = "0.14.0"
|
||||||
@@ -767,6 +814,15 @@ dependencies = [
|
|||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fuzzy-matcher"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94"
|
||||||
|
dependencies = [
|
||||||
|
"thread_local",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "0.14.9"
|
version = "0.14.9"
|
||||||
@@ -1029,6 +1085,12 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.14"
|
version = "0.4.14"
|
||||||
@@ -1596,6 +1658,19 @@ dependencies = [
|
|||||||
"semver",
|
"semver",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "1.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.23"
|
version = "1.0.23"
|
||||||
@@ -1761,6 +1836,12 @@ dependencies = [
|
|||||||
"sponge-cursor",
|
"sponge-cursor",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shell-words"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shlex"
|
name = "shlex"
|
||||||
version = "2.0.1"
|
version = "2.0.1"
|
||||||
@@ -1928,6 +2009,19 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tempfile"
|
||||||
|
version = "3.27.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
||||||
|
dependencies = [
|
||||||
|
"fastrand",
|
||||||
|
"getrandom 0.4.3",
|
||||||
|
"once_cell",
|
||||||
|
"rustix",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "2.0.20"
|
version = "2.0.20"
|
||||||
@@ -1948,6 +2042,15 @@ dependencies = [
|
|||||||
"syn 3.0.3",
|
"syn 3.0.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thread_local"
|
||||||
|
version = "1.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.53.1"
|
version = "1.53.1"
|
||||||
@@ -2039,6 +2142,12 @@ version = "1.0.24"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "universal-hash"
|
name = "universal-hash"
|
||||||
version = "0.6.1"
|
version = "0.6.1"
|
||||||
@@ -2369,6 +2478,7 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
"colored",
|
"colored",
|
||||||
|
"dialoguer",
|
||||||
"enable-ansi-support",
|
"enable-ansi-support",
|
||||||
"home",
|
"home",
|
||||||
"russh",
|
"russh",
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ edition = "2024"
|
|||||||
anyhow = "1.0.104"
|
anyhow = "1.0.104"
|
||||||
clap = { version = "4.6.6", features = ["derive"] }
|
clap = { version = "4.6.6", features = ["derive"] }
|
||||||
colored = "3.1.1"
|
colored = "3.1.1"
|
||||||
|
dialoguer = { version = "0.12.0", features = ["fuzzy-select"] }
|
||||||
enable-ansi-support = "0.3.1"
|
enable-ansi-support = "0.3.1"
|
||||||
home = "0.5.12"
|
home = "0.5.12"
|
||||||
russh = { version = "0.62.7", default-features = false, features = [
|
russh = { version = "0.62.7", default-features = false, features = [
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ cargo install --path .
|
|||||||
```sh
|
```sh
|
||||||
xboctploy --list # показать проекты из конфига
|
xboctploy --list # показать проекты из конфига
|
||||||
xboctploy --list-servers # показать серверы из конфига
|
xboctploy --list-servers # показать серверы из конфига
|
||||||
|
xboctploy --pick # интерактивно выбрать проект (нечёткий поиск)
|
||||||
xboctploy --dry-run demo # напечатать план деплоя без выполнения
|
xboctploy --dry-run demo # напечатать план деплоя без выполнения
|
||||||
xboctploy demo # задеплоить проект demo
|
xboctploy demo # задеплоить проект demo
|
||||||
```
|
```
|
||||||
|
|||||||
+4
-1
@@ -6,7 +6,7 @@ use clap::{ArgGroup, Parser};
|
|||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
#[command(name = "xboctploy", version, about)]
|
#[command(name = "xboctploy", version, about)]
|
||||||
#[command(arg_required_else_help = true)]
|
#[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 {
|
pub struct Cli {
|
||||||
/// Project name from deploy.toml
|
/// Project name from deploy.toml
|
||||||
pub project: Option<String>,
|
pub project: Option<String>,
|
||||||
@@ -22,4 +22,7 @@ pub struct Cli {
|
|||||||
/// List configured servers and exit
|
/// List configured servers and exit
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub list_servers: bool,
|
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 std::process::ExitCode;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result, bail};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
|
|
||||||
@@ -56,14 +56,43 @@ fn run(cli: &cli::Cli) -> Result<()> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = cli
|
let name = if cli.pick {
|
||||||
.project
|
match pick_project(&cfg)? {
|
||||||
.as_deref()
|
Some(name) => name,
|
||||||
.expect("clap guarantees <PROJECT> or --list");
|
None => return Ok(()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cli.project
|
||||||
|
.clone()
|
||||||
|
.expect("clap guarantees <PROJECT> or a mode flag")
|
||||||
|
};
|
||||||
let project = cfg
|
let project = cfg
|
||||||
.projects
|
.projects
|
||||||
.get(name)
|
.get(&name)
|
||||||
.with_context(|| format!("project '{name}' not found in {}", path.display()))?;
|
.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