Skip to content

Commit 8b00aae

Browse files
committed
add image build command
1 parent f2e1924 commit 8b00aae

File tree

2 files changed

+189
-21
lines changed

2 files changed

+189
-21
lines changed

src/rust-cli/image.rs

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
use anyhow::anyhow;
2+
use clap::Subcommand;
3+
use std::fs;
4+
use std::process::{Command, Stdio};
5+
6+
#[derive(Subcommand, Debug)]
7+
pub enum ImageCommand {
8+
/// Build bitcoind and bitcoin-cli from <repo>/<branch> as <registry>:<tag>.
9+
/// Optionally deploy to remote registry using --action=push, otherwise image is loaded to local registry.
10+
Build {
11+
#[arg(long)]
12+
repo: String,
13+
#[arg(long)]
14+
branch: String,
15+
#[arg(long)]
16+
registry: String,
17+
#[arg(long)]
18+
tag: String,
19+
#[arg(long)]
20+
build_args: Option<String>,
21+
#[arg(long)]
22+
arches: Option<String>,
23+
#[arg(long)]
24+
action: Option<String>,
25+
},
26+
}
27+
28+
const ARCHES: [&str; 3] = ["amd64", "arm64", "armv7"];
29+
30+
fn run_command(command: &str) -> anyhow::Result<bool> {
31+
println!("Executing: {}", command);
32+
let mut child = Command::new("bash")
33+
.arg("-c")
34+
.arg(command)
35+
.stdout(Stdio::inherit())
36+
.stderr(Stdio::inherit())
37+
.spawn()?;
38+
39+
let output = child.wait()?;
40+
41+
if output.success() {
42+
Ok(true)
43+
} else {
44+
Err(anyhow!("Command failed"))
45+
}
46+
}
47+
48+
fn build_image(
49+
repo: &String,
50+
branch: &String,
51+
docker_registry: &String,
52+
tag: &String,
53+
build_args: &Option<String>,
54+
arches: &Option<String>,
55+
action: &Option<String>,
56+
) -> anyhow::Result<()> {
57+
let build_args = match build_args {
58+
Some(args) => format!("\"{}\"", args),
59+
None => "\"--disable-tests --without-gui --disable-bench --disable-fuzz-binary --enable-suppress-external-warnings \"".to_string(),
60+
};
61+
62+
let mut build_arches = vec![];
63+
match arches {
64+
Some(a) => build_arches.extend(a.split(',').map(String::from)),
65+
None => build_arches.push("amd64".to_string()),
66+
}
67+
68+
for arch in &build_arches {
69+
if !ARCHES.contains(&arch.as_str()) {
70+
println!("Error: {} is not a supported architecture", arch);
71+
return Err(anyhow!("Unsupported architecture: {}", arch));
72+
}
73+
}
74+
75+
println!("repo={}", repo);
76+
println!("branch={}", branch);
77+
println!("docker_registry={}", docker_registry);
78+
println!("tag={}", tag);
79+
println!("build_args={}", build_args);
80+
println!("build_arches={:?}", build_arches);
81+
82+
if !fs::metadata("src/templates")
83+
.map(|m| m.is_dir())
84+
.unwrap_or(false)
85+
{
86+
println!("Directory src/templates does not exist.");
87+
println!("Please run this script from the project root.");
88+
return Err(anyhow!("src/templates directory not found"));
89+
}
90+
91+
let builder_name = "bitcoind-builder";
92+
let create_builder_cmd = format!("docker buildx create --name {} --use", builder_name);
93+
let creat_builder_res = run_command(&create_builder_cmd);
94+
if creat_builder_res.is_err() {
95+
let use_builder_cmd = format!("docker buildx use {}", builder_name);
96+
run_command(&use_builder_cmd)?;
97+
}
98+
99+
let image_full_name = format!("{}:{}", docker_registry, tag);
100+
println!("image_full_name={}", image_full_name);
101+
102+
let platforms = build_arches
103+
.iter()
104+
.map(|arch| format!("linux/{}", arch))
105+
.collect::<Vec<_>>()
106+
.join(",");
107+
108+
let action = match action {
109+
Some(action) => action,
110+
None => "load",
111+
};
112+
let build_command = format!(
113+
"docker buildx build --platform {} --build-arg REPO={} --build-arg BRANCH={} --build-arg BUILD_ARGS={} --tag {} --file src/templates/Dockerfile . --{}",
114+
platforms, repo, branch, build_args, image_full_name, action
115+
);
116+
117+
println!("Using build_command={}", build_command);
118+
119+
let res = run_command(&build_command);
120+
if res.is_ok() {
121+
println!("Build completed");
122+
} else {
123+
println!("Build failed.");
124+
}
125+
126+
let cleanup_builder_cmd = format!("docker buildx rm {}", builder_name);
127+
let cleanup_res = run_command(&cleanup_builder_cmd);
128+
if cleanup_res.is_ok() {
129+
println!("Buildx builder removed successfully.");
130+
} else {
131+
println!("Warning: Failed to remove the buildx builder.");
132+
}
133+
134+
match res {
135+
Ok(true) => Ok(()),
136+
Ok(false) => Err(anyhow!(
137+
"Build command failed, but no specific error was provided."
138+
)),
139+
Err(e) => Err(anyhow!("Build command failed with error: {}", e)),
140+
}
141+
}
142+
143+
pub async fn handle_image_command(command: &ImageCommand) -> anyhow::Result<()> {
144+
match command {
145+
ImageCommand::Build {
146+
repo,
147+
branch,
148+
registry,
149+
tag,
150+
build_args,
151+
arches,
152+
action,
153+
} => build_image(repo, branch, registry, tag, build_args, arches, action),
154+
}
155+
}

