Skip to content

Commit 63632a9

Browse files
committed
download wasm plugin binaries from github releases if not found locally
1 parent 63d564b commit 63632a9

File tree

2 files changed

+91
-4
lines changed

2 files changed

+91
-4
lines changed

cli/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,7 @@ repository = "https://github.com/zahash/jsoncodegen/"
99

1010
[dependencies]
1111
clap = { version = "4", features = ["derive", "env"] }
12+
reqwest = { version = "0.12", features = ["blocking", "json"] }
13+
serde = { version = "1", features = ["derive"] }
1214
wasmtime = "39"
1315
wasmtime-wasi = "39"

cli/src/main.rs

Lines changed: 89 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
use clap::Parser;
2-
use std::{env, error::Error, fs::File, path::PathBuf};
2+
use serde::Deserialize;
3+
use std::{
4+
env,
5+
error::Error,
6+
fs::{self, File},
7+
io::Write,
8+
path::PathBuf,
9+
};
310
use wasmtime::{Engine, Linker, Module, Store};
411
use wasmtime_wasi::WasiCtxBuilder;
512

@@ -34,10 +41,13 @@ fn main() -> Result<(), Box<dyn Error>> {
3441
})
3542
.ok_or("default runtime directory unavailable. please specify an alternate manually.")?;
3643

37-
// TODO: download the wasm binary if not available locally
38-
let codegen_wasm_path = runtime_dir.join(format!("jsoncodegen-{}.wasm", args.lang));
44+
let codegen_wasm_path =
45+
runtime_dir.join(format!("jsoncodegen-{}-wasm32-wasip1.wasm", args.lang));
46+
47+
// Check if WASM binary exists locally, if not download it
3948
if !codegen_wasm_path.is_file() {
40-
return Err("unsupported language".into());
49+
eprintln!("WASM binary not found locally");
50+
fetch_latest_wasm_release(&args.lang, &codegen_wasm_path)?;
4151
}
4252

4353
let ctx = {
@@ -74,3 +84,78 @@ fn main() -> Result<(), Box<dyn Error>> {
7484

7585
Ok(())
7686
}
87+
88+
#[derive(Debug, Deserialize)]
89+
struct Release {
90+
tag_name: String,
91+
assets: Vec<Asset>,
92+
}
93+
94+
#[derive(Debug, Deserialize)]
95+
struct Asset {
96+
name: String,
97+
browser_download_url: String,
98+
}
99+
100+
fn fetch_latest_wasm_release(lang: &str, dest_path: &PathBuf) -> Result<(), Box<dyn Error>> {
101+
eprintln!("Fetching latest WASM release info for language `{}`", lang);
102+
103+
let client = reqwest::blocking::Client::builder()
104+
.user_agent("jsoncodegen")
105+
.build()?;
106+
let mut releases: Vec<Release> = client
107+
.get("https://api.github.com/repos/zahash/jsoncodegen/releases")
108+
.send()?
109+
.json()?;
110+
111+
// Filter releases that match the pattern: jsoncodegen-{lang}-wasm32-wasip1-{version}
112+
let tag_prefix = format!("jsoncodegen-{}-wasm32-wasip1-", lang);
113+
releases.retain(|release| release.tag_name.starts_with(&tag_prefix));
114+
115+
// Sort by version number (descending) - extract the number after the last dash
116+
let latest_release = releases
117+
.into_iter()
118+
.max_by_key(|release| {
119+
release
120+
.tag_name
121+
.strip_prefix(&tag_prefix)
122+
.and_then(|version| version.parse::<usize>().ok())
123+
.unwrap_or(0)
124+
})
125+
.ok_or_else(|| format!("No WASM releases found for language `{}`", lang))?;
126+
127+
eprintln!("latest release found: {}", latest_release.tag_name);
128+
129+
let asset_name = format!("jsoncodegen-{}-wasm32-wasip1.wasm", lang);
130+
let asset = latest_release
131+
.assets
132+
.into_iter()
133+
.find(|a| a.name == asset_name)
134+
.ok_or_else(|| {
135+
format!(
136+
"WASM asset `{}` not found in release `{}`",
137+
asset_name, latest_release.tag_name
138+
)
139+
})?;
140+
141+
eprintln!(
142+
"Downloading WASM binary from: {}",
143+
asset.browser_download_url
144+
);
145+
146+
let response = client.get(asset.browser_download_url).send()?;
147+
if !response.status().is_success() {
148+
return Err(format!("Failed to download: HTTP {}", response.status()).into());
149+
}
150+
let bytes = response.bytes()?;
151+
152+
// Ensure the parent directory exists
153+
if let Some(parent) = dest_path.parent() {
154+
fs::create_dir_all(parent)?;
155+
}
156+
let mut file = File::create(dest_path)?;
157+
file.write_all(&bytes)?;
158+
159+
eprintln!("Successfully downloaded to: {}", dest_path.display());
160+
Ok(())
161+
}

0 commit comments

Comments
 (0)