implement installer as library and use it in gui

This commit is contained in:
Markus Unterwaditzer
2025-11-08 15:14:09 +01:00
committed by Markus Unterwaditzer
parent 9e9fe4d392
commit 3b44234ae1
19 changed files with 285 additions and 151 deletions
+9 -3
View File
@@ -12,16 +12,22 @@ You'll need to install [Tauri's dependencies](https://tauri.app/start/prerequisi
### Rayhunter CLI Installer
The Rayhunter GUI installer currently just bundles and wraps the CLI Rayhunter installer. When building the GUI installer, the CLI installer needs to be built and available for bundling. By default it assumed the installer is present in the repo's `target` directory at either `debug/installer` or `release/installer` depending on whether you're doing a debug or release build of the GUI installer.
The GUI installer pulls in the CLI installer as a library. Like with the CLI installer, the firmware binary needs to be present and can be overridden with the same envvars. See `../installer/build.rs` for options.
You can use a different path by setting the environment variable INSTALLER_PATH when the GUI installer being built. You can also use the environment variable SKIP_INSTALLER_COPY which leaves any previously bundled CLI installer unmodified or if one does not exist bundles a dummy installer file allowing the GUI installer to be successfully built.
For example, to build the firmware in development mode and then provide the path explicitly:
```bash
cargo build-daemon-firmware-devel
(cd installer-gui && FILE_RAYHUNTER_DAEMON=$PWD/../target/armv7-unknown-linux-musleabihf/firmware-devel/rayhunter-daemon npm run tauri android build)
```
## Building
After preparing dependencies, the GUI installer can be built by:
1. Running `npm install` in this directory.
2. Setting INSTALLER_PATH or SKIP_INSTALLER_COPY if desired and running `npm run tauri dev`.
2. Running `npm run tauri dev`.
This will build the GUI installer in development mode. While this command is running, any changes to either the frontend or backend code will cause the installer to be reloaded or rebuilt.
+1 -1
View File
@@ -22,4 +22,4 @@ serde = { version = "1", features = ["derive"] }
serde_json = "1"
tauri-plugin-shell = "2"
anyhow = "1.0.100"
installer = { path = "../../installer" }
-49
View File
@@ -1,52 +1,3 @@
use std::env::consts::EXE_SUFFIX;
use std::path::PathBuf;
fn main() {
println!("cargo::rerun-if-env-changed=INSTALLER_PATH");
println!("cargo::rerun-if-env-changed=SKIP_INSTALLER_COPY");
let destination = get_installer_destination();
if std::env::var_os("SKIP_INSTALLER_COPY").is_none() {
let cli_installer =
std::env::var_os("INSTALLER_PATH").map_or_else(default_installer_path, PathBuf::from);
if !cli_installer.exists() {
println!(
"cargo::error=CLI installer binary not present at {}",
cli_installer.display()
);
std::process::exit(0);
}
std::fs::copy(&cli_installer, &destination).unwrap();
println!("cargo::rerun-if-changed={}", cli_installer.display());
println!("cargo::rerun-if-changed={}", destination.display());
} else if !destination.exists() {
// if SKIP_INSTALLER_COPY is set, make sure something exists at destination so the build succeeds
std::fs::write(&destination, []).unwrap();
}
tauri_build::build()
}
fn default_installer_path() -> PathBuf {
// the approach used here was taken from https://github.com/rust-lang/cargo/issues/9661#issuecomment-1722358176
let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap());
let profile = std::env::var("PROFILE").unwrap();
let profile_dir = out_dir
.ancestors()
.find(|&path| path.ends_with(&profile))
.unwrap();
profile_dir.join(format!("installer{EXE_SUFFIX}"))
}
fn get_installer_destination() -> PathBuf {
// tauri expects included binaries to have the target triple appended to the file name like
// this. see https://tauri.app/develop/sidecar/
let target_triple = std::env::var("TARGET").unwrap();
[
"binaries",
&format!("installer-cli-{target_triple}{EXE_SUFFIX}"),
]
.iter()
.collect()
}
+11 -22
View File
@@ -1,29 +1,18 @@
use anyhow::Context;
use tauri::Emitter;
use tauri_plugin_shell::ShellExt;
use tauri_plugin_shell::process::CommandEvent;
async fn run_installer(app_handle: tauri::AppHandle, args: String) -> anyhow::Result<()> {
let (mut rx, _child) = app_handle
.shell()
.sidecar("installer-cli")
.context("Error preparing Rayhunter CLI installer to be run")?
.args(args.split_whitespace())
.spawn()
.context("Error launching Rayhunter CLI installer")?;
while let Some(event) = rx.recv().await {
match event {
CommandEvent::Stdout(line_bytes) | CommandEvent::Stderr(line_bytes) => {
let line = String::from_utf8(line_bytes)
.context("Error parsing Rayhunter CLI installer output")?;
tauri::async_runtime::spawn_blocking(move || {
installer::run_with_callback(
// TODO: we should split using something similar to shlex in python
args.split_whitespace().map(String::from).collect(),
Some(Box::new(move |output| {
app_handle
.emit("installer-output", &line)
.context("Error sending Rayhunter CLI installer output to GUI frontend")?;
}
_ => (),
};
}
Ok(())
.emit("installer-output", output)
.expect("Error sending Rayhunter CLI installer output to GUI frontend");
})),
)
})
.await?
}
#[tauri::command]
-1
View File
@@ -23,7 +23,6 @@
"bundle": {
"active": true,
"targets": ["app", "appimage", "deb", "msi", "nsis", "rpm"],
"externalBin": ["binaries/installer-cli"],
"icon": [
"icons/32x32.png",
"icons/128x128.png",