use crate::args::RepairArgs;
use crate::exit_codes::EXIT_REPAIR_FAILED;
use crate::installer;
use crate::style::{self, Palette};
use anyhow::Result;
use std::process::ExitCode;
pub(crate) async fn run(args: RepairArgs) -> Result<ExitCode> {
let palette = style::for_stdout();
let Palette {
yellow,
dim,
bold,
reset,
..
} = palette;
println!("{bold}keyhog repair{reset}");
let self_test = installer::scan_engine_self_test();
let healthy = matches!(self_test, Ok(true));
if healthy && !args.force {
println!(
" {} scan engine healthy - nothing to repair.",
style::pass("PASS", &palette)
);
println!(" {dim}use --force to reinstall the newest binary release asset anyway.{reset}");
return Ok(ExitCode::SUCCESS);
}
if healthy {
println!(" {dim}--force: reinstalling a fresh binary.{reset}");
} else {
match &self_test {
Ok(false) => {
println!(
" {yellow}self-test failed{reset} (planted secret was not detected) - reinstalling a fresh binary."
);
}
Err(error) => {
println!(
" {yellow}self-test failed{reset} ({error}) - reinstalling a fresh binary."
);
}
Ok(true) => {}
}
}
let client = installer::http_client()?;
let release = installer::resolve_release(&client, args.version.as_deref()).await?;
let asset = installer::select_asset(&release)?;
let expected_tag = release.tag_name.clone();
let allow_explicit_downgrade = args.version.is_some();
println!(" downloading {} ({})", asset.name, release.tag_name);
let bytes = installer::download_verified_asset(&client, &release, asset).await?;
let gpu_literal_asset = installer::select_gpu_literal_asset(&release, asset)?;
println!(" gpu literals {}", gpu_literal_asset.name);
let gpu_literal_bytes =
installer::download_verified_gpu_literal_asset(&client, &release, gpu_literal_asset)
.await?;
let gpu_literal_files =
installer::parse_gpu_literal_sidecar(&gpu_literal_bytes, &expected_tag)?;
let exe = installer::current_binary()?;
installer::reap_stale_binaries(&exe);
println!("\n{dim}reinstalling and verifying the new binary...{reset}\n");
match installer::install_with_rollback_checked(&exe, &bytes, |candidate| {
let gpu_transaction = installer::install_gpu_literal_files(&gpu_literal_files)?;
installer::verify_candidate_release(
candidate,
&expected_tag,
env!("CARGO_PKG_VERSION"),
allow_explicit_downgrade,
)?;
gpu_transaction.commit();
Ok(())
}) {
Ok(()) => {
let _report_span = keyhog_profile::span(keyhog_profile::Stage::Reporting);
println!(
"\n{} repaired: reinstalled {} and verified healthy.",
style::pass("PASS", &palette),
release.tag_name,
);
Ok(ExitCode::SUCCESS)
}
Err(e) => {
let stderr_palette = style::for_stderr();
let Palette { dim, reset, .. } = stderr_palette;
eprintln!(
"\n{} repair of {} did not produce a healthy binary: {e}\n\
{dim}If a shared library is missing, install it (see the doctor output above) \
and retry, or try `keyhog repair --version <older-tag>`.{reset}",
style::fail("FAIL", &stderr_palette),
release.tag_name
);
Ok(ExitCode::from(EXIT_REPAIR_FAILED))
}
}
}