1use crate::{
2 canister_build::cache::DefaultCanisterBuildCacheCleanup,
3 release_set::{icp_root, workspace_root},
4};
5use config_selection::resolve_install_config_path;
6use std::{path::PathBuf, time::Instant};
7use thiserror::Error as ThisError;
8
9mod activation;
10mod artifact_promotion;
11mod build_environment;
12mod build_targets;
13mod capabilities;
14mod clock;
15mod commands;
16mod config_selection;
17mod current_execution;
18mod deployment_registration;
19mod deployment_truth_gate;
20mod execution_preflight;
21mod identity;
22mod install_state;
23mod operations;
24mod options;
25mod output;
26mod phase_receipts;
27mod plan_artifacts;
28mod preparation;
29mod readiness;
30mod receipt_io;
31mod root_canister;
32mod root_cycles;
33mod root_verification;
34mod staging;
35mod state;
36mod timing;
37mod truth_check;
38
39use activation::run_root_activation_phases;
40use artifact_promotion::write_artifact_promotion_execution_receipt_for_install;
41use build_environment::resolve_install_build_context;
42pub use config_selection::{
43 current_canic_project_root, discover_canic_config_choices, discover_canic_project_root_from,
44 discover_project_canic_config_choices, project_fleet_roots,
45};
46use current_execution::current_install_execution_context;
47pub use deployment_registration::{
48 RegisterDeploymentStateOptions, VerifyDeploymentRootOptions, register_deployment_state,
49 verify_registered_deployment_root,
50};
51use identity::resolve_install_identity;
52use install_state::{build_install_state, write_install_state_with_deployment_truth_receipt};
53pub use options::InstallRootOptions;
54use output::{print_install_result_summary, print_install_timing_summary};
55use phase_receipts::InstallReceiptScope;
56use plan_artifacts::emit_manifest_with_deployment_truth_receipt;
57use preparation::prepare_install_deployment_truth;
58pub use receipt_io::latest_deployment_truth_receipt_path_from_root;
59pub use state::{
60 InstallState, RootVerificationStatus, read_named_deployment_install_state,
61 read_named_deployment_install_state_from_root,
62};
63use timing::InstallTimingSummary as CurrentInstallTimingSummary;
64pub use truth_check::{check_install_deployment_truth, check_install_execution_preflight};
65
66#[cfg(test)]
67mod tests;
68
69#[derive(Clone, Copy, Debug, Eq, PartialEq)]
76pub enum InstallRootBlockKind {
77 DeploymentExecutionPreflight,
78 DeploymentTruth,
79}
80
81#[derive(Debug, ThisError)]
88#[error("{message}")]
89pub struct InstallRootBlockedError {
90 kind: InstallRootBlockKind,
91 message: String,
92}
93
94impl InstallRootBlockedError {
95 pub(super) const fn new(kind: InstallRootBlockKind, message: String) -> Self {
96 Self { kind, message }
97 }
98
99 #[must_use]
100 pub const fn kind(&self) -> InstallRootBlockKind {
101 self.kind
102 }
103}
104
105pub fn discover_current_canic_config_choices() -> Result<Vec<PathBuf>, Box<dyn std::error::Error>> {
107 let project_root = current_canic_project_root()?;
108 let choices = config_selection::discover_workspace_canic_config_choices(&project_root)?;
109 if !choices.is_empty() {
110 return Ok(choices);
111 }
112
113 if let Ok(icp_root) = icp_root()
114 && icp_root != project_root
115 {
116 return config_selection::discover_workspace_canic_config_choices(&icp_root);
117 }
118
119 Ok(choices)
120}
121
122pub fn install_root(options: InstallRootOptions) -> Result<(), Box<dyn std::error::Error>> {
124 let workspace_root = workspace_root()?;
125 let _build_cache_cleanup = DefaultCanisterBuildCacheCleanup::for_install(&workspace_root);
126 let icp_root = match &options.icp_root {
127 Some(path) => path.canonicalize()?,
128 None => icp_root()?,
129 };
130 let config_path = resolve_install_config_path(
131 &icp_root,
132 options.config_path.as_deref(),
133 options.interactive_config_selection,
134 )?;
135 let build_context =
136 current_install_build_context(&workspace_root, &icp_root, &config_path, &options)?;
137 let (fleet_name, deployment_name) = resolve_install_identity(&options, &config_path)?;
138 let total_started_at = Instant::now();
139 let mut timings = CurrentInstallTimingSummary::default();
140 let network = options.network.as_str();
141 let execution_context = current_install_execution_context(&workspace_root, &icp_root, network);
142
143 println!("Installing deployment {deployment_name}");
144 println!("Fleet template {fleet_name}");
145 println!();
146 let prepared = prepare_install_deployment_truth(
147 &options,
148 &workspace_root,
149 &icp_root,
150 &config_path,
151 &deployment_name,
152 &execution_context,
153 &build_context,
154 )?;
155 timings.create_canisters = prepared.timings.create_canisters;
156 timings.build_all = prepared.timings.build_all;
157
158 let (manifest_path, emit_manifest_duration) = emit_manifest_with_deployment_truth_receipt(
159 &workspace_root,
160 &icp_root,
161 &options,
162 &config_path,
163 &deployment_name,
164 &prepared.deployment_truth_check,
165 &execution_context,
166 )?;
167 timings.emit_manifest = emit_manifest_duration;
168 let activation_timings = run_root_activation_phases(
169 InstallReceiptScope {
170 icp_root: &icp_root,
171 network,
172 deployment_name: &deployment_name,
173 check: &prepared.deployment_truth_check,
174 execution_context: Some(&execution_context),
175 },
176 &options,
177 &prepared.root_canister_id,
178 &manifest_path,
179 total_started_at,
180 &build_context,
181 )?;
182 timings.install_root = activation_timings.install_root;
183 timings.fund_root = activation_timings.fund_root;
184 timings.stage_release_set = activation_timings.stage_release_set;
185 timings.resume_bootstrap = activation_timings.resume_bootstrap;
186 timings.wait_ready = activation_timings.wait_ready;
187 timings.finalize_root_funding = activation_timings.finalize_root_funding;
188
189 print_install_timing_summary(&timings, total_started_at.elapsed());
190 let state = build_install_state(
191 &options,
192 &workspace_root,
193 &icp_root,
194 &config_path,
195 &manifest_path,
196 (&deployment_name, &fleet_name),
197 &prepared.root_canister_id,
198 )?;
199 let state_path = write_install_state_with_deployment_truth_receipt(
200 InstallReceiptScope {
201 icp_root: &icp_root,
202 network,
203 deployment_name: &deployment_name,
204 check: &prepared.deployment_truth_check,
205 execution_context: Some(&execution_context),
206 },
207 &options.network,
208 &state,
209 )?;
210 write_artifact_promotion_execution_receipt_for_install(
211 &options,
212 &icp_root,
213 network,
214 &deployment_name,
215 &prepared.deployment_truth_check,
216 &execution_context,
217 )?;
218 print_install_result_summary(
219 &options.network,
220 &state.deployment_name,
221 &state.fleet_template,
222 &state_path,
223 );
224 Ok(())
225}
226
227fn current_install_build_context(
228 workspace_root: &std::path::Path,
229 icp_root: &std::path::Path,
230 config_path: &std::path::Path,
231 options: &InstallRootOptions,
232) -> Result<crate::canister_build::WorkspaceBuildContext, Box<dyn std::error::Error>> {
233 resolve_install_build_context(
234 workspace_root,
235 icp_root,
236 config_path,
237 &options.network,
238 &options.root_build_target,
239 options.build_profile,
240 )
241}