diff --git a/CHANGELOG.md b/CHANGELOG.md index 9dd9043c0c..ab402d7386 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,6 +63,11 @@ Note that the default value of `allow_raw_access` is still `true`. Removed the logic for calling a different version of dfx based on DFX_VERSION or the `dfx` field in dfx.json. This is now performed by dfxvm. +### feat: --always-assist flag for `dfx canister call/install/sign and dfx deploy` + +When all the arguments are optional, dfx automatically provides a `null` value when no arguments are provided. +`--always-assist` flag enables the candid assist feature for optional arguments, instead of providing a default `null` value. + ## Dependencies ### Replica diff --git a/src/dfx/src/commands/canister/call.rs b/src/dfx/src/commands/canister/call.rs index 400291a8e6..6ec0d2b336 100644 --- a/src/dfx/src/commands/canister/call.rs +++ b/src/dfx/src/commands/canister/call.rs @@ -51,7 +51,12 @@ pub struct CanisterCallOpts { update: bool, /// Specifies the config for generating random argument. - #[arg(long, conflicts_with("argument"), conflicts_with("argument_file"))] + #[arg( + long, + conflicts_with("argument"), + conflicts_with("argument_file"), + conflicts_with("always_assist") + )] random: Option, /// Specifies the format for displaying the method's return result. @@ -69,6 +74,15 @@ pub struct CanisterCallOpts { /// for project canisters. #[arg(long)] candid: Option, + + /// Always use Candid assist when the argument types are all optional. + #[arg( + long, + conflicts_with("argument"), + conflicts_with("argument_file"), + conflicts_with("random") + )] + always_assist: bool, } #[derive(Clone, CandidType, Deserialize, Debug)] @@ -282,6 +296,7 @@ pub async fn exec( argument_type.as_deref(), &method_type, false, + opts.always_assist, )?; // amount has been validated by cycle_amount_validator diff --git a/src/dfx/src/commands/canister/delete.rs b/src/dfx/src/commands/canister/delete.rs index e2341a8cb0..43db297d6c 100644 --- a/src/dfx/src/commands/canister/delete.rs +++ b/src/dfx/src/commands/canister/delete.rs @@ -199,7 +199,7 @@ async fn delete_canister( "Installing temporary wallet in canister {} to enable transfer of cycles.", canister ); - let args = blob_from_arguments(None, None, None, None, &None, false)?; + let args = blob_from_arguments(None, None, None, None, &None, false, false)?; let mode = InstallMode::Reinstall; let install_builder = mgr .install_code(&canister_id, &wasm_module) diff --git a/src/dfx/src/commands/canister/install.rs b/src/dfx/src/commands/canister/install.rs index dff780db7c..fbaeda35b6 100644 --- a/src/dfx/src/commands/canister/install.rs +++ b/src/dfx/src/commands/canister/install.rs @@ -64,6 +64,15 @@ pub struct CanisterInstallOpts { /// Skips upgrading the asset canister, to only install the assets themselves. #[arg(long)] no_asset_upgrade: bool, + + /// Always use Candid assist when the argument types are all optional. + #[arg( + long, + conflicts_with("argument"), + conflicts_with("argument_file"), + conflicts_with("yes") + )] + always_assist: bool, } pub async fn exec( @@ -97,6 +106,7 @@ pub async fn exec( argument_type.as_deref(), &None, true, + opts.always_assist, )?; let wasm_module = dfx_core::fs::read(wasm_path)?; let mode = mode.context("The install mode cannot be auto when using --wasm")?; @@ -157,6 +167,7 @@ pub async fn exec( opts.yes, None, opts.no_asset_upgrade, + opts.always_assist, ) .await .map_err(Into::into) @@ -176,6 +187,7 @@ pub async fn exec( opts.yes, env_file.as_deref(), opts.no_asset_upgrade, + opts.always_assist, ) .await .map_err(Into::into) @@ -219,6 +231,7 @@ pub async fn exec( opts.yes, env_file.as_deref(), opts.no_asset_upgrade, + opts.always_assist, ) .await?; } diff --git a/src/dfx/src/commands/canister/sign.rs b/src/dfx/src/commands/canister/sign.rs index 1356c1b427..0a1051ae79 100644 --- a/src/dfx/src/commands/canister/sign.rs +++ b/src/dfx/src/commands/canister/sign.rs @@ -43,7 +43,12 @@ pub struct CanisterSignOpts { update: bool, /// Specifies the config for generating random argument. - #[arg(long, conflicts_with("argument"), conflicts_with("argument_file"))] + #[arg( + long, + conflicts_with("argument"), + conflicts_with("argument_file"), + conflicts_with("always_assist") + )] random: Option, /// Specifies how long the message will be valid in seconds, default to be 300s (5 minutes) @@ -53,6 +58,15 @@ pub struct CanisterSignOpts { /// Specifies the output file name. #[arg(long, default_value = "message.json")] file: PathBuf, + + /// Always use Candid assist when the argument types are all optional. + #[arg( + long, + conflicts_with("argument"), + conflicts_with("argument_file"), + conflicts_with("random") + )] + always_assist: bool, } pub async fn exec( @@ -99,6 +113,7 @@ pub async fn exec( argument_type.as_deref(), &method_type, false, + opts.always_assist, )?; let agent = env.get_agent(); diff --git a/src/dfx/src/commands/deploy.rs b/src/dfx/src/commands/deploy.rs index 96f463e3d8..ea9ddff556 100644 --- a/src/dfx/src/commands/deploy.rs +++ b/src/dfx/src/commands/deploy.rs @@ -113,6 +113,15 @@ pub struct DeployOpts { #[command(flatten)] subnet_selection: SubnetSelectionOpt, + + /// Always use Candid assist when the argument types are all optional. + #[arg( + long, + conflicts_with("argument"), + conflicts_with("argument_file"), + conflicts_with("yes") + )] + always_assist: bool, } pub fn exec(env: &dyn Environment, opts: DeployOpts) -> DfxResult { @@ -192,6 +201,7 @@ pub fn exec(env: &dyn Environment, opts: DeployOpts) -> DfxResult { env_file, opts.no_asset_upgrade, &mut subnet_selection, + opts.always_assist, ))?; if matches!(deploy_mode, NormalDeploy | ForceReinstallSingleCanister(_)) { diff --git a/src/dfx/src/lib/integrations/mod.rs b/src/dfx/src/lib/integrations/mod.rs index ee35e4062d..0edc4c77ae 100644 --- a/src/dfx/src/lib/integrations/mod.rs +++ b/src/dfx/src/lib/integrations/mod.rs @@ -49,7 +49,7 @@ pub async fn initialize_integration_canister( }; try_create_canister(agent, logger, &canister_id, &pulled_canister).await?; - let install_arg = blob_from_arguments(None, Some(init_arg), None, None, &None, true)?; + let install_arg = blob_from_arguments(None, Some(init_arg), None, None, &None, true, false)?; install_canister(agent, logger, &canister_id, wasm, install_arg, name).await } diff --git a/src/dfx/src/lib/operations/canister/deploy_canisters.rs b/src/dfx/src/lib/operations/canister/deploy_canisters.rs index 3bacf81cd2..a50da48962 100644 --- a/src/dfx/src/lib/operations/canister/deploy_canisters.rs +++ b/src/dfx/src/lib/operations/canister/deploy_canisters.rs @@ -38,6 +38,7 @@ pub enum DeployMode { } #[context("Failed while trying to deploy canisters.")] +#[allow(clippy::too_many_arguments)] pub async fn deploy_canisters( env: &dyn Environment, some_canister: Option<&str>, @@ -55,6 +56,7 @@ pub async fn deploy_canisters( env_file: Option, no_asset_upgrade: bool, subnet_selection: &mut SubnetSelectionType, + always_assist: bool, ) -> DfxResult { let log = env.get_logger(); @@ -156,6 +158,7 @@ pub async fn deploy_canisters( skip_consent, env_file.as_deref(), no_asset_upgrade, + always_assist, ) .await?; info!(log, "Deployed canisters."); @@ -307,6 +310,7 @@ async fn install_canisters( skip_consent: bool, env_file: Option<&Path>, no_asset_upgrade: bool, + always_assist: bool, ) -> DfxResult { info!(env.get_logger(), "Installing canisters..."); @@ -340,6 +344,7 @@ async fn install_canisters( skip_consent, env_file, no_asset_upgrade, + always_assist, ) .await?; } diff --git a/src/dfx/src/lib/operations/canister/install_canister.rs b/src/dfx/src/lib/operations/canister/install_canister.rs index 489990981d..f37a415c66 100644 --- a/src/dfx/src/lib/operations/canister/install_canister.rs +++ b/src/dfx/src/lib/operations/canister/install_canister.rs @@ -50,6 +50,7 @@ pub async fn install_canister( skip_consent: bool, env_file: Option<&Path>, no_asset_upgrade: bool, + always_assist: bool, ) -> DfxResult { let log = env.get_logger(); let agent = env.get_agent(); @@ -165,8 +166,15 @@ The command line value will be used.", (None, Some(_)) => (argument_from_json, Some("idl")), // `init_arg` in dfx.json is always in Candid format (None, None) => (None, None), }; - let install_args = - blob_from_arguments(Some(env), argument, None, argument_type, &init_type, true)?; + let install_args = blob_from_arguments( + Some(env), + argument, + None, + argument_type, + &init_type, + true, + always_assist, + )?; if let Some(timestamp) = canister_id_store.get_timestamp(canister_info.get_name()) { let new_timestamp = playground_install_code( env, diff --git a/src/dfx/src/util/mod.rs b/src/dfx/src/util/mod.rs index 6657144f4a..9defe3d3b9 100644 --- a/src/dfx/src/util/mod.rs +++ b/src/dfx/src/util/mod.rs @@ -197,6 +197,7 @@ pub fn blob_from_arguments( arg_type: Option<&str>, method_type: &Option<(TypeEnv, Function)>, is_init_arg: bool, + always_assist: bool, ) -> DfxResult> { let arg_type = arg_type.unwrap_or("idl"); match arg_type { @@ -226,6 +227,7 @@ pub fn blob_from_arguments( .args .iter() .all(|t| matches!(t.as_ref(), TypeInner::Opt(_))) + && !always_assist { // If the user provided no arguments, and if all the expected arguments are // optional, then use null values.