11use anyhow:: anyhow;
2- use candid:: { CandidType , Principal } ;
2+ use candid:: { CandidType , IDLArgs , Principal } ;
33use clap:: Args ;
44use futures:: { StreamExt , future:: try_join_all, stream:: FuturesOrdered } ;
55use ic_agent:: Agent ;
@@ -11,7 +11,8 @@ use icp::{
1111} ;
1212use icp_canister_interfaces:: candid_ui:: MAINNET_CANDID_UI_CID ;
1313use serde:: Serialize ;
14- use std:: collections:: BTreeMap ;
14+ use std:: collections:: { BTreeMap , HashMap } ;
15+ use std:: sync:: Arc ;
1516use tracing:: info;
1617
1718use crate :: {
@@ -21,6 +22,7 @@ use crate::{
2122 build:: build_many_with_progress_bar,
2223 candid_compat:: check_candid_compatibility_many,
2324 create:: { CreateOperation , CreateTarget } ,
25+ customize,
2426 install:: { install_many, resolve_install_mode_and_status} ,
2527 settings:: sync_settings_many,
2628 sync:: sync_many,
@@ -119,6 +121,38 @@ pub(crate) async fn exec(ctx: &Context, args: &DeployArgs) -> Result<(), anyhow:
119121 )
120122 . await ?;
121123
124+ // Collect interactive init arg customizations before the build so the user
125+ // fills in all prompts upfront, uninterrupted by build output.
126+ let customize_overrides: Arc < HashMap < String , IDLArgs > > = {
127+ let project = ctx. project . load ( ) . await . map_err ( |e| anyhow ! ( e) ) ?;
128+ let customize_path = project. dir . join ( customize:: CUSTOMIZE_FILE ) ;
129+ match customize:: load_customize_manifest ( & project. dir ) . map_err ( |e| anyhow ! ( e) ) ? {
130+ None => Arc :: new ( HashMap :: new ( ) ) ,
131+ Some ( manifest) => {
132+ let init_args: HashMap < String , Option < icp:: InitArgs > > = cnames
133+ . iter ( )
134+ . map ( |name| {
135+ let ia = env
136+ . get_canister_info ( name)
137+ . ok ( )
138+ . and_then ( |( _, info) | info. init_args . clone ( ) ) ;
139+ ( name. clone ( ) , ia)
140+ } )
141+ . collect ( ) ;
142+ Arc :: new (
143+ customize:: prompt_customizations (
144+ & manifest,
145+ & cnames,
146+ & init_args,
147+ args. yes ,
148+ & customize_path,
149+ )
150+ . map_err ( |e| anyhow ! ( e) ) ?,
151+ )
152+ }
153+ }
154+ } ;
155+
122156 // Build the selected canisters
123157 info ! ( "Building canisters:" ) ;
124158
@@ -263,6 +297,7 @@ pub(crate) async fn exec(ctx: &Context, args: &DeployArgs) -> Result<(), anyhow:
263297 let canisters = try_join_all ( cnames. iter ( ) . map ( |name| {
264298 let environment_selection = environment_selection. clone ( ) ;
265299 let agent = agent. clone ( ) ;
300+ let co = customize_overrides. clone ( ) ;
266301 async move {
267302 let cid = ctx
268303 . get_canister_id_for_env (
@@ -279,9 +314,15 @@ pub(crate) async fn exec(ctx: &Context, args: &DeployArgs) -> Result<(), anyhow:
279314 let ( _canister_path, canister_info) =
280315 env. get_canister_info ( name) . map_err ( |e| anyhow ! ( e) ) ?;
281316
282- // CLI --args/--args-file take priority over manifest init_args
317+ // Priority: CLI --args/--args-file > icp_customize.yaml prompts > manifest init_args
283318 let init_args_bytes = if args. args_opt . is_some ( ) {
284319 args. args_opt . resolve_bytes ( ) ?
320+ } else if let Some ( customized) = co. get ( name) {
321+ Some (
322+ customized
323+ . to_bytes ( )
324+ . map_err ( |e| anyhow ! ( "failed to serialize customized init args: {e}" ) ) ?,
325+ )
285326 } else {
286327 canister_info
287328 . init_args
0 commit comments