huskies: merge 1138 story In-container huskies self-update — huskies upgrade pulls a fresh binary without docker rebuild
This commit is contained in:
@@ -49,6 +49,8 @@ pub mod sled_uplink;
|
||||
mod startup;
|
||||
mod state;
|
||||
mod store;
|
||||
/// In-container binary self-update — fetch, atomic replace, and re-exec.
|
||||
pub mod upgrade;
|
||||
/// Validated input layer — transport-agnostic newtypes and request structs for all MCP write tools.
|
||||
pub mod validation;
|
||||
mod workflow;
|
||||
@@ -72,6 +74,19 @@ mod cli;
|
||||
|
||||
use cli::{parse_cli_args, resolve_path_arg};
|
||||
|
||||
/// Convert a WebSocket gateway URL into the binary download HTTP URL.
|
||||
///
|
||||
/// `ws://gateway:3000/api/sled-uplink?token=x` → `http://gateway:3000/api/huskies-binary`
|
||||
fn derive_binary_url_from_ws(ws_url: &str) -> Option<String> {
|
||||
let http = ws_url
|
||||
.strip_prefix("wss://")
|
||||
.map(|s| format!("https://{s}"))
|
||||
.or_else(|| ws_url.strip_prefix("ws://").map(|s| format!("http://{s}")))?;
|
||||
// Strip any path and query string, then append the binary endpoint.
|
||||
let base = http.split('/').take(3).collect::<Vec<_>>().join("/");
|
||||
Some(format!("{base}/api/huskies-binary"))
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), std::io::Error> {
|
||||
// Reap zombie grandchildren on Unix (for native deployments without tini/init).
|
||||
@@ -145,6 +160,27 @@ async fn main() -> Result<(), std::io::Error> {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Upgrade mode: fetch new binary, replace, exit ───────────────────────
|
||||
if cli.upgrade {
|
||||
let source = cli
|
||||
.upgrade_source
|
||||
.clone()
|
||||
.or_else(|| std::env::var("HUSKIES_BINARY_SOURCE").ok())
|
||||
.unwrap_or_else(|| {
|
||||
// Derive from HUSKIES_UPSTREAM_GATEWAY: ws://host:port/... → http://host:port/api/huskies-binary
|
||||
std::env::var("HUSKIES_UPSTREAM_GATEWAY")
|
||||
.ok()
|
||||
.and_then(|ws| derive_binary_url_from_ws(&ws))
|
||||
.unwrap_or_else(|| "http://gateway:3000/api/huskies-binary".to_string())
|
||||
});
|
||||
let target = upgrade::resolve_target_path();
|
||||
if let Err(e) = upgrade::run_cli_upgrade(&source, &target).await {
|
||||
eprintln!("error: {e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// ── Gateway mode: multi-project proxy ────────────────────────────────────
|
||||
if is_gateway {
|
||||
let config_dir = explicit_path.unwrap_or_else(|| cwd.clone());
|
||||
@@ -464,4 +500,28 @@ name = "coder"
|
||||
config::ProjectConfig::load(tmp.path())
|
||||
.unwrap_or_else(|e| panic!("Invalid project.toml: {e}"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derive_binary_url_strips_ws_scheme_and_path() {
|
||||
let url = derive_binary_url_from_ws("ws://gateway:3000/api/sled-uplink?token=abc");
|
||||
assert_eq!(
|
||||
url.as_deref(),
|
||||
Some("http://gateway:3000/api/huskies-binary")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derive_binary_url_handles_wss_scheme() {
|
||||
let url = derive_binary_url_from_ws("wss://myhost:443/path");
|
||||
assert_eq!(
|
||||
url.as_deref(),
|
||||
Some("https://myhost:443/api/huskies-binary")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn derive_binary_url_invalid_scheme_returns_none() {
|
||||
let url = derive_binary_url_from_ws("http://not-a-ws-url");
|
||||
assert!(url.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user