Skip to content

Commit 177d6de

Browse files
Customizations files
1 parent e6bb694 commit 177d6de

5 files changed

Lines changed: 772 additions & 4 deletions

File tree

crates/icp-cli/src/commands/deploy.rs

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use anyhow::anyhow;
2-
use candid::{CandidType, Principal};
2+
use candid::{CandidType, IDLArgs, Principal};
33
use clap::Args;
44
use futures::{StreamExt, future::try_join_all, stream::FuturesOrdered};
55
use ic_agent::Agent;
@@ -11,7 +11,8 @@ use icp::{
1111
};
1212
use icp_canister_interfaces::candid_ui::MAINNET_CANDID_UI_CID;
1313
use serde::Serialize;
14-
use std::collections::BTreeMap;
14+
use std::collections::{BTreeMap, HashMap};
15+
use std::sync::Arc;
1516
use tracing::info;
1617

1718
use 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

crates/icp-cli/src/operations/bundle.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ use icp::{
2828
use snafu::{ResultExt, Snafu};
2929
use tar::Builder;
3030

31-
use crate::operations::build::{BuildManyError, build_many_with_progress_bar};
31+
use crate::operations::{
32+
build::{BuildManyError, build_many_with_progress_bar},
33+
customize::CUSTOMIZE_FILE,
34+
};
3235

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

79+
#[snafu(display("failed to read '{path}'"))]
80+
ReadCustomize { path: PathBuf, source: fs::IoError },
81+
7682
#[snafu(display("failed to serialize bundle manifest"))]
7783
SerializeManifest { source: serde_yaml::Error },
7884

@@ -221,9 +227,22 @@ pub(crate) async fn create_bundle(
221227
environments,
222228
};
223229

230+
let customize_path = project_dir.join(CUSTOMIZE_FILE);
231+
let customize_bytes = match fs::read(&customize_path) {
232+
Ok(bytes) => Some(bytes),
233+
Err(e) if e.kind() == std::io::ErrorKind::NotFound => None,
234+
Err(source) => {
235+
return Err(BundleError::ReadCustomize {
236+
path: customize_path,
237+
source,
238+
});
239+
}
240+
};
241+
224242
write_archive(
225243
output,
226244
&bundle_manifest,
245+
customize_bytes.as_deref(),
227246
&bundle_artifacts,
228247
&init_args_files,
229248
)
@@ -520,6 +539,7 @@ async fn inline_environments(
520539
fn write_archive(
521540
output: &Path,
522541
bundle_manifest: &ProjectManifest,
542+
customize_bytes: Option<&[u8]>,
523543
artifacts: &BundleArtifacts,
524544
init_args_files: &[InitArgsFile],
525545
) -> Result<(), BundleError> {
@@ -539,6 +559,10 @@ fn write_archive(
539559

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

562+
if let Some(customize_bytes) = customize_bytes {
563+
append_bytes(&mut archive, CUSTOMIZE_FILE, customize_bytes)?;
564+
}
565+
542566
for nb in &artifacts.wasms {
543567
append_bytes(&mut archive, &nb.archive_path, &nb.bytes)?;
544568
}

0 commit comments

Comments
 (0)