canic-host 0.93.33

Host-side build, install, deployment, and fleet-template library for Canic workspaces
Documentation
use crate::{
    canister_build::{CanisterBuildProfile, WorkspaceBuildContext},
    icp::{self, LocalReplicaTarget},
    icp_config::resolve_icp_build_network_from_root,
    replica_query,
};
use std::path::Path;

pub(super) fn resolve_install_build_context(
    workspace_root: &Path,
    icp_root: &Path,
    config_path: &Path,
    environment: &str,
    role: &str,
    build_profile: Option<CanisterBuildProfile>,
) -> Result<WorkspaceBuildContext, Box<dyn std::error::Error>> {
    let profile = build_profile.unwrap_or(CanisterBuildProfile::Release);
    let build_network = resolve_icp_build_network_from_root(icp_root, environment)?;

    Ok(WorkspaceBuildContext {
        role: role.to_string(),
        profile,
        environment: environment.to_string(),
        build_network,
        workspace_root: workspace_root.to_path_buf(),
        icp_root: icp_root.to_path_buf(),
        config_path: config_path.to_path_buf(),
        local_replica: local_replica_icp_target(environment, icp_root),
        refresh_canonical_wasm_store_did: false,
    })
}

pub(super) fn local_replica_icp_target(
    environment: &str,
    icp_root: &Path,
) -> Option<LocalReplicaTarget> {
    if !replica_query::should_use_local_replica_query(Some(environment)) {
        return None;
    }
    if icp_ping(icp_root, environment).unwrap_or(false) {
        return None;
    }
    let root_key = replica_query::local_replica_root_key_from_root(Some(environment), icp_root)
        .ok()
        .flatten()?;
    Some(LocalReplicaTarget {
        url: replica_query::local_replica_endpoint_from_root(Some(environment), icp_root),
        root_key,
    })
}

pub(super) fn ensure_icp_environment_ready(
    icp_root: &Path,
    environment: &str,
) -> Result<(), Box<dyn std::error::Error>> {
    if icp_ping(icp_root, environment)? {
        return Ok(());
    }
    if replica_query::should_use_local_replica_query(Some(environment))
        && replica_query::local_replica_status_reachable_from_root(Some(environment), icp_root)
    {
        println!(
            "Replica reachable via HTTP status endpoint even though ICP CLI reports environment '{environment}' stopped; continuing from ICP root {}.",
            icp_root.display()
        );
        return Ok(());
    }

    Err(format!(
        "ICP environment '{environment}' is not running\nStart the target replica in another terminal with `canic replica start` and rerun."
    )
    .into())
}

fn icp_ping(icp_root: &Path, environment: &str) -> Result<bool, Box<dyn std::error::Error>> {
    let mut command = icp_ping_command(icp_root, environment);
    Ok(icp::run_success(&mut command)?)
}

fn icp_ping_command(icp_root: &Path, environment: &str) -> std::process::Command {
    let mut command = icp::default_command_in(icp_root);
    command.args(["network", "ping"]);
    icp::add_target_args(&mut command, Some(environment), None);
    command
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn icp_ping_selects_named_environment_without_treating_it_as_a_network() {
        let command = icp_ping_command(Path::new("/workspace/app"), "staging");

        assert_eq!(
            icp::command_display(&command),
            "icp --project-root-override /workspace/app network ping -e staging"
        );
    }
}