From 72cb25775dd9edec0a01b6ae3d4763485bd0a72e Mon Sep 17 00:00:00 2001 From: Eric Swanson <64809312+ericswanson-dfinity@users.noreply.github.com> Date: Fri, 8 Nov 2024 00:23:02 -0800 Subject: [PATCH] feat: DfxInterfaceBuilder can provide extension manager (#3976) This is so extension commands can load dfx.json if it references a canister with a type defined by an extension. See https://dfinity.atlassian.net/browse/SDK-1832 --- src/dfx-core/src/config/cache.rs | 19 +++++++++++++-- src/dfx-core/src/error/cache.rs | 13 +++++++++++ src/dfx-core/src/error/interface.rs | 12 ++++++++++ src/dfx-core/src/error/mod.rs | 1 + src/dfx-core/src/interface/builder.rs | 33 +++++++++++++++++++++++++-- 5 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 src/dfx-core/src/error/interface.rs diff --git a/src/dfx-core/src/config/cache.rs b/src/dfx-core/src/config/cache.rs index 11c813f810..13e39a2b61 100644 --- a/src/dfx-core/src/config/cache.rs +++ b/src/dfx-core/src/config/cache.rs @@ -2,13 +2,13 @@ use crate::config::directories::project_dirs; use crate::error::cache::{ DeleteCacheError, EnsureCacheVersionsDirError, GetBinaryCommandPathError, GetCacheRootError, - IsCacheInstalledError, ListCacheVersionsError, + GetVersionFromCachePathError, IsCacheInstalledError, ListCacheVersionsError, }; #[cfg(not(windows))] use crate::foundation::get_user_home; use crate::fs::composite::ensure_dir_exists; use semver::Version; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; pub trait Cache { fn version_str(&self) -> String; @@ -50,6 +50,21 @@ pub fn get_cache_path_for_version(v: &str) -> Result Ok(p) } +pub fn get_version_from_cache_path( + cache_path: &Path, +) -> Result { + let version = cache_path + .file_name() + .ok_or(GetVersionFromCachePathError::NoCachePathFilename( + cache_path.to_path_buf(), + ))? + .to_str() + .ok_or(GetVersionFromCachePathError::CachePathFilenameNotUtf8( + cache_path.to_path_buf(), + ))?; + Ok(Version::parse(version)?) +} + /// Return the binary cache root. It constructs it if not present /// already. pub fn ensure_cache_versions_dir() -> Result { diff --git a/src/dfx-core/src/error/cache.rs b/src/dfx-core/src/error/cache.rs index f74e684eb0..b431c4eecc 100644 --- a/src/dfx-core/src/error/cache.rs +++ b/src/dfx-core/src/error/cache.rs @@ -5,6 +5,7 @@ use crate::error::fs::{ }; use crate::error::get_current_exe::GetCurrentExeError; use crate::error::get_user_home::GetUserHomeError; +use std::path::PathBuf; use thiserror::Error; #[derive(Error, Debug)] @@ -34,6 +35,18 @@ pub enum EnsureCacheVersionsDirError { GetCacheRoot(#[from] GetCacheRootError), } +#[derive(Error, Debug)] +pub enum GetVersionFromCachePathError { + #[error("no filename in cache path '{0}'")] + NoCachePathFilename(PathBuf), + + #[error("filename in cache path '{0}' is not valid UTF-8")] + CachePathFilenameNotUtf8(PathBuf), + + #[error("cannot parse version from cache path filename")] + ParseVersion(#[from] semver::Error), +} + #[derive(Error, Debug)] pub enum GetCacheRootError { #[error(transparent)] diff --git a/src/dfx-core/src/error/interface.rs b/src/dfx-core/src/error/interface.rs new file mode 100644 index 0000000000..79a7929949 --- /dev/null +++ b/src/dfx-core/src/error/interface.rs @@ -0,0 +1,12 @@ +use crate::error::cache::GetVersionFromCachePathError; +use crate::error::extension::NewExtensionManagerError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum NewExtensionManagerFromCachePathError { + #[error(transparent)] + GetVersionFromCachePath(#[from] GetVersionFromCachePathError), + + #[error(transparent)] + NewExtensionManager(#[from] NewExtensionManagerError), +} diff --git a/src/dfx-core/src/error/mod.rs b/src/dfx-core/src/error/mod.rs index 5d720b50ce..ff13e4e49e 100644 --- a/src/dfx-core/src/error/mod.rs +++ b/src/dfx-core/src/error/mod.rs @@ -12,6 +12,7 @@ pub mod fs; pub mod get_current_exe; pub mod get_user_home; pub mod identity; +pub mod interface; pub mod keyring; pub mod load_dfx_config; pub mod load_networks_config; diff --git a/src/dfx-core/src/interface/builder.rs b/src/dfx-core/src/interface/builder.rs index 20776e6552..e7390e2c93 100644 --- a/src/dfx-core/src/interface/builder.rs +++ b/src/dfx-core/src/interface/builder.rs @@ -1,12 +1,16 @@ use crate::{ + config::cache::get_version_from_cache_path, config::model::{ dfinity::{Config, NetworksConfig}, network_descriptor::NetworkDescriptor, }, error::{ builder::{BuildAgentError, BuildDfxInterfaceError, BuildIdentityError}, + extension::NewExtensionManagerError, + interface::NewExtensionManagerFromCachePathError, network_config::NetworkConfigError, }, + extension::manager::ExtensionManager, identity::{identity_manager::InitializeIdentity, IdentityManager}, network::{ provider::{create_network_descriptor, LocalBindDetermination}, @@ -16,6 +20,8 @@ use crate::{ }; use ic_agent::{agent::route_provider::RoundRobinRouteProvider, Agent, Identity}; use reqwest::Client; +use semver::Version; +use std::path::Path; use std::sync::Arc; #[derive(PartialEq)] @@ -42,14 +48,17 @@ pub struct DfxInterfaceBuilder { /// There is no need to set this for the local network, where the root key is fetched by default. /// This would typically be set for a testnet, or an alias for the local network. force_fetch_root_key_insecure_non_mainnet_only: bool, + + extension_manager: Option, } impl DfxInterfaceBuilder { - pub(crate) fn new() -> Self { + pub fn new() -> Self { Self { identity: IdentityPicker::Selected, network: NetworkPicker::Local, force_fetch_root_key_insecure_non_mainnet_only: false, + extension_manager: None, } } @@ -77,6 +86,26 @@ impl DfxInterfaceBuilder { self.with_network(NetworkPicker::Named(name.to_string())) } + pub fn with_extension_manager( + self, + version: Version, + ) -> Result { + let extension_manager = Some(ExtensionManager::new(&version)?); + Ok(Self { + extension_manager, + ..self + }) + } + + pub fn with_extension_manager_from_cache_path( + self, + cache_path: &Path, + ) -> Result { + let version = get_version_from_cache_path(cache_path)?; + + Ok(self.with_extension_manager(version)?) + } + pub fn with_force_fetch_root_key_insecure_non_mainnet_only(self) -> Self { Self { force_fetch_root_key_insecure_non_mainnet_only: true, @@ -88,7 +117,7 @@ impl DfxInterfaceBuilder { let fetch_root_key = self.network == NetworkPicker::Local || self.force_fetch_root_key_insecure_non_mainnet_only; let networks_config = NetworksConfig::new()?; - let config = Config::from_current_dir(None)?.map(Arc::new); + let config = Config::from_current_dir(self.extension_manager.as_ref())?.map(Arc::new); let network_descriptor = self.build_network_descriptor(config.clone(), &networks_config)?; let identity = self.build_identity()?; let agent = self.build_agent(identity.clone(), &network_descriptor)?;