src/rust-cli/main.rs

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,23 @@ use clap::{Parser, Subcommand};
44
mod debug;
55
mod general;
66
mod graph;
7+
mod image;
78
mod network;
89
mod rpc_call;
910
mod scenarios;
1011
mod util;
1112
use crate::debug::{handle_debug_command, DebugCommand};
1213
use crate::general::*;
1314
use crate::graph::{handle_graph_command, GraphCommand};
15+
use crate::image::{handle_image_command, ImageCommand};
1416
use crate::network::{handle_network_command, NetworkCommand};
1517
use crate::scenarios::{handle_scenario_command, ScenarioCommand};
1618

1719
#[derive(Parser, Debug)]
1820
#[command(version, about, long_about = None)]
1921
struct Cli {
2022
#[arg(long)]
23+
/// The warnet network command corresponds to
2124
network: Option<String>,
2225

2326
#[command(subcommand)]
@@ -26,46 +29,51 @@ struct Cli {
2629

2730
#[derive(Subcommand, Debug)]
2831
enum Commands {
29-
/// Network commands
30-
Network {
31-
#[command(subcommand)]
32-
command: Option<NetworkCommand>,
33-
},
34-
/// Debug commands [[deprecated]]
32+
/// Debug commands (deprecated)
3533
Debug {
3634
#[command(subcommand)]
3735
command: Option<DebugCommand>,
3836
},
37+
/// Fetch the Bitcoin Core debug log from <node> in [network]
38+
DebugLog { node: u64 },
3939
/// Graph commands
4040
Graph {
4141
#[command(subcommand)]
4242
command: Option<GraphCommand>,
4343
},
44-
/// Scenario commands
45-
Scenarios {
44+
/// Grep combined logs via fluentd using regex <pattern>
45+
GrepLogs { pattern: String },
46+
/// Build a warnet-ready bitcoind docker image from a github branch
47+
Image {
4648
#[command(subcommand)]
47-
command: Option<ScenarioCommand>,
48-
},
49-
/// Call bitcoin-cli <method> [params] on <node> in [network]
50-
Rpc {
51-
node: u64,
52-
#[arg(allow_hyphen_values=true)]
53-
method: String,
54-
params: Option<Vec<String>>,
49+
command: Option<ImageCommand>,
5550
},
5651
/// Call lncli <method> [params] on <node> in [network]
5752
LnCli {
5853
node: u64,
5954
method: String,
6055
params: Option<Vec<String>>,
6156
},
62-
/// Fetch the Bitcoin Core debug log from <node> in [network]
63-
DebugLog { node: u64 },
6457
/// Fetch messages sent between <node_a> and <node_b> in [network]
6558
Messages { node_a: u64, node_b: u64 },
66-
/// Grep combined logs via fluentd using regex <pattern>
67-
GrepLogs { pattern: String },
68-
/// Stop warnet
59+
/// Network commands
60+
Network {
61+
#[command(subcommand)]
62+
command: Option<NetworkCommand>,
63+
},
64+
/// Call bitcoin-cli <method> [params] on <node> in [network]
65+
Rpc {
66+
node: u64,
67+
#[arg(allow_hyphen_values = true)]
68+
method: String,
69+
params: Option<Vec<String>>,
70+
},
71+
/// Scenario commands
72+
Scenarios {
73+
#[command(subcommand)]
74+
command: Option<ScenarioCommand>,
75+
},
76+
/// Stop warnet server
6977
Stop {},
7078
}
7179

@@ -117,6 +125,11 @@ async fn main() -> anyhow::Result<()> {
117125
Some(Commands::DebugLog { node }) => {
118126
handle_debug_log_command(node, rpc_params).await?;
119127
}
128+
Some(Commands::Image { command }) => {
129+
if let Some(command) = command {
130+
handle_image_command(command).await?;
131+
}
132+
}
120133
Some(Commands::Messages { node_a, node_b }) => {
121134
handle_messages_command(node_a, node_b, rpc_params).await?;
122135
}

0 commit comments

Comments
 (0)