v0.2.2 : suggestions d'installation selon l'OS (scoop/winget/apt/dnf/pacman/apk/brew) + README remanie

This commit is contained in:
2026-08-15 12:56:33 -04:00
parent bd1d566a44
commit e513dd485d
9 changed files with 638 additions and 390 deletions
+52 -18
View File
@@ -111,34 +111,60 @@ pub fn check_agent(agent: &AgentDef, runner: &dyn Runner) -> Vec<DepCheck> {
}
/// Fail with an actionable message when dependencies are missing.
/// With auto_install_deps enabled, dependencies that define an install_cmd
/// are offered for automatic installation first.
///
/// With auto_install_deps enabled (the default), missing tools are offered
/// for installation right away: an explicit install_cmd from the config
/// first, otherwise the command detected for the current OS
/// (scoop/winget on Windows, apt/dnf/pacman/apk on Linux, brew on macOS).
pub fn ensure_deps(app: &App, agent: &AgentDef, runner: &dyn Runner) -> Result<()> {
let mut checks = check_agent(agent, runner);
if checks.iter().all(|c| c.ok) {
return Ok(());
}
let os = crate::toolchain::OsInfo::detect();
if app.config.settings.auto_install_deps() && !app.dry_run() {
for dep in &agent.dependencies {
if dep.install_cmd.is_empty() {
continue;
}
if check_dep(runner, dep).ok {
continue;
}
let cmd_line = dep.install_cmd.join(" ");
app.log.warn(&format!("dependency '{}' is missing", dep.name));
let proceed = app
.confirm(&format!("Run '{cmd_line}' to install it?"))
.unwrap_or(false);
if proceed {
let prog = dep.install_cmd[0].clone();
let args: Vec<String> = dep.install_cmd[1..].to_vec();
let empty = BTreeMap::new();
let out = runner.run(&prog, &args, &empty, None, false)?;
if out.code != 0 {
app.log
.warn(&format!("automatic installation of '{}' failed", dep.name));
// 1) Explicit install command from the configuration.
if !dep.install_cmd.is_empty() {
let cmd_line = dep.install_cmd.join(" ");
if app.confirm(&format!("Run '{cmd_line}' to install it?"))? {
let prog = dep.install_cmd[0].clone();
let args: Vec<String> = dep.install_cmd[1..].to_vec();
let empty = BTreeMap::new();
let out = runner.run(&prog, &args, &empty, None, false)?;
if out.code != 0 {
app.log.warn(&format!(
"automatic installation of '{}' failed (exit code {})",
dep.name, out.code
));
}
}
continue;
}
// 2) Command detected for the current OS.
let cmds = crate::toolchain::install_commands(&dep.name, &os);
if let Some(cmd) = cmds.first() {
let proceed = app
.confirm(&format!("Run the detected installer for {}? -> {cmd}", os.name))?;
if proceed {
if let Ok(tokens) = shell_words::split(cmd) {
if !tokens.is_empty() {
let prog = tokens[0].clone();
let args: Vec<String> = tokens[1..].to_vec();
let empty = BTreeMap::new();
let out = runner.run(&prog, &args, &empty, None, false)?;
if out.code != 0 {
app.log.warn(&format!(
"installation of '{}' failed (exit code {})",
dep.name, out.code
));
}
}
}
}
}
}
@@ -153,7 +179,15 @@ pub fn ensure_deps(app: &App, agent: &AgentDef, runner: &dyn Runner) -> Result<(
if let Some(min) = &c.min {
line.push_str(&format!(" (minimum: {min})"));
}
if let Some(dep) = agent.dependencies.iter().find(|d| d.name == c.name) {
let cmds = crate::toolchain::install_commands(&c.name, &os);
if !cmds.is_empty() {
line.push_str(&format!(
"
on {}: {}",
os.name,
cmds.join(" | ")
));
} else if let Some(dep) = agent.dependencies.iter().find(|d| d.name == c.name) {
if let Some(hint) = &dep.install_hint {
line.push_str(&format!(" — install via {hint}"));
}