Skip to content

Commit f1cb4b4

Browse files
author
Matt "Siyuan" Yan
committed
feat: allow 'latest' as wasm-opt version
1 parent 8a57dba commit f1cb4b4

File tree

1 file changed

+78
-12
lines changed

1 file changed

+78
-12
lines changed

src/tools.rs

Lines changed: 78 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use directories::ProjectDirs;
88
use futures_util::stream::StreamExt;
99
use once_cell::sync::Lazy;
1010
use regex::Regex;
11+
use serde::Deserialize;
1112
use std::collections::HashMap;
1213
use std::path::PathBuf;
1314
use tokio::fs::File;
@@ -46,6 +47,57 @@ pub struct HttpClientOptions {
4647
}
4748

4849
impl Application {
50+
async fn fetch_if_latest(&self, version: &str) -> Result<String> {
51+
Ok(if version == "latest" {
52+
match self {
53+
Application::WasmOpt => {
54+
#[derive(Deserialize)]
55+
struct GitHubRelease {
56+
tag_name: String,
57+
}
58+
let url = "https://api.github.com/repos/WebAssembly/binaryen/releases/latest";
59+
// let client = reqwest::blocking::Client::new();
60+
let client = reqwest::Client::new();
61+
62+
// github api requires a user agent
63+
// https://docs.github.com/en/rest/using-the-rest-api/troubleshooting-the-rest-api?apiVersion=2022-11-28#user-agent-required
64+
let req_builder = client
65+
.get(url)
66+
.header("User-Agent", "trunk-wasm-opt-checker");
67+
68+
// Send the request
69+
let res = req_builder
70+
.send()
71+
.await
72+
.context("Failed to send request to GitHub API")?;
73+
74+
if !res.status().is_success() {
75+
// Get more details about the error
76+
let status = res.status();
77+
78+
let error_text = res
79+
.text()
80+
.await
81+
.unwrap_or_else(|_| "Could not read error response".to_string());
82+
83+
anyhow::bail!(
84+
"GitHub API request failed with status: {status}. Details: {error_text}"
85+
);
86+
}
87+
88+
let release: GitHubRelease = res
89+
.json()
90+
.await
91+
.context("Failed to parse GitHub API response")?;
92+
release.tag_name
93+
}
94+
_ => bail!("version 'latest' is not supported for {}", self.name()),
95+
}
96+
} else {
97+
version.to_string()
98+
})
99+
}
100+
49101
/// Base name of the executable without extension.
50102
pub(crate) fn name(&self) -> &str {
51103
match self {
@@ -296,7 +348,7 @@ pub async fn get_info(
296348
) -> Result<ToolInformation> {
297349
tracing::debug!("Getting tool");
298350

299-
if let Some((path, detected_version)) = find_system(app).await {
351+
let download_version = if let Some((path, detected_version)) = find_system(app).await {
300352
// consider system installed version
301353

302354
if let Some(required_version) = version {
@@ -315,8 +367,17 @@ pub async fn get_info(
315367
app.name(),
316368
)
317369
} else {
318-
// a mismatch, so we need to download
319-
tracing::debug!("tool version mismatch (required: {required_version}, system: {detected_version})");
370+
let required_version = app.fetch_if_latest(required_version).await?;
371+
if detected_version != required_version {
372+
// a mismatch, so we need to download
373+
tracing::debug!("tool version mismatch (required: {required_version}, system: {detected_version})");
374+
required_version
375+
} else {
376+
return Ok(ToolInformation {
377+
path,
378+
version: detected_version,
379+
});
380+
}
320381
}
321382
} else {
322383
// we don't require any specific version
@@ -325,38 +386,43 @@ pub async fn get_info(
325386
version: detected_version,
326387
});
327388
}
328-
}
329-
330-
if offline {
389+
} else if offline {
331390
return Err(anyhow!(
332391
"couldn't find application {name} (version: {version}), unable to download in offline mode",
333392
name = &app.name(),
334393
version = version.unwrap_or("<any>")
335394
));
336-
}
395+
} else if let Some(version) = version {
396+
app.fetch_if_latest(version).await?
397+
} else {
398+
tracing::debug!(
399+
"no version specified for {}, falling back to default",
400+
app.name()
401+
);
402+
app.default_version().to_string()
403+
};
337404

338405
let cache_dir = cache_dir().await?;
339-
let version = version.unwrap_or_else(|| app.default_version());
340-
let app_dir = cache_dir.join(format!("{}-{}", app.name(), version));
406+
let app_dir = cache_dir.join(format!("{}-{}", app.name(), download_version));
341407
let bin_path = app_dir.join(app.path());
342408

343409
if !is_executable(&bin_path).await? {
344410
GLOBAL_APP_CACHE
345411
.lock()
346412
.await
347-
.install_once(app, version, app_dir, client_options)
413+
.install_once(app, &download_version, app_dir, client_options)
348414
.await?;
349415
}
350416

351417
tracing::debug!(
352-
"Using {} ({version}) from: {}",
418+
"Using {} ({download_version}) from: {}",
353419
app.name(),
354420
bin_path.display()
355421
);
356422

357423
Ok(ToolInformation {
358424
path: bin_path,
359-
version: version.to_owned(),
425+
version: download_version,
360426
})
361427
}
362428

0 commit comments

Comments
 (0)