1use crate::canister_build::CanisterBuildProfile;
2use crate::deployment_truth::{
3 ArtifactPromotionPlanV1, DeploymentExecutorCapabilityV1, DeploymentPlanV1,
4};
5use crate::release_set::{configured_fleet_name, icp_root, workspace_root};
6use config_selection::resolve_install_config_path;
7use std::{
8 path::{Path, PathBuf},
9 time::{Instant, SystemTime, UNIX_EPOCH},
10};
11
12mod activation;
13mod artifact_promotion;
14mod build_environment;
15mod build_targets;
16mod commands;
17mod config_selection;
18mod current_execution;
19mod deployment_registration;
20mod deployment_truth_gate;
21mod execution_preflight;
22mod install_state;
23mod operations;
24mod output;
25mod phase_receipts;
26mod plan_artifacts;
27mod preparation;
28mod readiness;
29mod receipt_io;
30mod root_canister;
31mod root_cycles;
32mod root_verification;
33mod staging;
34mod state;
35mod timing;
36mod truth_check;
37
38use activation::run_root_activation_phases;
39use artifact_promotion::write_artifact_promotion_execution_receipt_for_install;
40use build_environment::BuildEnvGuard;
41#[cfg(test)]
42use commands::{
43 add_create_root_target, add_icp_environment_target, icp_canister_command_in_network,
44 is_missing_canister_id_error, parse_created_canister_id,
45};
46pub use config_selection::{
47 current_canic_project_root, discover_canic_config_choices, discover_canic_project_root_from,
48 discover_project_canic_config_choices, project_fleet_roots,
49};
50use current_execution::current_install_execution_context;
51#[cfg(test)]
52use current_execution::current_install_executor_missing_capabilities;
53pub use deployment_registration::{
54 RegisterDeploymentStateOptions, VerifyDeploymentRootOptions, register_deployment_state,
55 verify_registered_deployment_root,
56};
57#[cfg(test)]
58use deployment_truth_gate::{
59 enforce_install_deployment_truth_gate, install_deployment_truth_gate_lines,
60 install_deployment_truth_gate_receipt,
61};
62#[cfg(test)]
63use execution_preflight::write_current_install_execution_preflight_receipt;
64use install_state::{build_install_state, write_install_state_with_deployment_truth_receipt};
65#[cfg(test)]
66use operations::EmitRootManifestOperation;
67#[cfg(test)]
68use operations::{
69 BuildInstallTargetsOperation, EnsureRootCyclesOperation, InstallRootWasmOperation,
70 ResolveRootCanisterOperation, ResumeBootstrapOperation, WaitRootReadyOperation,
71};
72#[cfg(test)]
73use output::render_install_timing_summary;
74use output::{print_install_result_summary, print_install_timing_summary};
75use phase_receipts::InstallReceiptScope;
76#[cfg(test)]
77use phase_receipts::install_deployment_truth_phase_receipt;
78#[cfg(test)]
79use phase_receipts::{CompletedInstallPhase, write_completed_install_phase_receipt};
80use plan_artifacts::emit_manifest_with_deployment_truth_receipt;
81#[cfg(test)]
82use plan_artifacts::validate_plan_artifacts_with_phase;
83use preparation::prepare_install_deployment_truth;
84pub use receipt_io::latest_deployment_truth_receipt_path_from_root;
85#[cfg(test)]
86use receipt_io::write_install_deployment_truth_receipt;
87#[cfg(test)]
88use root_cycles::add_local_root_create_cycles_arg;
89#[cfg(test)]
90use root_verification::write_verified_root_state_if_unchanged;
91#[cfg(test)]
92use staging::StageReleaseSetOperation;
93#[cfg(test)]
94use staging::current_install_staging_evidence;
95use state::validate_state_name;
96#[cfg(test)]
97use state::{INSTALL_STATE_SCHEMA_VERSION, write_install_state};
98pub use state::{
99 InstallState, RootVerificationStatus, read_named_deployment_install_state,
100 read_named_deployment_install_state_from_root,
101};
102#[cfg(test)]
103use state::{deployment_install_state_path, read_deployment_install_state};
104#[cfg(test)]
105use timing::InstallTimingSummary;
106use timing::InstallTimingSummary as CurrentInstallTimingSummary;
107#[cfg(test)]
108use truth_check::current_install_deployment_truth_check_at;
109use truth_check::validate_expected_fleet_name;
110pub use truth_check::{check_install_deployment_truth, check_install_execution_preflight};
111
112#[cfg(test)]
113mod tests;
114
115#[cfg(test)]
116use commands::{parse_canister_id_json, root_init_args};
117#[cfg(test)]
118use config_selection::config_selection_error;
119#[cfg(test)]
120use readiness::parse_bootstrap_status_value;
121#[cfg(test)]
122use receipt_io::install_deployment_truth_receipt_path;
123#[cfg(test)]
124use state::legacy_fleet_install_state_path;
125
126#[derive(Clone, Debug)]
131pub struct InstallRootOptions {
132 pub root_canister: String,
133 pub root_build_target: String,
134 pub network: String,
135 pub deployment_name: Option<String>,
136 pub icp_root: Option<PathBuf>,
137 pub build_profile: Option<CanisterBuildProfile>,
138 pub ready_timeout_seconds: u64,
139 pub config_path: Option<String>,
140 pub expected_fleet: Option<String>,
141 pub interactive_config_selection: bool,
142 pub deployment_plan_override: Option<DeploymentPlanV1>,
143 pub artifact_promotion_plan_override: Option<ArtifactPromotionPlanV1>,
144}
145
146const CURRENT_INSTALL_REQUIRED_CAPABILITIES: &[DeploymentExecutorCapabilityV1] = &[
147 DeploymentExecutorCapabilityV1::CreateCanister,
148 DeploymentExecutorCapabilityV1::InstallCode,
149 DeploymentExecutorCapabilityV1::Call,
150 DeploymentExecutorCapabilityV1::Query,
151 DeploymentExecutorCapabilityV1::StageArtifact,
152];
153
154pub fn discover_current_canic_config_choices() -> Result<Vec<PathBuf>, Box<dyn std::error::Error>> {
156 let project_root = current_canic_project_root()?;
157 let choices = config_selection::discover_workspace_canic_config_choices(&project_root)?;
158 if !choices.is_empty() {
159 return Ok(choices);
160 }
161
162 if let Ok(icp_root) = icp_root()
163 && icp_root != project_root
164 {
165 return config_selection::discover_workspace_canic_config_choices(&icp_root);
166 }
167
168 Ok(choices)
169}
170
171pub fn install_root(options: InstallRootOptions) -> Result<(), Box<dyn std::error::Error>> {
173 let workspace_root = workspace_root()?;
174 let icp_root = match &options.icp_root {
175 Some(path) => path.canonicalize()?,
176 None => icp_root()?,
177 };
178 let config_path = resolve_install_config_path(
179 &icp_root,
180 options.config_path.as_deref(),
181 options.interactive_config_selection,
182 )?;
183 let _install_env = BuildEnvGuard::apply(&options.network, &config_path, &icp_root);
184 let (fleet_name, deployment_name) = resolve_install_identity(&options, &config_path)?;
185 let total_started_at = Instant::now();
186 let mut timings = CurrentInstallTimingSummary::default();
187 let network = options.network.as_str();
188 let execution_context = current_install_execution_context(&workspace_root, &icp_root, network);
189
190 println!("Installing deployment {deployment_name}");
191 println!("Fleet template {fleet_name}");
192 println!();
193 let prepared = prepare_install_deployment_truth(
194 &options,
195 &workspace_root,
196 &icp_root,
197 &config_path,
198 &deployment_name,
199 &execution_context,
200 )?;
201 timings.create_canisters = prepared.timings.create_canisters;
202 timings.build_all = prepared.timings.build_all;
203
204 let (manifest_path, emit_manifest_duration) = emit_manifest_with_deployment_truth_receipt(
205 &workspace_root,
206 &icp_root,
207 &options,
208 &config_path,
209 &deployment_name,
210 &prepared.deployment_truth_check,
211 &execution_context,
212 )?;
213 timings.emit_manifest = emit_manifest_duration;
214 let activation_timings = run_root_activation_phases(
215 InstallReceiptScope {
216 icp_root: &icp_root,
217 network,
218 deployment_name: &deployment_name,
219 check: &prepared.deployment_truth_check,
220 execution_context: Some(&execution_context),
221 },
222 &options,
223 &prepared.root_canister_id,
224 &manifest_path,
225 total_started_at,
226 )?;
227 timings.install_root = activation_timings.install_root;
228 timings.fund_root = activation_timings.fund_root;
229 timings.stage_release_set = activation_timings.stage_release_set;
230 timings.resume_bootstrap = activation_timings.resume_bootstrap;
231 timings.wait_ready = activation_timings.wait_ready;
232 timings.finalize_root_funding = activation_timings.finalize_root_funding;
233
234 print_install_timing_summary(&timings, total_started_at.elapsed());
235 let state = build_install_state(
236 &options,
237 &workspace_root,
238 &icp_root,
239 &config_path,
240 &manifest_path,
241 (&deployment_name, &fleet_name),
242 &prepared.root_canister_id,
243 )?;
244 let state_path = write_install_state_with_deployment_truth_receipt(
245 InstallReceiptScope {
246 icp_root: &icp_root,
247 network,
248 deployment_name: &deployment_name,
249 check: &prepared.deployment_truth_check,
250 execution_context: Some(&execution_context),
251 },
252 &options.network,
253 &state,
254 )?;
255 write_artifact_promotion_execution_receipt_for_install(
256 &options,
257 &icp_root,
258 network,
259 &deployment_name,
260 &prepared.deployment_truth_check,
261 &execution_context,
262 )?;
263 print_install_result_summary(
264 &options.network,
265 &state.deployment_name,
266 &state.fleet_template,
267 &state_path,
268 );
269 Ok(())
270}
271
272fn resolve_install_identity(
273 options: &InstallRootOptions,
274 config_path: &Path,
275) -> Result<(String, String), Box<dyn std::error::Error>> {
276 let fleet_name = configured_fleet_name(config_path)?;
277 validate_expected_fleet_name(options.expected_fleet.as_deref(), &fleet_name, config_path)?;
278 validate_state_name(&fleet_name)?;
279 let deployment_name = options
280 .deployment_name
281 .clone()
282 .unwrap_or_else(|| fleet_name.clone());
283 validate_state_name(&deployment_name)?;
284 Ok((fleet_name, deployment_name))
285}
286
287fn current_unix_secs() -> Result<u64, Box<dyn std::error::Error>> {
289 Ok(SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs())
290}
291
292fn current_unix_timestamp_label() -> Result<String, Box<dyn std::error::Error>> {
293 Ok(format!("unix:{}", current_unix_secs()?))
294}