use std::path::PathBuf;
use clap::Args;
use crate::update;
#[derive(Args, Debug, Clone)]
pub struct SpikeUpdateArgs {
#[arg(long)]
pub exe: PathBuf,
#[arg(long)]
pub hook: PathBuf,
#[arg(long)]
pub sig_dir: PathBuf,
#[arg(long)]
pub expected_version: String,
#[arg(long, hide = true)]
pub hook_install_path: Option<PathBuf>,
#[arg(long)]
pub simulate_restart_loop: bool,
}
pub fn run(args: &SpikeUpdateArgs) -> bool {
eprintln!("[spike] Phase 1 — verifying signatures…");
let exe_sig = args.sig_dir.join("openlatch.minisig");
if let Err(e) = update::verify_with_any_trusted_key(&args.exe, &exe_sig) {
eprintln!("[spike] daemon signature verify failed: {e}");
return false;
}
let hook_sig = args.sig_dir.join("openlatch-hook.minisig");
if let Err(e) = update::verify_with_any_trusted_key(&args.hook, &hook_sig) {
eprintln!("[spike] hook signature verify failed: {e}");
return false;
}
eprintln!("[spike] signatures OK");
eprintln!("[spike] sanity-checking staging binary…");
if let Err(e) = update::sanity_check_version(&args.exe, &args.expected_version) {
eprintln!("[spike] sanity check failed: {e}");
return false;
}
eprintln!("[spike] sanity OK");
eprintln!("[spike] resolving hook install path…");
let hook_path = match args.hook_install_path.as_deref() {
Some(p) => p.to_path_buf(),
None => match update::locate_hook_binary() {
Ok(p) => p,
Err(e) => {
eprintln!("[spike] hook resolution failed: {e}");
return false;
}
},
};
eprintln!("[spike] hook resolved at {}", hook_path.display());
eprintln!("[spike] performing swap…");
let handle = match update::perform_swap(&args.exe, &args.hook, &hook_path) {
Ok(h) => h,
Err(e) => {
eprintln!("[spike] swap failed: {e}");
return false;
}
};
eprintln!("[spike] swap complete");
eprintln!("[spike] current_exe = {}", handle.current_exe.display());
eprintln!("[spike] hook_path = {}", handle.hook_path.display());
eprintln!("[spike] hook_bak = {}", handle.hook_bak.display());
if args.simulate_restart_loop {
eprintln!("[spike] simulating rollback (restore_from_bak)…");
if let Err(e) = update::restore_from_bak(&handle) {
eprintln!("[spike] rollback failed: {e}");
return false;
}
eprintln!("[spike] rollback complete (hook restored)");
eprintln!(
"[spike] note: daemon-side restore is implementation-defined \
(self_replace leaves a `.<random>` sibling in the install \
directory) and is left to P3's restart-loop rollback."
);
}
eprintln!("[spike] DONE");
true
}