Skip to content

Commit

Permalink
feat: Read install arg from file (#3431)
Browse files Browse the repository at this point in the history
# Description
This PR adds support for providing install arguments from a file.

This is motivated by these issues:

* It is impossible to pass a large canister init arg as the length of shell arguments is limited by the terminal (which in turn may be limited by the operating system).
* For shorter arguments, it is more convenient to write `--argument-file somefile.hex` than `--argument "$(cat somefile.hex)"`.
* The `dfx canister call` method supports `--argument-file` for similar reasons.  It would be nice to be consistent.
  • Loading branch information
bitdivine authored Nov 6, 2023
1 parent abc51b1 commit 12a8ea1
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 3 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

# UNRELEASED

=== feat: Read dfx canister install argument from a file

Enables passing large arguments that cannot be passed directly in the command line using the `--argument-file` flag. For example `dfx canister install --argument-file ./my/argument/file.txt my_canister_name`.


### feat: change `list_permitted` and `list_authorized` to an update call.

This requires the `list_authorized` and `list_permitted` methods to be called as an update and disables the ability to
Expand Down
1 change: 1 addition & 0 deletions docs/cli-reference/dfx-canister.md
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ You can use the following optional flags with the `dfx canister install` command

| Flag | Description |
|-----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `--argument-file` | Specifies the file from which to read the argument to pass to the init method. Stdin may be referred to as `-`. |
| `--async-call` | Enables you to continue without waiting for the result of the installation to be returned by polling the Internet Computer or the local canister execution environment. |
| `--upgrade-unchanged` | Upgrade the canister even if the .wasm did not change. |

Expand Down
27 changes: 26 additions & 1 deletion e2e/tests-dfx/install.bash
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,32 @@ teardown() {
assert_contains db07e7e24f6f8ddf53c33a610713259a7c1eb71c270b819ebd311e2d223267f0
}

@test "installing one canister with an argument succeeds" {
dfx_start
assert_command dfx canister create e2e_project_backend
assert_command dfx build e2e_project_backend
assert_command dfx canister install e2e_project_backend --argument '()'
}

@test "installing with an argument in a file succeeds" {
dfx_start
assert_command dfx canister create e2e_project_backend
assert_command dfx build e2e_project_backend
TMPFILE="$(mktemp)"
echo '()' >"$TMPFILE"
assert_command dfx canister install e2e_project_backend --argument-file "$TMPFILE"
}

@test "installing with an argument on stdin succeeds" {
dfx_start
assert_command dfx canister create e2e_project_backend
assert_command dfx build e2e_project_backend
TMPFILE="$(mktemp)"
echo '()' >"$TMPFILE"
assert_command dfx canister install e2e_project_backend --argument-file - <"$TMPFILE"
}

@test "installing multiple canisters with arguments fails" {
assert_command_fail dfx canister install --all --argument hello
assert_command_fail dfx canister install --all --argument '()'
assert_contains "error: the argument '--all' cannot be used with '--argument <ARGUMENT>'"
}
23 changes: 21 additions & 2 deletions src/dfx/src/commands/canister/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ use crate::lib::environment::Environment;
use crate::lib::error::DfxResult;
use crate::lib::operations::canister::install_canister::install_canister;
use crate::lib::root_key::fetch_root_key_if_needed;
use crate::util::clap::parsers::file_or_stdin_parser;
use crate::util::get_candid_init_type;
use crate::{lib::canister_info::CanisterInfo, util::blob_from_arguments};
use crate::{
lib::canister_info::CanisterInfo,
util::{arguments_from_file, blob_from_arguments},
};
use dfx_core::identity::CallSender;

use anyhow::{anyhow, bail, Context};
Expand Down Expand Up @@ -40,9 +44,17 @@ pub struct CanisterInstallOpts {
upgrade_unchanged: bool,

/// Specifies the argument to pass to the method.
#[arg(long)]
#[arg(long, conflicts_with("argument_file"))]
argument: Option<String>,

/// Specifies the file from which to read the argument to pass to the method.
#[arg(
long,
value_parser = file_or_stdin_parser,
conflicts_with("argument")
)]
argument_file: Option<PathBuf>,

/// Specifies the data type for the argument when making the call using an argument.
#[arg(long, requires("argument"), value_parser = ["idl", "raw"])]
argument_type: Option<String>,
Expand Down Expand Up @@ -107,7 +119,14 @@ pub async fn exec(

let canister_id =
Principal::from_text(canister).or_else(|_| canister_id_store.get(canister))?;

let arguments_from_file = opts
.argument_file
.map(|v| arguments_from_file(&v))
.transpose()?;
let arguments = opts.argument.as_deref();
let arguments = arguments_from_file.as_deref().or(arguments);

let arg_type = opts.argument_type.as_deref();
let canister_info = config.as_ref()
.ok_or_else(|| anyhow!("Cannot find dfx configuration file in the current working directory. Did you forget to create one?"))
Expand Down

0 comments on commit 12a8ea1

Please sign in to comment.