cargo_tangle/command/run/
tangle.rs

1use alloy_signer_local::PrivateKeySigner;
2use blueprint_crypto::tangle_pair_signer::TanglePairSigner;
3use blueprint_manager::config::{BlueprintManagerConfig, DEFAULT_DOCKER_HOST};
4use blueprint_manager::executor::run_blueprint_manager;
5use blueprint_runner::config::BlueprintEnvironment;
6use color_eyre::eyre::{Result, eyre};
7use dialoguer::console::style;
8use indicatif::{ProgressBar, ProgressStyle};
9use sp_core::sr25519;
10use std::path::PathBuf;
11use std::time::Duration;
12use tokio::signal;
13use url::Url;
14
15#[derive(Clone)]
16pub struct RunOpts {
17    /// The HTTP RPC URL of the Tangle Network
18    pub http_rpc_url: Url,
19    /// The WS RPC URL of the Tangle Network
20    pub ws_rpc_url: Url,
21    /// The signer for Tangle operations
22    pub signer: Option<TanglePairSigner<sr25519::Pair>>,
23    /// The signer for EVM operations
24    pub signer_evm: Option<PrivateKeySigner>,
25    /// The blueprint ID to run
26    pub blueprint_id: Option<u64>,
27    /// The keystore path
28    pub keystore_path: Option<String>,
29    /// The data directory path
30    pub data_dir: Option<PathBuf>,
31    /// Whether to allow invalid GitHub attestations (binary integrity checks)
32    ///
33    /// This will also allow for running the manager without the GitHub CLI installed.
34    pub allow_unchecked_attestations: bool,
35    /// The Podman host to use for containerized blueprints
36    pub podman_host: Option<Url>,
37}
38
39/// Runs a blueprint using the blueprint manager
40///
41/// # Arguments
42///
43/// * `opts` - Options for running the blueprint
44///
45/// # Errors
46///
47/// Returns an error if:
48/// * Blueprint ID is not provided
49/// * Failed to create or configure the blueprint manager
50/// * Failed to run the blueprint
51#[allow(clippy::missing_panics_doc)]
52pub async fn run_blueprint(opts: RunOpts) -> Result<()> {
53    let blueprint_id = opts
54        .blueprint_id
55        .ok_or_else(|| eyre!("Blueprint ID is required"))?;
56
57    let mut blueprint_config = BlueprintEnvironment::default();
58    blueprint_config.http_rpc_endpoint = opts.http_rpc_url;
59    blueprint_config.ws_rpc_endpoint = opts.ws_rpc_url;
60
61    if let Some(keystore_path) = opts.keystore_path {
62        blueprint_config.keystore_uri = keystore_path;
63    }
64
65    blueprint_config.keystore_uri = std::path::absolute(&blueprint_config.keystore_uri)?
66        .display()
67        .to_string();
68
69    blueprint_config.data_dir = opts.data_dir.unwrap_or_else(|| PathBuf::from("./data"));
70
71    let blueprint_manager_config = BlueprintManagerConfig {
72        keystore_uri: blueprint_config.keystore_uri.clone(),
73        data_dir: blueprint_config.data_dir.clone(),
74        verbose: 2,
75        pretty: true,
76        instance_id: Some(format!("Blueprint-{}", blueprint_id)),
77        allow_unchecked_attestations: opts.allow_unchecked_attestations,
78        podman_host: opts.podman_host.unwrap_or(DEFAULT_DOCKER_HOST.clone()),
79        ..Default::default()
80    };
81
82    println!(
83        "{}",
84        style(format!(
85            "Starting blueprint manager for blueprint ID: {}",
86            blueprint_id
87        ))
88        .cyan()
89        .bold()
90    );
91
92    let shutdown_signal = async move {
93        let _ = signal::ctrl_c().await;
94        println!(
95            "{}",
96            style("Received shutdown signal, stopping blueprint manager")
97                .yellow()
98                .bold()
99        );
100    };
101
102    println!(
103        "{}",
104        style("Preparing Blueprint to run, this may take a few minutes...").cyan()
105    );
106
107    let pb = ProgressBar::new_spinner();
108    pb.set_style(
109        ProgressStyle::default_spinner()
110            .tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈ ")
111            .template("{spinner:.blue} {msg}")
112            .unwrap(),
113    );
114    pb.set_message("Initializing Blueprint");
115    pb.enable_steady_tick(Duration::from_millis(100));
116
117    let mut handle =
118        run_blueprint_manager(blueprint_manager_config, blueprint_config, shutdown_signal).await?;
119
120    pb.finish_with_message("Blueprint initialized successfully!");
121
122    println!(
123        "{}",
124        style("Starting blueprint execution...").green().bold()
125    );
126    handle.start()?;
127
128    println!(
129        "{}",
130        style("Blueprint is running. Press Ctrl+C to stop.").cyan()
131    );
132    handle.await?;
133
134    println!("{}", style("Blueprint manager has stopped").green());
135    Ok(())
136}