diff --git a/Cargo.lock b/Cargo.lock index 0b245748..0a48dc54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -279,9 +279,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" dependencies = [ + "futures-core", "getrandom", "instant", + "pin-project-lite", "rand", + "tokio", ] [[package]] @@ -930,6 +933,7 @@ name = "dfx-extensions-utils" version = "0.0.0" dependencies = [ "anyhow", + "backoff", "dfx-core", "flate2", "fn-error-context", @@ -945,6 +949,7 @@ dependencies = [ "slog-term", "tempfile", "thiserror", + "tokio", "url", ] diff --git a/extensions-utils/Cargo.toml b/extensions-utils/Cargo.toml index aef537c6..c0a085da 100644 --- a/extensions-utils/Cargo.toml +++ b/extensions-utils/Cargo.toml @@ -17,6 +17,7 @@ reqwest.workspace = true dfx-core.workspace = true anyhow.workspace = true +backoff = { version = "0.4.0", features = [ "futures", "tokio" ] } flate2 = { version = "1.0.25", default-features = false, features = ["zlib-ng"] } fn-error-context.workspace = true futures-util.workspace = true @@ -31,4 +32,5 @@ slog-term = "2.9.0" slog.workspace = true tempfile.workspace = true thiserror = "1.0.40" +tokio.workspace = true url.workspace = true diff --git a/extensions-utils/src/dependencies/call.rs b/extensions-utils/src/dependencies/call.rs new file mode 100644 index 00000000..74ca9e7e --- /dev/null +++ b/extensions-utils/src/dependencies/call.rs @@ -0,0 +1,63 @@ +use anyhow::anyhow; +use fn_error_context::context; +use std::{ + ffi::OsStr, + path::Path, + process::{self, Command}, +}; + +/// Calls a binary that was delivered with an extension tarball. +/// +/// # Returns +/// - On success, returns stdout as a string. +/// - On error, returns an error message including stdout and stderr. +#[context("Calling {} CLI failed, or, it returned an error.", binary_name)] +pub fn call_extension_bundled_binary( + dfx_cache_path: &Path, + binary_name: &str, + args: I, +) -> anyhow::Result +where + I: IntoIterator, + S: AsRef, +{ + let extension_binary_path = + std::env::current_exe().map_err(|e| anyhow::anyhow!("Failed to get current exe: {}", e))?; + let extension_dir_path = extension_binary_path.parent().ok_or_else(|| { + anyhow::anyhow!( + "Failed to locate parent of dir of executable: {}", + extension_binary_path.display() + ) + })?; + let binary_to_call = extension_dir_path.join(binary_name); + // TODO + dbg!(&binary_to_call); + std::fs::remove_file(binary_to_call.clone()).unwrap(); + panic!("trying to prettify command output, but something is not working the way I expect."); + let mut command = Command::new(&binary_to_call); + // If extension's dependency calls dfx; it should call dfx in this dir. + command.env("PATH", dfx_cache_path.join("dfx")); + command.args(args); + command + .stdin(process::Stdio::null()) + .output() + .map_err(anyhow::Error::from) + .and_then(|output| -> Result { + if output.status.success() { + Ok(String::from_utf8_lossy(&output.stdout).into_owned()) + } else { + let args: Vec<_> = command + .get_args() + .into_iter() + .map(OsStr::to_string_lossy) + .collect(); + Err(anyhow!( + "Call failed:\n{:?} {}\nStdout:\n{}\n\nStderr:\n{}", + command.get_program(), + args.join(" "), + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + )) + } + }) +} diff --git a/extensions-utils/src/dfx.rs b/extensions-utils/src/dependencies/dfx.rs similarity index 60% rename from extensions-utils/src/dfx.rs rename to extensions-utils/src/dependencies/dfx.rs index f5a509c4..992a5761 100644 --- a/extensions-utils/src/dfx.rs +++ b/extensions-utils/src/dependencies/dfx.rs @@ -23,64 +23,10 @@ where S: AsRef, { let binary = dfx_cache_path.join(command); - let mut command = Command::new(&binary); + // If extension's dependency calls dfx; it should call dfx in this dir. + command.env("PATH", dfx_cache_path.join("dfx")); command.args(args); - // The sns command line tool itself calls dfx; it should call this dfx. - // The sns command line tool should not rely on commands not packaged with dfx. - // The same applies to other bundled binaries. - command.env("PATH", binary.parent().unwrap_or_else(|| Path::new("."))); - command - .stdin(process::Stdio::null()) - .output() - .map_err(anyhow::Error::from) - .and_then(|output| { - if output.status.success() { - Ok(String::from_utf8_lossy(&output.stdout).into_owned()) - } else { - let args: Vec<_> = command - .get_args() - .into_iter() - .map(OsStr::to_string_lossy) - .collect(); - Err(anyhow!( - "Call failed:\n{:?} {}\nStdout:\n{}\n\nStderr:\n{}", - command.get_program(), - args.join(" "), - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr) - )) - } - }) -} - -/// Calls a binary that was delivered with an extension tarball. -/// -/// # Returns -/// - On success, returns stdout as a string. -/// - On error, returns an error message including stdout and stderr. -#[context("Calling {} CLI failed, or, it returned an error.", binary_name)] -pub fn call_extension_bundled_binary(binary_name: &str, args: I) -> anyhow::Result -where - I: IntoIterator, - S: AsRef, -{ - let extension_binary_path = - std::env::current_exe().map_err(|e| anyhow::anyhow!("Failed to get current exe: {}", e))?; - let extension_dir_path = extension_binary_path - .parent() - .unwrap_or_else(|| Path::new(".")); - let binary_to_call = extension_dir_path.join(binary_name); - - let mut command = Command::new(&binary_to_call); - command.args(args); - // The sns command line tool itself calls dfx; it should call this dfx. - // The sns command line tool should not rely on commands not packaged with dfx. - // The same applies to other bundled binaries. - command.env( - "PATH", - binary_to_call.parent().unwrap_or_else(|| Path::new(".")), - ); command .stdin(process::Stdio::null()) .output() diff --git a/extensions-utils/src/dependencies/download_ic_binaries.rs b/extensions-utils/src/dependencies/download_ic_binaries.rs new file mode 100644 index 00000000..53473237 --- /dev/null +++ b/extensions-utils/src/dependencies/download_ic_binaries.rs @@ -0,0 +1,64 @@ +use backoff::future::retry; +use backoff::ExponentialBackoffBuilder; +use flate2::read::GzDecoder; +use std::path::Path; +use std::time::Duration; +use std::{fs, io::copy}; +use tokio::runtime::Runtime; + +pub fn download_ic_binary(replica_rev: &str, binary_name: &str, destination_path: &Path) { + let arch = match std::env::consts::ARCH { + "x86_64" => "x86_64", + "aarch64" => "x86_64", // let's rely on rosetta2 for now, since ic binaiers are not available for arm64 + _ => panic!("Unsupported architecture"), + }; + let os = match std::env::consts::OS { + "macos" => "darwin", + "linux" => "linux", + // "windows" => "windows", // unsupported till dfx supports windows + _ => panic!("Unsupported OS"), + }; + + let url = format!( + "https://download.dfinity.systems/ic/{replica_rev}/openssl-static-binaries/{arch}-{os}/{binary_name}.gz", + arch = arch, + os = os, + binary_name = binary_name, + ); + println!("Downloading {}", url); + + let bytes = Runtime::new().unwrap().block_on(download_bytes(&url)); + let mut d = GzDecoder::new(&*bytes); + let tempdir = tempfile::tempdir().expect("Failed to create temp dir"); + let temp_file = tempdir.path().join(binary_name); + let mut temp = fs::File::create(&temp_file).expect("Failed to create the file"); + copy(&mut d, &mut temp).expect("Failed to copy content"); + + fs::rename(temp_file, &destination_path).expect("Failed to move extension"); + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + dfx_core::fs::set_permissions(&destination_path, std::fs::Permissions::from_mode(0o500)) + .expect("Failed to set permissions"); + } +} + +async fn download_bytes(url: &str) -> Vec { + let retry_policy = ExponentialBackoffBuilder::new() + .with_initial_interval(Duration::from_secs(1)) + .with_max_interval(Duration::from_secs(16)) + .with_multiplier(2.0) + .with_max_elapsed_time(Some(Duration::from_secs(300))) + .build(); + let resp = retry(retry_policy, || async { + match reqwest::get(url).await { + Ok(response) => Ok(response), + Err(err) => Err(backoff::Error::transient(err)), + } + }) + .await + .unwrap(); + + let bytes = resp.bytes().await.expect("Failed to read response"); + bytes.to_vec() +} diff --git a/extensions-utils/src/download_wasms/mod.rs b/extensions-utils/src/dependencies/download_wasms/mod.rs similarity index 100% rename from extensions-utils/src/download_wasms/mod.rs rename to extensions-utils/src/dependencies/download_wasms/mod.rs diff --git a/extensions-utils/src/download_wasms/nns.rs b/extensions-utils/src/dependencies/download_wasms/nns.rs similarity index 100% rename from extensions-utils/src/download_wasms/nns.rs rename to extensions-utils/src/dependencies/download_wasms/nns.rs diff --git a/extensions-utils/src/download_wasms/sns.rs b/extensions-utils/src/dependencies/download_wasms/sns.rs similarity index 100% rename from extensions-utils/src/download_wasms/sns.rs rename to extensions-utils/src/dependencies/download_wasms/sns.rs diff --git a/extensions-utils/src/dependencies/mod.rs b/extensions-utils/src/dependencies/mod.rs new file mode 100644 index 00000000..be50e04c --- /dev/null +++ b/extensions-utils/src/dependencies/mod.rs @@ -0,0 +1,4 @@ +pub mod call; +pub mod dfx; +pub mod download_ic_binaries; +pub mod download_wasms; diff --git a/extensions-utils/src/download_dependencies.rs b/extensions-utils/src/download_dependencies.rs deleted file mode 100644 index deb8cebf..00000000 --- a/extensions-utils/src/download_dependencies.rs +++ /dev/null @@ -1,44 +0,0 @@ -use flate2::read::GzDecoder; -use std::fs::File; -use std::path::Path; -use std::{env, fs, io::copy}; - -pub fn download_ic_binary(replica_rev: &str, binary_name: &str, renamed_binary_name: &str) { - let arch = match std::env::consts::ARCH { - "x86_64" => "x86_64", - "aarch64" => "x86_64", // let's rely on rosetta2 for now, since sns-cli is not available for arm64 - _ => panic!("Unsupported architecture"), - }; - let os = match std::env::consts::OS { - "macos" => "darwin", - "linux" => "linux", - // "windows" => "windows", // unsupported till dfx supports windows - _ => panic!("Unsupported OS"), - }; - - let url = format!( - "https://download.dfinity.systems/ic/{replica_rev}/openssl-static-binaries/{arch}-{os}/{binary_name}.gz", - arch = arch, - os = os, - binary_name = binary_name, - ); - println!("Downloading {}", url); - - let resp = reqwest::blocking::get(&url).expect("Request failed"); - let bytes = resp.bytes().expect("Failed to read response"); - - let mut d = GzDecoder::new(&*bytes); - let mut dest = File::create(binary_name).expect("Failed to create sns file"); - - copy(&mut d, &mut dest).expect("Failed to copy content"); - - let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); - let out_path = Path::new(&manifest_dir).join(renamed_binary_name); - fs::rename(binary_name, &out_path).expect("Failed to move extension"); - #[cfg(unix)] - { - use std::os::unix::fs::PermissionsExt; - dfx_core::fs::set_permissions(&out_path, std::fs::Permissions::from_mode(0o777)) - .expect("Failed to set permissions"); - } -} diff --git a/extensions-utils/src/lib.rs b/extensions-utils/src/lib.rs index 66dd22e9..512cdafa 100644 --- a/extensions-utils/src/lib.rs +++ b/extensions-utils/src/lib.rs @@ -1,24 +1,23 @@ //! Library for calling bundled command line tools. -mod dfx; -pub mod download_dependencies; -mod download_wasms; +pub mod dependencies; mod error; mod logger; mod project; -pub use dfx::{ - call_dfx_bundled_binary, call_extension_bundled_binary, dfx_version, replica_rev, - webserver_port, +pub use dependencies::{ + call::call_extension_bundled_binary, + dfx::{call_dfx_bundled_binary, dfx_version, replica_rev, webserver_port}, + download_ic_binaries::download_ic_binary, + download_wasms::{ + download_ic_repo_wasm, + nns::{ + download_nns_wasms, nns_wasm_dir, IcNnsInitCanister, StandardCanister, + ED25519_TEST_ACCOUNT, NNS_CORE, NNS_FRONTEND, NNS_SNS_WASM, SECP256K1_TEST_ACCOUNT, + }, + sns::{download_sns_wasms, SnsCanisterInstallation, SNS_CANISTERS}, + }, }; -pub use download_wasms::download_ic_repo_wasm; -pub use download_wasms::nns::download_nns_wasms; -pub use download_wasms::nns::{ - nns_wasm_dir, IcNnsInitCanister, StandardCanister, ED25519_TEST_ACCOUNT, NNS_CORE, - NNS_FRONTEND, NNS_SNS_WASM, SECP256K1_TEST_ACCOUNT, -}; -pub use download_wasms::sns::SnsCanisterInstallation; -pub use download_wasms::sns::{download_sns_wasms, SNS_CANISTERS}; pub use logger::new_logger; pub use project::import::import_canister_definitions; pub use project::network_mappings::get_network_mappings; diff --git a/extensions/nns/build.rs b/extensions/nns/build.rs index f9cff355..43a22945 100644 --- a/extensions/nns/build.rs +++ b/extensions/nns/build.rs @@ -1,3 +1,5 @@ +use std::path::Path; + const REPLICA_REV: &str = "90e2799c255733409d0e61682685afcc2431c928"; const BINARY_DEPENDENCIES: &[(&str, &str)] = &[ @@ -8,11 +10,9 @@ const BINARY_DEPENDENCIES: &[(&str, &str)] = &[ ]; fn main() { + let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); for (binary_name, renamed_binary_name) in BINARY_DEPENDENCIES { - dfx_extensions_utils::download_dependencies::download_ic_binary( - REPLICA_REV, - binary_name, - renamed_binary_name, - ); + let destination_path = Path::new(&manifest_dir).join(renamed_binary_name); + dfx_extensions_utils::download_ic_binary(REPLICA_REV, binary_name, &destination_path); } } diff --git a/extensions/nns/src/install_nns.rs b/extensions/nns/src/install_nns.rs index dc9078f2..a4ea1bdf 100644 --- a/extensions/nns/src/install_nns.rs +++ b/extensions/nns/src/install_nns.rs @@ -28,6 +28,7 @@ use ic_utils::interfaces::management_canister::builders::InstallMode; use ic_utils::interfaces::ManagementCanister; use reqwest::Url; use slog::Logger; +use std::ffi::OsString; use std::fs; use std::io::Write; use std::path::Component; @@ -78,7 +79,7 @@ pub async fn install_nns( test_accounts, sns_subnets: Some(subnet_id.to_string()), }; - ic_nns_init(&ic_nns_init_opts).await?; + ic_nns_init(&ic_nns_init_opts, dfx_cache_path).await?; eprintln!("Uploading NNS configuration data..."); upload_nns_sns_wasms_canister_wasms(dfx_cache_path)?; @@ -112,8 +113,8 @@ pub async fn install_nns( } // ... and configure the backend NNS canisters: eprintln!("Configuring the NNS..."); - set_xdr_rate(1234567, &nns_url)?; - set_cmc_authorized_subnets(&nns_url, &subnet_id)?; + set_xdr_rate(1234567, &nns_url, dfx_cache_path)?; + set_cmc_authorized_subnets(&nns_url, &subnet_id, dfx_cache_path)?; print_nns_details(provider_url)?; Ok(()) @@ -412,24 +413,24 @@ pub struct IcNnsInitOpts { /// - This won't work with an HSM, because the agent holds a session open /// - The provider_url is what the agent connects to, and forwards to the replica. #[context("Failed to install NNS components.")] -pub async fn ic_nns_init(opts: &IcNnsInitOpts) -> anyhow::Result { - let mut args = vec![ - "--pass-specified-id", - "--url", - &opts.nns_url, - "--wasm-dir", - opts.wasm_dir.to_str().unwrap(), +pub async fn ic_nns_init(opts: &IcNnsInitOpts, dfx_cache_path: &Path) -> anyhow::Result { + let mut args: Vec = vec![ + "--pass-specified-id".into(), + "--url".into(), + opts.nns_url.clone().into(), + "--wasm-dir".into(), + opts.wasm_dir.as_os_str().into(), ]; for account in &opts.test_accounts { - args.push("--initialize-ledger-with-test-accounts"); - args.push(account); + args.push("--initialize-ledger-with-test-accounts".into()); + args.push(account.into()); } if let Some(subnets) = &opts.sns_subnets { - args.push("--sns-subnet"); - args.push(subnets); + args.push("--sns-subnet".into()); + args.push(subnets.into()); } - call_extension_bundled_binary("ic-nns-init", &args) - .with_context(|| format!("Error executing `ic-admin` with args: {:?}", args)) + call_extension_bundled_binary(dfx_cache_path, "ic-nns-init", &args) + .with_context(|| format!("Error executing `ic-nns-init` with args: {:?}", args)) } /// Sets the exchange rate between ICP and cycles. @@ -439,7 +440,7 @@ pub async fn ic_nns_init(opts: &IcNnsInitOpts) -> anyhow::Result { /// proposals with a test user pass immediately, as the small test neuron is /// the only neuron and has absolute majority. #[context("Failed to set an initial exchange rate between ICP and cycles. It may not be possible to create canisters or purchase cycles.")] -pub fn set_xdr_rate(rate: u64, nns_url: &Url) -> anyhow::Result { +pub fn set_xdr_rate(rate: u64, nns_url: &Url, dfx_cache_path: &Path) -> anyhow::Result { let summary = format!("Set the cycle exchange rate to {}.", rate.clone()); let xdr_permyriad_per_icp = rate.to_string(); let args = vec![ @@ -452,13 +453,17 @@ pub fn set_xdr_rate(rate: u64, nns_url: &Url) -> anyhow::Result { "--xdr-permyriad-per-icp", &xdr_permyriad_per_icp, ]; - call_extension_bundled_binary("ic-admin", args) + call_extension_bundled_binary(dfx_cache_path, "ic-admin", args) .map_err(|e| anyhow!("Call to propose to set xdr rate failed: {e}")) } /// Sets the subnets the CMC is authorized to create canisters in. #[context("Failed to authorize a subnet for use by the cycles management canister. The CMC may not be able to create canisters.")] -pub fn set_cmc_authorized_subnets(nns_url: &Url, subnet: &str) -> anyhow::Result { +pub fn set_cmc_authorized_subnets( + nns_url: &Url, + subnet: &str, + dfx_cache_path: &Path, +) -> anyhow::Result { let summary = format!( "Authorize the Cycles Minting Canister to create canisters in the subnet '{}'.", subnet.clone() @@ -475,7 +480,7 @@ pub fn set_cmc_authorized_subnets(nns_url: &Url, subnet: &str) -> anyhow::Result "--subnets", subnet, ]; - call_extension_bundled_binary("ic-admin", args) + call_extension_bundled_binary(dfx_cache_path, "ic-admin", args) .map_err(|e| anyhow!("Call to propose to set authorized subnets failed: {e}")) } @@ -488,23 +493,20 @@ pub fn upload_nns_sns_wasms_canister_wasms(dfx_cache_path: &Path) -> anyhow::Res .. } in SNS_CANISTERS { - let wasm_path = nns_wasm_dir(dfx_cache_path) - .join(wasm_name) - .display() - .to_string(); + let wasm_path = nns_wasm_dir(dfx_cache_path).join(wasm_name); let args = vec![ - "add-sns-wasm-for-tests", - "--network", - "local", - "--override-sns-wasm-canister-id-for-tests", - NNS_SNS_WASM.canister_id, - "--wasm-file", - &wasm_path, - upload_name, + "add-sns-wasm-for-tests".into(), + "--network".into(), + "local".into(), + "--override-sns-wasm-canister-id-for-tests".into(), + NNS_SNS_WASM.canister_id.into(), + "--wasm-file".into(), + wasm_path.clone().into_os_string(), + upload_name.into(), ]; - call_extension_bundled_binary("sns-cli", &args) + call_extension_bundled_binary(dfx_cache_path,"sns-cli", &args) .map_err(|e| anyhow!( - "Failed to upload {upload_name} from {wasm_path} to the nns-sns-wasm canister by calling `sns-cli` with args {args:?}: {e}" + "Failed to upload {upload_name} from {wasm_path:?} to the nns-sns-wasm canister by calling `sns-cli` with args {args:?}: {e}" ))?; } Ok(()) diff --git a/extensions/sns/build.rs b/extensions/sns/build.rs index 8570d373..5d8a598c 100644 --- a/extensions/sns/build.rs +++ b/extensions/sns/build.rs @@ -1,3 +1,5 @@ +use std::path::Path; + const REPLICA_REV: &str = "90e2799c255733409d0e61682685afcc2431c928"; const BINARY_DEPENDENCIES: &[(&str, &str)] = &[ @@ -6,11 +8,9 @@ const BINARY_DEPENDENCIES: &[(&str, &str)] = &[ ]; fn main() { + let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); for (binary_name, renamed_binary_name) in BINARY_DEPENDENCIES { - dfx_extensions_utils::download_dependencies::download_ic_binary( - REPLICA_REV, - binary_name, - renamed_binary_name, - ); + let destination_path = Path::new(&manifest_dir).join(renamed_binary_name); + dfx_extensions_utils::download_ic_binary(REPLICA_REV, binary_name, &destination_path); } } diff --git a/extensions/sns/src/commands/config/create.rs b/extensions/sns/src/commands/config/create.rs index 9eb90595..927a5e37 100644 --- a/extensions/sns/src/commands/config/create.rs +++ b/extensions/sns/src/commands/config/create.rs @@ -1,4 +1,6 @@ //! Code for executing `dfx sns config create` +use std::path::Path; + use crate::create_config::create_config; use crate::CONFIG_FILE_NAME; use clap::Parser; @@ -9,14 +11,14 @@ use dfx_core::config::model::dfinity::Config; pub struct CreateOpts {} /// Executes `dfx sns config create` -pub fn exec(_opts: CreateOpts) -> anyhow::Result<()> { +pub fn exec(_opts: CreateOpts, dfx_cache_path: &Path) -> anyhow::Result<()> { let sns_config_path = if let Some(config) = Config::from_current_dir()? { config.get_project_root().join(CONFIG_FILE_NAME) } else { anyhow::bail!(crate::errors::DFXJSON_NOT_FOUND); }; - create_config(&sns_config_path)?; + create_config(&sns_config_path, dfx_cache_path)?; println!( "Created SNS configuration at: {}", sns_config_path.display() diff --git a/extensions/sns/src/commands/config/mod.rs b/extensions/sns/src/commands/config/mod.rs index 9cf65e88..02dd3066 100644 --- a/extensions/sns/src/commands/config/mod.rs +++ b/extensions/sns/src/commands/config/mod.rs @@ -1,4 +1,6 @@ //! Code for the command line `dfx sns config`. +use std::path::Path; + use clap::Parser; mod create; @@ -27,9 +29,9 @@ enum SubCommand { } /// Executes `dfx sns config` and its subcommands. -pub fn exec(opts: SnsConfigOpts) -> anyhow::Result<()> { +pub fn exec(opts: SnsConfigOpts, dfx_cache_path: &Path) -> anyhow::Result<()> { match opts.subcmd { - SubCommand::Create(v) => create::exec(v), - SubCommand::Validate(v) => validate::exec(v), + SubCommand::Create(v) => create::exec(v, dfx_cache_path), + SubCommand::Validate(v) => validate::exec(v, dfx_cache_path), } } diff --git a/extensions/sns/src/commands/config/validate.rs b/extensions/sns/src/commands/config/validate.rs index b02be30a..7c7859ef 100644 --- a/extensions/sns/src/commands/config/validate.rs +++ b/extensions/sns/src/commands/config/validate.rs @@ -1,4 +1,6 @@ //! Code for executing `dfx sns config validate` +use std::path::Path; + use crate::validate_config::validate_config; use crate::CONFIG_FILE_NAME; use clap::Parser; @@ -9,11 +11,11 @@ use dfx_core::config::model::dfinity::Config; pub struct ValidateOpts {} /// Executes `dfx sns config validate` -pub fn exec(_opts: ValidateOpts) -> anyhow::Result<()> { +pub fn exec(_opts: ValidateOpts, dfx_cache_path: &Path) -> anyhow::Result<()> { let sns_config_path = if let Some(config) = Config::from_current_dir()? { config.get_project_root().join(CONFIG_FILE_NAME) } else { anyhow::bail!(crate::errors::DFXJSON_NOT_FOUND); }; - validate_config(&sns_config_path).map(|stdout| println!("{}", stdout)) + validate_config(&sns_config_path, dfx_cache_path).map(|stdout| println!("{}", stdout)) } diff --git a/extensions/sns/src/commands/deploy.rs b/extensions/sns/src/commands/deploy.rs index 4568793f..efae4aee 100644 --- a/extensions/sns/src/commands/deploy.rs +++ b/extensions/sns/src/commands/deploy.rs @@ -1,4 +1,6 @@ //! Code for the command line `dfx sns deploy`. +use std::path::Path; + use crate::deploy::deploy_sns; use crate::CONFIG_FILE_NAME; use clap::Parser; @@ -13,7 +15,7 @@ use dfx_core::config::model::dfinity::Config; pub struct DeployOpts {} /// Executes the command line `dfx sns deploy`. -pub fn exec(_opts: DeployOpts) -> anyhow::Result<()> { +pub fn exec(_opts: DeployOpts, dfx_cache_path: &Path) -> anyhow::Result<()> { println!("Creating SNS canisters. This typically takes about one minute..."); let sns_config_path = if let Some(config) = Config::from_current_dir()? { config.get_project_root().join(CONFIG_FILE_NAME) @@ -21,6 +23,6 @@ pub fn exec(_opts: DeployOpts) -> anyhow::Result<()> { anyhow::bail!(crate::errors::DFXJSON_NOT_FOUND); }; - println!("{}", deploy_sns(&sns_config_path)?); + println!("{}", deploy_sns(&sns_config_path, dfx_cache_path)?); Ok(()) } diff --git a/extensions/sns/src/create_config.rs b/extensions/sns/src/create_config.rs index 4d7111ca..67c4d2b6 100644 --- a/extensions/sns/src/create_config.rs +++ b/extensions/sns/src/create_config.rs @@ -7,13 +7,13 @@ use dfx_extensions_utils::call_extension_bundled_binary; /// Ceates an SNS configuration template. #[context("Failed to create sns config at {}.", path.display())] -pub fn create_config(path: &Path) -> anyhow::Result<()> { +pub fn create_config(path: &Path, dfx_cache_path: &Path) -> anyhow::Result<()> { let args = vec![ OsString::from("init-config-file"), OsString::from("--init-config-file-path"), OsString::from(path), OsString::from("new"), ]; - call_extension_bundled_binary("sns-cli", &args)?; + call_extension_bundled_binary(dfx_cache_path, "sns-cli", &args)?; Ok(()) } diff --git a/extensions/sns/src/deploy.rs b/extensions/sns/src/deploy.rs index 8ac1e4fd..2e959524 100644 --- a/extensions/sns/src/deploy.rs +++ b/extensions/sns/src/deploy.rs @@ -8,7 +8,7 @@ use dfx_extensions_utils::call_extension_bundled_binary; /// Creates an SNS. This requires funds but no proposal. #[context("Failed to deploy SNS with config: {}", path.display())] -pub fn deploy_sns(path: &Path) -> anyhow::Result { +pub fn deploy_sns(path: &Path, dfx_cache_path: &Path) -> anyhow::Result { // Note: It MAY be possible to get the did file location using existing sdk methods. let did_file = "candid/nns-sns-wasm.did"; if !Path::new(did_file).exists() { @@ -30,7 +30,7 @@ pub fn deploy_sns(path: &Path) -> anyhow::Result { OsString::from("--save-to"), OsString::from(canister_ids_file), ]; - call_extension_bundled_binary("sns-cli", &args).map(|stdout| { + call_extension_bundled_binary(dfx_cache_path, "sns-cli", &args).map(|stdout| { format!( "Deployed SNS:\nSNS config: {}\nCanister ID file: {}\n\n{}", path.display(), diff --git a/extensions/sns/src/main.rs b/extensions/sns/src/main.rs index a335827f..d011989b 100644 --- a/extensions/sns/src/main.rs +++ b/extensions/sns/src/main.rs @@ -60,9 +60,9 @@ fn main() -> anyhow::Result<()> { })?; match opts.subcmd { - SubCommand::Config(v) => commands::config::exec(v), + SubCommand::Config(v) => commands::config::exec(v, &dfx_cache_path), SubCommand::Import(v) => commands::import::exec(v, &dfx_cache_path), - SubCommand::Deploy(v) => commands::deploy::exec(v), + SubCommand::Deploy(v) => commands::deploy::exec(v, &dfx_cache_path), SubCommand::Download(v) => commands::download::exec(v, &dfx_cache_path), }?; Ok(()) diff --git a/extensions/sns/src/validate_config.rs b/extensions/sns/src/validate_config.rs index 4281d917..02c793c5 100644 --- a/extensions/sns/src/validate_config.rs +++ b/extensions/sns/src/validate_config.rs @@ -7,7 +7,7 @@ use dfx_extensions_utils::call_extension_bundled_binary; /// Checks whether an SNS configuration file is valid. #[context("Failed to validate SNS config at {}.", path.display())] -pub fn validate_config(path: &Path) -> anyhow::Result { +pub fn validate_config(path: &Path, dfx_cache_path: &Path) -> anyhow::Result { let args = vec![ OsString::from("init-config-file"), OsString::from("--init-config-file-path"), @@ -16,6 +16,6 @@ pub fn validate_config(path: &Path) -> anyhow::Result { ]; // current binary - call_extension_bundled_binary("sns-cli", &args) + call_extension_bundled_binary(dfx_cache_path, "sns-cli", &args) .map(|_| format!("SNS config file is valid: {}", path.display())) }