use std::path::{Path, PathBuf};
use std::process;
use clap::Args;
use super::{open_codebase, prepare_artifact, registry_client, try_load_and_parse};
#[derive(Args)]
pub struct DeployArgs {
/// Input .tri file, project directory, or .deploy/ artifact
pub input: PathBuf,
/// Target VM or OS (default: triton)
#[arg(long, default_value = "triton")]
pub target: String,
/// Engine (geeky for terrain/VM)
#[arg(long, conflicts_with_all = ["terrain", "network", "union_flag"])]
pub engine: Option<String>,
/// Terrain (gamy for engine/VM)
#[arg(long, conflicts_with_all = ["engine", "network", "union_flag"])]
pub terrain: Option<String>,
/// Network (geeky for union/OS)
#[arg(long, conflicts_with_all = ["engine", "terrain", "union_flag"])]
pub network: Option<String>,
/// Union (gamy for network/OS)
#[arg(long = "union", conflicts_with_all = ["engine", "terrain", "network"])]
pub union_flag: Option<String>,
/// Vimputer (geeky for state/chain instance)
#[arg(long, conflicts_with = "state")]
pub vimputer: Option<String>,
/// State (gamy for vimputer/chain instance)
#[arg(long, conflicts_with = "vimputer")]
pub state: Option<String>,
/// Compilation profile for cfg flags (default: release)
#[arg(long, default_value = "release")]
pub profile: String,
/// Registry URL to deploy to
#[arg(long)]
pub registry: Option<String>,
/// Run formal audit before deploying
#[arg(long)]
pub audit: bool,
/// Show what would be deployed without actually deploying
#[arg(long)]
pub dry_run: bool,
}
pub fn cmd_deploy(args: DeployArgs) {
let DeployArgs {
input,
target,
engine,
terrain,
network,
union_flag,
vimputer,
state,
profile,
registry,
audit,
dry_run,
} = args;
let bf = super::resolve_battlefield(
&target,
&engine,
&terrain,
&network,
&union_flag,
&vimputer,
&state,
);
let target = bf.target;
let state_selection = bf.state;
// Handle pre-packaged .deploy/ artifact directory
if input.is_dir() && input.join("manifest.json").exists() && input.join("program.tasm").exists()
{
let manifest_json = match std::fs::read_to_string(input.join("manifest.json")) {
Ok(s) => s,
Err(e) => {
eprintln!("error: cannot read manifest.json: {}", e);
process::exit(1);
}
};
if dry_run {
eprintln!("Dry run โ would deploy artifact:");
eprintln!(" Artifact: {}", input.display());
for line in manifest_json.lines() {
let trimmed = line.trim();
if trimmed.starts_with("\"name\"") || trimmed.starts_with("\"program_digest\"") {
eprintln!(" {}", trimmed.trim_end_matches(','));
}
}
return;
}
let client = registry_client(registry);
deploy_to_registry(&input, &client);
return;
}
// Build from source
let art = prepare_artifact(&input, &target, &profile, audit);
let output_base = art.entry.parent().unwrap_or(Path::new(".")).to_path_buf();
// Resolve state config if specified
let state_config = if let Some(ref state_name) = state_selection {
if let Some(ref os) = art.resolved.os {
match trident::target::StateConfig::resolve(&os.name, state_name) {
Ok(Some(sc)) => Some(sc),
Ok(None) => {
eprintln!(
"error: unknown state '{}' for union '{}'",
state_name, os.name
);
let available = trident::target::StateConfig::list_states(&os.name);
if !available.is_empty() {
eprintln!(" available: {}", available.join(", "));
}
process::exit(1);
}
Err(e) => {
eprintln!("error: {}", e.message);
process::exit(1);
}
}
} else {
eprintln!(
"error: --state requires a union target, not bare terrain '{}'",
target
);
process::exit(1);
}
} else {
None
};
let target_display = if let Some(ref os) = art.resolved.os {
if let Some(ref sc) = state_config {
format!("{} {} ({})", os.name, sc.display_name, art.resolved.vm.name)
} else {
format!("{} ({})", os.name, art.resolved.vm.name)
}
} else {
art.resolved.vm.name.clone()
};
if dry_run {
let program_digest =
trident::hash::ContentHash(trident::poseidon2::hash_bytes(art.tasm.as_bytes()));
eprintln!("Dry run โ would deploy:");
eprintln!(" Name: {}", art.name);
eprintln!(" Version: {}", art.version);
eprintln!(" Target: {}", target_display);
if let Some(ref sc) = state_config {
eprintln!(" State: {} (chain_id: {})", sc.name, sc.chain_id);
if !sc.rpc_url.is_empty() {
eprintln!(" RPC: {}", sc.rpc_url);
}
}
eprintln!(" Program digest: {}", program_digest.to_hex());
eprintln!(" Padded height: {}", art.cost.padded_height);
return;
}
let result = match trident::deploy::generate_artifact(
&art.name,
&art.version,
&art.tasm,
&art.file,
&art.cost,
&art.resolved.vm,
art.resolved.os.as_ref(),
&output_base,
) {
Ok(r) => r,
Err(e) => {
eprintln!("error: {}", e);
process::exit(1);
}
};
eprintln!("Packaged -> {}", result.artifact_dir.display());
eprintln!(" digest: {}", result.manifest.program_digest);
let client = registry_client(registry);
deploy_to_registry(&result.artifact_dir, &client);
}
/// Deploy a validated artifact directory (must contain manifest.json + program.tasm).
fn deploy_to_registry(artifact_dir: &Path, client: &trident::registry::RegistryClient) {
eprintln!("Deploying...");
// Try to find and add source to codebase
let source_path = artifact_dir.parent().and_then(|parent| {
let stem = artifact_dir
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("")
.trim_end_matches(".deploy");
let tri_file = parent.join(format!("{}.tri", stem));
tri_file.exists().then_some(tri_file)
});
let mut cb = open_codebase();
if let Some(source_file) = source_path {
if let Some((_, file)) = try_load_and_parse(&source_file) {
cb.add_file(&file);
if let Err(e) = cb.save() {
eprintln!("error: cannot save codebase: {}", e);
process::exit(1);
}
}
}
match trident::registry::publish_codebase(&cb, client, &[]) {
Ok(results) => {
let created = results.iter().filter(|r| r.created).count();
eprintln!("Deployed: {} definitions ({} new)", results.len(), created);
}
Err(e) => {
eprintln!("error: deploy failed: {}", e);
process::exit(1);
}
}
}
trident/src/cli/deploy.rs
ฯ 0.0%
use ;
use process;
use Args;
use ;
/// Deploy a validated artifact directory (must contain manifest.json + program.tasm).