cargo_tangle/command/run/
tangle.rs1use alloy_signer_local::PrivateKeySigner;
2use blueprint_crypto::tangle_pair_signer::TanglePairSigner;
3use blueprint_manager::config::{BlueprintManagerConfig, DEFAULT_DOCKER_HOST, SourceType};
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 pub http_rpc_url: String,
19 pub ws_rpc_url: String,
21 pub signer: Option<TanglePairSigner<sr25519::Pair>>,
23 pub signer_evm: Option<PrivateKeySigner>,
25 pub blueprint_id: Option<u64>,
27 pub keystore_path: Option<String>,
29 pub data_dir: Option<PathBuf>,
31 pub podman_host: Option<Url>,
33}
34
35#[allow(clippy::missing_panics_doc)]
48pub async fn run_blueprint(opts: RunOpts) -> Result<()> {
49 let blueprint_id = opts
50 .blueprint_id
51 .ok_or_else(|| eyre!("Blueprint ID is required"))?;
52
53 let mut gadget_config = BlueprintEnvironment::default();
54 gadget_config.http_rpc_endpoint = opts.http_rpc_url.clone();
55 gadget_config.ws_rpc_endpoint = opts.ws_rpc_url.clone();
56
57 if let Some(keystore_path) = opts.keystore_path {
58 gadget_config.keystore_uri = keystore_path;
59 }
60
61 gadget_config.keystore_uri = std::path::absolute(&gadget_config.keystore_uri)?
62 .display()
63 .to_string();
64
65 gadget_config.data_dir = opts.data_dir;
66
67 let blueprint_manager_config = BlueprintManagerConfig {
68 gadget_config: None,
69 keystore_uri: gadget_config.keystore_uri.clone(),
70 data_dir: gadget_config
71 .data_dir
72 .clone()
73 .unwrap_or_else(|| PathBuf::from("./data")),
74 verbose: 2,
75 pretty: true,
76 instance_id: Some(format!("Blueprint-{}", blueprint_id)),
77 test_mode: false,
78 preferred_source: SourceType::default(),
79 podman_host: opts.podman_host.unwrap_or(DEFAULT_DOCKER_HOST.clone()),
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, gadget_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}