canic-host 0.70.11

Host-side build, install, deployment, and fleet-template library for Canic workspaces
Documentation
use super::build_targets::planned_build_artifact_root;
use super::deployment_truth_gate::{
    enforce_install_deployment_truth_gate, install_deployment_truth_gate_receipt,
    print_install_deployment_truth_gate,
};
use super::execution_preflight::write_current_install_execution_preflight_receipt;
use super::phase_receipts::receipt_with_execution_context;
use super::receipt_io::write_install_deployment_truth_receipt;
use super::{CURRENT_INSTALL_REQUIRED_CAPABILITIES, InstallRootOptions};
use crate::deployment_truth::{
    CurrentCliDeploymentExecutor, DeploymentCheckV1, DeploymentExecutionContextV1,
    DeploymentExecutor, DeploymentExecutorCapabilityV1, artifact_gate_phase_receipt,
    artifact_gate_role_phase_receipts, missing_executor_capabilities,
};
use crate::release_set::resolve_artifact_root;
use std::path::Path;

pub(super) fn current_install_execution_context(
    workspace_root: &Path,
    icp_root: &Path,
    network: &str,
) -> DeploymentExecutionContextV1 {
    CurrentCliDeploymentExecutor::new(
        Some(workspace_root.display().to_string()),
        Some(icp_root.display().to_string()),
        current_install_artifact_roots(icp_root, network),
    )
    .execution_context()
}

pub(super) fn ensure_current_install_executor_capabilities(
    execution_context: &DeploymentExecutionContextV1,
) -> Result<(), Box<dyn std::error::Error>> {
    let missing = current_install_executor_missing_capabilities(execution_context);
    if missing.is_empty() {
        return Ok(());
    }

    Err(format!(
        "current install executor backend {:?} is missing required capabilities: {missing:?}",
        execution_context.backend
    )
    .into())
}

pub(super) fn current_install_executor_missing_capabilities(
    execution_context: &DeploymentExecutionContextV1,
) -> Vec<DeploymentExecutorCapabilityV1> {
    missing_executor_capabilities(
        &execution_context.backend_capabilities,
        CURRENT_INSTALL_REQUIRED_CAPABILITIES,
    )
}

pub(super) fn run_install_deployment_truth_safety_gate(
    options: &InstallRootOptions,
    workspace_root: &Path,
    icp_root: &Path,
    config_path: &Path,
    deployment_name: &str,
    execution_context: &DeploymentExecutionContextV1,
) -> Result<DeploymentCheckV1, Box<dyn std::error::Error>> {
    let truth_gate_started_at = super::current_unix_timestamp_label()?;
    let deployment_truth_check = super::truth_check::current_install_deployment_truth_check_at(
        options,
        workspace_root,
        icp_root,
        config_path,
        deployment_name,
        truth_gate_started_at.clone(),
    )?;
    let artifact_gate_receipt = artifact_gate_phase_receipt(
        &deployment_truth_check,
        truth_gate_started_at.clone(),
        Some(super::current_unix_timestamp_label()?),
    );
    let role_receipts = artifact_gate_role_phase_receipts(&deployment_truth_check);
    let deployment_receipt = receipt_with_execution_context(
        install_deployment_truth_gate_receipt(
            &deployment_truth_check,
            truth_gate_started_at,
            vec![artifact_gate_receipt],
            role_receipts,
        ),
        execution_context,
    );
    let receipt_write = write_install_deployment_truth_receipt(
        icp_root,
        &options.network,
        deployment_name,
        &deployment_receipt,
    );
    match &receipt_write {
        Ok(path) => println!("Deployment truth receipt JSON: {}", path.display()),
        Err(err) => eprintln!("Deployment truth receipt JSON write failed: {err}"),
    }
    print_install_deployment_truth_gate(&deployment_truth_check, &deployment_receipt);
    enforce_install_deployment_truth_gate(&deployment_truth_check)?;
    receipt_write?;
    write_current_install_execution_preflight_receipt(
        icp_root,
        &options.network,
        deployment_name,
        &deployment_truth_check,
        execution_context,
    )?;
    Ok(deployment_truth_check)
}

fn current_install_artifact_roots(icp_root: &Path, network: &str) -> Vec<String> {
    let planned_root = planned_build_artifact_root(icp_root);
    let mut roots = vec![planned_root.display().to_string()];
    if let Ok(resolved_root) = resolve_artifact_root(icp_root, network)
        && resolved_root != planned_root
    {
        roots.push(resolved_root.display().to_string());
    }
    roots
}