From 159c4959286921a297a950d191e338ae7bceb08a Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Sun, 24 Mar 2024 23:13:29 +0100 Subject: [PATCH] Trust and install tool in add subcommand --- src/cli/add.rs | 64 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 8 deletions(-) diff --git a/src/cli/add.rs b/src/cli/add.rs index 451b34b..ded01b3 100644 --- a/src/cli/add.rs +++ b/src/cli/add.rs @@ -2,12 +2,16 @@ use anyhow::{bail, Context, Result}; use clap::Parser; use aftman::{ + description::Description, manifests::AftmanManifest, storage::Home, tool::{ToolAlias, ToolId}, }; +use tokio::time::Instant; -use crate::util::{discover_aftman_manifest_dir, github_tool_source, ToolIdOrSpec}; +use crate::util::{ + discover_aftman_manifest_dir, github_tool_source, prompt_for_install_trust, ToolIdOrSpec, +}; /// Adds a new tool to Aftman and installs it. #[derive(Debug, Parser)] @@ -15,18 +19,22 @@ pub struct AddSubcommand { /// A tool identifier or specification describing where /// to get the tool, and optionally what version to install. pub tool: ToolIdOrSpec, - /// The name that will be used to run the tool. pub alias: Option, - /// Add this tool globally instead of adding /// it to the nearest manifest file. #[clap(long)] pub global: bool, + /// Force add and install the tool, even + /// if it is already added or installed. + #[clap(long)] + force: bool, } impl AddSubcommand { pub async fn run(&self, home: &Home) -> Result<()> { + let start = Instant::now(); + let id: ToolId = self.tool.clone().into(); let alias: ToolAlias = match self.alias.as_ref() { Some(alias) => alias.clone(), @@ -41,13 +49,22 @@ impl AddSubcommand { let source = github_tool_source(home).await?; + // Check for trust, or prompt the user to trust the tool + let trust_cache = home.trust_cache(); + if !trust_cache.is_trusted(&id) { + if !self.force && !prompt_for_install_trust(&id).await? { + bail!("Tool is not trusted - installation was aborted"); + } + trust_cache.add_tool(id.clone()); + } + // Load manifest and do a preflight check to make sure we don't overwrite any tool let mut manifest = if self.global { AftmanManifest::load_or_create(&manifest_path).await? } else { AftmanManifest::load(&manifest_path).await? }; - if manifest.has_tool(&alias) { + if manifest.has_tool(&alias) && !self.force { let global_flag = if self.global { "--global " } else { "" }; bail!( "Tool already exists and can't be added: {id}\n\ @@ -61,7 +78,7 @@ impl AddSubcommand { let spec = match self.tool.clone() { ToolIdOrSpec::Spec(spec) => spec, ToolIdOrSpec::Id(id) => { - tracing::info!("Looking for latest version of {id}..."); + tracing::info!("Looking for the latest version of {id}..."); let version = source .find_latest_version(&id, false) .await? @@ -72,10 +89,41 @@ impl AddSubcommand { // Add the tool spec to the desired manifest file and save it manifest.add_tool(&alias, &spec); - manifest.save(home.path()).await?; - tracing::info!("Added tool successfully: {id}"); + manifest.save(manifest_path).await?; + tracing::info!("Added tool successfully: {spec}"); + + // Install the tool and create the link for its alias + let description = Description::current(); + let install_cache = home.install_cache(); + let tool_storage = home.tool_storage(); + if !install_cache.is_installed(&spec) && !self.force { + tracing::info!("Downloading {spec}"); + let release = source + .find_release(&spec) + .await? + .with_context(|| format!("Failed to find release for {spec}"))?; + let artifact = source + .find_compatible_artifacts(&spec, &release, &description) + .first() + .cloned() + .with_context(|| format!("No compatible artifact found for {spec}"))?; + let contents = source + .download_artifact_contents(&artifact) + .await + .with_context(|| format!("Failed to download contents for {spec}"))?; + + tracing::info!("Installing {spec}"); + let extracted = artifact + .extract_contents(contents) + .await + .with_context(|| format!("Failed to extract contents for {spec}"))?; + tool_storage.replace_tool_contents(&spec, extracted).await?; + + install_cache.add_spec(spec.clone()); + } - // TODO: Install the tool + tool_storage.create_tool_link(&alias).await?; + tracing::info!("Completed in {:.2?}", start.elapsed()); Ok(()) }