Fleet tools: add Rust version card (probe rustc + nightly latest + rustup update)
ci / gates (push) Successful in 5s
ci / frontend (push) Successful in 24s
ci / rust (push) Failing after 2m39s
ci / e2e (push) Has been skipped

Adds Rust to the per-node dev-tool cards: daemon probes rustc → reports 'rust';
nightly checker fetches latest stable from GitHub rust-lang/rust; GET endpoint maps
rust→Rust (after docker); one-click update runs 'rustup update stable'. Frontend is
data-driven (no change). $HOME/.cargo/bin added to probe candidate dirs.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
Omar Sobh
2026-06-27 13:04:16 -07:00
co-authored by Claude Opus 4.8
parent 96d1409a23
commit 1a531e91cd
3 changed files with 34 additions and 5 deletions
+20 -3
View File
@@ -216,14 +216,23 @@ fn probe_tools() -> Value {
"/usr/bin".to_string(), "/usr/bin".to_string(),
"/bin".to_string(), "/bin".to_string(),
format!("{home}/.local/share/uv/tools/kimi-cli/bin"), format!("{home}/.local/share/uv/tools/kimi-cli/bin"),
format!("{home}/.cargo/bin"),
]; ];
let mut out = serde_json::Map::new(); let mut out = serde_json::Map::new();
for tool in ["docker", "claude", "kimi-cli", "ollama"] { // (report key, binary name) — usually identical; Rust reports "rust" but its
// binary is `rustc`.
for (key, bin) in [
("docker", "docker"),
("claude", "claude"),
("kimi-cli", "kimi-cli"),
("ollama", "ollama"),
("rust", "rustc"),
] {
for d in &dirs { for d in &dirs {
let p = std::path::Path::new(d).join(tool); let p = std::path::Path::new(d).join(bin);
if p.exists() { if p.exists() {
if let Some(v) = tool_version(&p) { if let Some(v) = tool_version(&p) {
out.insert(tool.to_string(), Value::String(v)); out.insert(key.to_string(), Value::String(v));
} }
break; break;
} }
@@ -309,6 +318,14 @@ async fn update_tool(tool: &str) -> (bool, String) {
c c
} }
} }
"rust" => match find("rustup") {
Some(p) => {
let mut c = tokio::process::Command::new(p);
c.args(["update", "stable"]);
c
}
None => return (false, "rustup not found".into()),
},
_ => return (false, "unsupported tool".into()), _ => return (false, "unsupported tool".into()),
}; };
match tokio::time::timeout(Duration::from_secs(170), cmd.output()).await { match tokio::time::timeout(Duration::from_secs(170), cmd.output()).await {
+2 -1
View File
@@ -199,6 +199,7 @@ pub async fn tools(
// (probe key, display name, ui key) in display order; `glm` mirrors `claude`. // (probe key, display name, ui key) in display order; `glm` mirrors `claude`.
let order = [ let order = [
("docker", "Docker", "docker"), ("docker", "Docker", "docker"),
("rust", "Rust", "rust"),
("claude", "Claude Code", "claude"), ("claude", "Claude Code", "claude"),
("kimi-cli", "Kimi", "kimi"), ("kimi-cli", "Kimi", "kimi"),
("glm", "GLM (Claude Code)", "glm"), ("glm", "GLM (Claude Code)", "glm"),
@@ -230,7 +231,7 @@ pub async fn tool_update(
nodes::get(&state.pool, node_id, user.workspace_id) nodes::get(&state.pool, node_id, user.workspace_id)
.await? .await?
.ok_or(ApiError::NotFound)?; .ok_or(ApiError::NotFound)?;
if !["claude", "glm", "kimi", "ollama"].contains(&tool.as_str()) { if !["claude", "glm", "kimi", "ollama", "rust"].contains(&tool.as_str()) {
return Ok(Json(json!({ "ok": false, "output": "tool not updatable" }))); return Ok(Json(json!({ "ok": false, "output": "tool not updatable" })));
} }
// ~180s: tool updates (npm/uv/brew/installer) legitimately run long. // ~180s: tool updates (npm/uv/brew/installer) legitimately run long.
+12 -1
View File
@@ -10,7 +10,8 @@ use serde_json::Value;
use sqlx::PgPool; use sqlx::PgPool;
/// Fetch the latest version of each checkable tool and upsert `tool_latest`. /// Fetch the latest version of each checkable tool and upsert `tool_latest`.
/// Stored under the same keys the daemon reports (`claude`, `kimi-cli`, `ollama`). /// Stored under the same keys the daemon reports (`claude`, `kimi-cli`, `ollama`,
/// `rust`).
async fn check(pool: &PgPool, client: &reqwest::Client) { async fn check(pool: &PgPool, client: &reqwest::Client) {
// Claude Code — the npm package's latest dist-tag. // Claude Code — the npm package's latest dist-tag.
if let Some(v) = fetch_field( if let Some(v) = fetch_field(
@@ -42,6 +43,16 @@ async fn check(pool: &PgPool, client: &reqwest::Client) {
{ {
let _ = node_tools::set_latest(pool, "ollama", v.trim_start_matches('v')).await; let _ = node_tools::set_latest(pool, "ollama", v.trim_start_matches('v')).await;
} }
// Rust — latest stable, from the rust-lang/rust GitHub release tag.
if let Some(v) = fetch_field(
client,
"https://api.github.com/repos/rust-lang/rust/releases/latest",
&["tag_name"],
)
.await
{
let _ = node_tools::set_latest(pool, "rust", v.trim_start_matches('v')).await;
}
} }
/// GET `url` as JSON and read a (possibly nested) string field by `path`. /// GET `url` as JSON and read a (possibly nested) string field by `path`.