use std::{fs, path::PathBuf, process::Command};
use crate::icp::LocalReplicaTarget;
use super::{
CanisterBuildProfile,
process::{icp_ancestor_process_id, parent_process_id},
};
const LOCAL_NETWORK_URL_ENV: &str = "CANIC_ICP_LOCAL_NETWORK_URL";
const LOCAL_ROOT_KEY_ENV: &str = "CANIC_ICP_LOCAL_ROOT_KEY";
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct WorkspaceBuildContext {
pub role: String,
pub profile: CanisterBuildProfile,
pub requested_profile: String,
pub environment: String,
pub build_network: String,
pub workspace_root: PathBuf,
pub icp_root: PathBuf,
pub config_path: PathBuf,
pub local_replica: Option<LocalReplicaTarget>,
}
impl WorkspaceBuildContext {
#[must_use]
pub fn lines(&self) -> Vec<String> {
let mut lines = vec![
"Canic build:".to_string(),
format!("role: {}", self.role),
format!("profile: {}", self.profile.target_dir_name()),
format!("environment: {}", self.environment),
format!("build network: {}", self.build_network),
format!("workspace: {}", self.workspace_root.display()),
];
if self.requested_profile != "unset" {
lines.push(format!("requested profile: {}", self.requested_profile));
}
if self.icp_root != self.workspace_root {
lines.push(format!("icp root: {}", self.icp_root.display()));
}
lines
}
#[must_use]
pub fn with_profile(&self, profile: CanisterBuildProfile) -> Self {
let mut context = self.clone();
context.profile = profile;
context
}
#[must_use]
pub fn with_role(&self, role: impl Into<String>) -> Self {
let mut context = self.clone();
context.role = role.into();
context
}
pub fn apply_to_command(&self, command: &mut Command) {
command
.env("ICP_ENVIRONMENT", &self.build_network)
.env_remove("CANIC_ICP_BUILD_ENVIRONMENT")
.env("CANIC_WORKSPACE_ROOT", &self.workspace_root)
.env("CANIC_ICP_ROOT", &self.icp_root)
.env("CANIC_CONFIG_PATH", &self.config_path);
if let Some(local_replica) = &self.local_replica {
command
.env(LOCAL_NETWORK_URL_ENV, &local_replica.url)
.env(LOCAL_ROOT_KEY_ENV, &local_replica.root_key);
} else {
command
.env_remove(LOCAL_NETWORK_URL_ENV)
.env_remove(LOCAL_ROOT_KEY_ENV);
}
}
}
pub fn print_workspace_build_context_once(
context: &WorkspaceBuildContext,
) -> Result<(), Box<dyn std::error::Error>> {
if workspace_build_context_once(context)? {
eprintln!("{}", context.lines().join("\n"));
}
Ok(())
}
pub fn workspace_build_context_once(
context: &WorkspaceBuildContext,
) -> Result<bool, Box<dyn std::error::Error>> {
let marker_dir = context.icp_root.join(".icp");
fs::create_dir_all(&marker_dir)?;
let marker_key = icp_ancestor_process_id()
.or_else(parent_process_id)
.unwrap_or_else(std::process::id)
.to_string();
let marker_file = marker_dir.join(format!(".canic-build-context-{marker_key}"));
if marker_file.exists() {
return Ok(false);
}
fs::write(&marker_file, [])?;
Ok(true)
}