Remove all alternate update paths — fleet redeploy is release + upgrade all

Killed:
- rebuild_and_restart (in-container cargo self-compile): the MCP tool,
  the `rebuild` chat command in all four transports, the web-ui bot
  command, and the underlying function. This was the path that caused
  the exec() deadlocks.
- upgrade_sled gateway MCP tool: second entry point to sled upgrades,
  defaulted to serving the gateway's own macOS binary to Linux sleds.
- GET /api/huskies-binary (both sled and gateway route trees): served
  current_exe(), wrong platform when the gateway is macOS. Superseded
  by /api/artifacts/ which now also serves on the gateway route tree.
- `huskies upgrade` CLI subcommand and --source flag: third way of
  doing the same download-and-replace. Escape hatch for a bricked sled
  is `docker cp` + restart.

Kept, distinct jobs: `project-rebuild` (container/image updates),
`rebuild gateway` + script/local-release (gateway self-update).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019fHdm92yjvguPi2LiXfLB9
This commit is contained in:
Timmy
2026-07-15 16:46:11 +01:00
co-authored by Claude Fable 5
parent 83f941b77e
commit f39c4b7c4b
24 changed files with 36 additions and 824 deletions
-83
View File
@@ -27,14 +27,6 @@ pub(crate) struct CliArgs {
/// forwards all `prompt_permission` tool calls to the gateway over a WebSocket.
/// Also readable from the `HUSKIES_UPSTREAM_GATEWAY` env var.
pub(crate) upstream_gateway: Option<String>,
/// Whether the `upgrade` subcommand was given.
pub(crate) upgrade: bool,
/// Source URL for the `upgrade` subcommand (`--source <URL>`).
///
/// If omitted, the upgrade subcommand falls back to
/// `HUSKIES_BINARY_SOURCE` env var, then derives the URL from
/// `HUSKIES_UPSTREAM_GATEWAY`.
pub(crate) upgrade_source: Option<String>,
/// Path to a trampoline job file (`--trampoline <path>`).
///
/// When set, the binary runs as a detached trampoline helper: it kills the
@@ -54,8 +46,6 @@ pub(crate) fn parse_cli_args(args: &[String]) -> Result<CliArgs, String> {
let mut join_token: Option<String> = None;
let mut gateway_url: Option<String> = None;
let mut upstream_gateway: Option<String> = None;
let mut upgrade = false;
let mut upgrade_source: Option<String> = None;
let mut trampoline: Option<String> = None;
let mut i = 0;
@@ -136,19 +126,6 @@ pub(crate) fn parse_cli_args(args: &[String]) -> Result<CliArgs, String> {
"agent" => {
agent = true;
}
"upgrade" => {
upgrade = true;
}
"--source" => {
i += 1;
if i >= args.len() {
return Err("--source requires a value".to_string());
}
upgrade_source = Some(args[i].clone());
}
a if a.starts_with("--source=") => {
upgrade_source = Some(a["--source=".len()..].to_string());
}
"--trampoline" => {
i += 1;
if i >= args.len() {
@@ -186,8 +163,6 @@ pub(crate) fn parse_cli_args(args: &[String]) -> Result<CliArgs, String> {
join_token,
gateway_url,
upstream_gateway,
upgrade,
upgrade_source,
trampoline,
})
}
@@ -197,16 +172,12 @@ pub(crate) fn print_help() {
println!("huskies init [OPTIONS] [PATH]");
println!("huskies agent --rendezvous <URL> [OPTIONS] [PATH]");
println!("huskies --gateway [OPTIONS] [PATH]");
println!("huskies upgrade [--source <URL>]");
println!();
println!("Serve a huskies project.");
println!();
println!("COMMANDS:");
println!(" init Scaffold a new .huskies/ project and start the interactive setup wizard.");
println!(" agent Run as a headless build agent — syncs CRDT state, claims and runs work.");
println!(
" upgrade Fetch a new huskies binary from SOURCE and atomically replace the current"
);
println!();
println!("ARGS:");
println!(
@@ -236,8 +207,6 @@ pub(crate) fn print_help() {
println!(" sled connects to WS URL and forwards all");
println!(" prompt_permission calls via the uplink protocol.");
println!(" Also readable from HUSKIES_UPSTREAM_GATEWAY env var.");
println!(" --source <URL> Binary source URL for the `upgrade` subcommand.");
println!(" Falls back to HUSKIES_BINARY_SOURCE env var.");
}
/// Resolve the optional positional path argument into an absolute `PathBuf`.
@@ -447,58 +416,6 @@ mod tests {
assert!(parse_cli_args(&args).is_err());
}
// ── upgrade subcommand ──────────────────────────────────────────
#[test]
fn parse_upgrade_subcommand() {
let args = vec!["upgrade".to_string()];
let result = parse_cli_args(&args).unwrap();
assert!(result.upgrade);
assert_eq!(result.upgrade_source, None);
}
#[test]
fn parse_upgrade_with_source_flag() {
let args = vec![
"upgrade".to_string(),
"--source".to_string(),
"http://gateway:3000/api/huskies-binary".to_string(),
];
let result = parse_cli_args(&args).unwrap();
assert!(result.upgrade);
assert_eq!(
result.upgrade_source,
Some("http://gateway:3000/api/huskies-binary".to_string())
);
}
#[test]
fn parse_upgrade_with_source_equals_syntax() {
let args = vec![
"upgrade".to_string(),
"--source=http://gw:3000/api/b".to_string(),
];
let result = parse_cli_args(&args).unwrap();
assert!(result.upgrade);
assert_eq!(
result.upgrade_source,
Some("http://gw:3000/api/b".to_string())
);
}
#[test]
fn parse_upgrade_source_missing_value_is_error() {
let args = vec!["upgrade".to_string(), "--source".to_string()];
assert!(parse_cli_args(&args).is_err());
}
#[test]
fn parse_no_args_upgrade_is_false() {
let result = parse_cli_args(&[]).unwrap();
assert!(!result.upgrade);
assert_eq!(result.upgrade_source, None);
}
// ── resolve_path_arg ────────────────────────────────────────────
#[test]