Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 44 additions & 3 deletions crates/icp-cli/src/commands/deploy.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use anyhow::anyhow;
use candid::{CandidType, Principal};
use candid::{CandidType, IDLArgs, Principal};
use clap::Args;
use futures::{StreamExt, future::try_join_all, stream::FuturesOrdered};
use ic_agent::Agent;
Expand All @@ -11,7 +11,8 @@ use icp::{
};
use icp_canister_interfaces::candid_ui::MAINNET_CANDID_UI_CID;
use serde::Serialize;
use std::collections::BTreeMap;
use std::collections::{BTreeMap, HashMap};
use std::sync::Arc;
use tracing::info;

use crate::{
Expand All @@ -21,6 +22,7 @@ use crate::{
build::build_many_with_progress_bar,
candid_compat::check_candid_compatibility_many,
create::{CreateOperation, CreateTarget},
customize,
install::{install_many, resolve_install_mode_and_status},
settings::sync_settings_many,
sync::sync_many,
Expand Down Expand Up @@ -119,6 +121,38 @@ pub(crate) async fn exec(ctx: &Context, args: &DeployArgs) -> Result<(), anyhow:
)
.await?;

// Collect interactive init arg customizations before the build so the user
// fills in all prompts upfront, uninterrupted by build output.
let customize_overrides: Arc<HashMap<String, IDLArgs>> = {
let project = ctx.project.load().await.map_err(|e| anyhow!(e))?;
let customize_path = project.dir.join(customize::CUSTOMIZE_FILE);
match customize::load_customize_manifest(&project.dir).map_err(|e| anyhow!(e))? {
None => Arc::new(HashMap::new()),
Some(manifest) => {
Comment on lines +124 to +131
let init_args: HashMap<String, Option<icp::InitArgs>> = cnames
.iter()
.map(|name| {
let ia = env
.get_canister_info(name)
.ok()
.and_then(|(_, info)| info.init_args.clone());
(name.clone(), ia)
})
.collect();
Arc::new(
customize::prompt_customizations(
&manifest,
&cnames,
&init_args,
args.yes,
&customize_path,
)
.map_err(|e| anyhow!(e))?,
)
}
}
};

// Build the selected canisters
info!("Building canisters:");

Expand Down Expand Up @@ -263,6 +297,7 @@ pub(crate) async fn exec(ctx: &Context, args: &DeployArgs) -> Result<(), anyhow:
let canisters = try_join_all(cnames.iter().map(|name| {
let environment_selection = environment_selection.clone();
let agent = agent.clone();
let co = customize_overrides.clone();
async move {
let cid = ctx
.get_canister_id_for_env(
Expand All @@ -279,9 +314,15 @@ pub(crate) async fn exec(ctx: &Context, args: &DeployArgs) -> Result<(), anyhow:
let (_canister_path, canister_info) =
env.get_canister_info(name).map_err(|e| anyhow!(e))?;

// CLI --args/--args-file take priority over manifest init_args
// Priority: CLI --args/--args-file > icp_customize.yaml prompts > manifest init_args
let init_args_bytes = if args.args_opt.is_some() {
args.args_opt.resolve_bytes()?
} else if let Some(customized) = co.get(name) {
Some(
customized
.to_bytes()
.map_err(|e| anyhow!("failed to serialize customized init args: {e}"))?,
)
} else {
canister_info
.init_args
Expand Down
26 changes: 25 additions & 1 deletion crates/icp-cli/src/operations/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ use icp::{
use snafu::{ResultExt, Snafu};
use tar::Builder;

use crate::operations::build::{BuildManyError, build_many_with_progress_bar};
use crate::operations::{
build::{BuildManyError, build_many_with_progress_bar},
customize::CUSTOMIZE_FILE,
};

#[derive(Debug, Snafu)]
pub enum BundleError {
Expand Down Expand Up @@ -73,6 +76,9 @@ pub enum BundleError {
#[snafu(display("failed to read init_args file '{path}'"))]
ReadInitArgs { path: PathBuf, source: fs::IoError },

#[snafu(display("failed to read '{path}'"))]
ReadCustomize { path: PathBuf, source: fs::IoError },

#[snafu(display("failed to serialize bundle manifest"))]
SerializeManifest { source: serde_yaml::Error },

Expand Down Expand Up @@ -221,9 +227,22 @@ pub(crate) async fn create_bundle(
environments,
};

let customize_path = project_dir.join(CUSTOMIZE_FILE);
let customize_bytes = match fs::read(&customize_path) {
Ok(bytes) => Some(bytes),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => None,
Err(source) => {
return Err(BundleError::ReadCustomize {
path: customize_path,
source,
});
Comment on lines +230 to +238
}
};

write_archive(
output,
&bundle_manifest,
customize_bytes.as_deref(),
&bundle_artifacts,
&init_args_files,
)
Expand Down Expand Up @@ -520,6 +539,7 @@ async fn inline_environments(
fn write_archive(
output: &Path,
bundle_manifest: &ProjectManifest,
customize_bytes: Option<&[u8]>,
artifacts: &BundleArtifacts,
init_args_files: &[InitArgsFile],
) -> Result<(), BundleError> {
Expand All @@ -539,6 +559,10 @@ fn write_archive(

append_bytes(&mut archive, "icp.yaml", manifest_yaml.as_bytes())?;

if let Some(customize_bytes) = customize_bytes {
append_bytes(&mut archive, CUSTOMIZE_FILE, customize_bytes)?;
}

for nb in &artifacts.wasms {
append_bytes(&mut archive, &nb.archive_path, &nb.bytes)?;
}
Expand Down
Loading
Loading