From f8760155e4da7fff1edb1a81072861e77d29b197 Mon Sep 17 00:00:00 2001 From: Jacob Schmidt Date: Sat, 28 Feb 2026 15:05:02 -0600 Subject: [PATCH] Automate sidecar bundling and simplify sidecar path resolution --- Journal.App/package.json | 1 + Journal.App/src-tauri/src/lib.rs | 43 ++++++++++------------ Journal.App/src-tauri/tauri.conf.json | 5 ++- scripts/tauri-prebuild.ps1 | 52 +++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 25 deletions(-) create mode 100644 scripts/tauri-prebuild.ps1 diff --git a/Journal.App/package.json b/Journal.App/package.json index bcb8b0a..cece5f0 100644 --- a/Journal.App/package.json +++ b/Journal.App/package.json @@ -6,6 +6,7 @@ "scripts": { "dev": "vite dev", "build": "vite build", + "tauri:prebuild": "pwsh -NoProfile -ExecutionPolicy Bypass -File ../scripts/tauri-prebuild.ps1", "preview": "vite preview", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", diff --git a/Journal.App/src-tauri/src/lib.rs b/Journal.App/src-tauri/src/lib.rs index cb96fe6..ba633de 100644 --- a/Journal.App/src-tauri/src/lib.rs +++ b/Journal.App/src-tauri/src/lib.rs @@ -110,8 +110,8 @@ struct ManagedSidecar { } impl ManagedSidecar { - fn start(root: &Path) -> Result { - let sidecar_path = resolve_sidecar_path(root)?; + fn start(root: &Path, resource_dir: Option<&Path>) -> Result { + let sidecar_path = resolve_sidecar_path(root, resource_dir)?; let mut cmd = Command::new(sidecar_path); cmd.stdin(Stdio::piped()) .stdout(Stdio::piped()) @@ -186,6 +186,7 @@ struct SidecarState { process: Mutex>, root_override: Mutex>, config_path: PathBuf, + resource_dir: Option, } fn load_settings(path: &Path) -> AppSettings { @@ -221,44 +222,37 @@ fn effective_root(root_override: &Option) -> Result { auto_detect_root() } -fn resolve_sidecar_path(root: &Path) -> Result { +fn resolve_sidecar_path(root: &Path, resource_dir: Option<&Path>) -> Result { #[cfg(windows)] let exe_name = "Journal.Sidecar.exe"; #[cfg(not(windows))] let exe_name = "Journal.Sidecar"; - // 1. If root is explicitly pointing to the executable file itself: if root.is_file() && root.file_name().and_then(|n| n.to_str()) == Some(exe_name) { return Ok(root.to_path_buf()); } - // 2. Direct paths relative to the folder: - let direct_paths = [ - root.join(exe_name), - root.join("output").join(exe_name), - root.join("publish").join(exe_name), - root.join("Journal.Sidecar/bin/Debug/net10.0").join(exe_name), - root.join("Journal.Sidecar/bin/Release/net10.0/win-x64/publish").join(exe_name), - ]; - - for path in direct_paths { - if path.exists() { - return Ok(path); - } + let root_exe_path = root.join(exe_name); + if root_exe_path.exists() { + return Ok(root_exe_path); } - // 3. Fallback: recursively search the known sidecar folder let sidecar_src_root = root.join("Journal.Sidecar"); if let Some(path) = find_sidecar_executable(&sidecar_src_root, exe_name) { return Ok(path); } - // 4. Fallback: recursively search the provided root itself - if let Some(path) = find_sidecar_executable(root, exe_name) { - return Ok(path); + if let Some(resource_dir) = resource_dir { + let resource_sidecar_path = resource_dir.join("bin").join(exe_name); + if resource_sidecar_path.exists() { + return Ok(resource_sidecar_path); + } } - Err(format!("{exe_name} not found in {}. Build Journal.Sidecar first.", root.display())) + Err(format!( + "{exe_name} not found in root, Journal.Sidecar tree, or resource dir for {}.", + root.display() + )) } fn find_sidecar_executable(search_root: &Path, exe_name: &str) -> Option { @@ -317,7 +311,7 @@ async fn send_with_managed_sidecar( None => true, }; if should_start { - *guard = Some(ManagedSidecar::start(&root)?); + *guard = Some(ManagedSidecar::start(&root, state.resource_dir.as_deref())?); } let Some(process) = guard.as_mut() else { @@ -369,7 +363,7 @@ async fn set_sidecar_root( new_root.display() )); } - resolve_sidecar_path(&new_root)?; + resolve_sidecar_path(&new_root, state.resource_dir.as_deref())?; (Some(new_root.clone()), new_root) }; @@ -476,6 +470,7 @@ pub fn run() { process: Mutex::new(None), root_override: Mutex::new(root_override), config_path, + resource_dir: app.path().resource_dir().ok(), }); Ok(()) }) diff --git a/Journal.App/src-tauri/tauri.conf.json b/Journal.App/src-tauri/tauri.conf.json index 2b445fc..0a5189e 100644 --- a/Journal.App/src-tauri/tauri.conf.json +++ b/Journal.App/src-tauri/tauri.conf.json @@ -6,7 +6,7 @@ "build": { "beforeDevCommand": "npm run dev", "devUrl": "http://localhost:1420", - "beforeBuildCommand": "npm run build", + "beforeBuildCommand": "npm run tauri:prebuild && npm run build", "frontendDist": "../build" }, "app": { @@ -24,6 +24,9 @@ "bundle": { "active": true, "targets": "all", + "resources": [ + "bin/Journal.Sidecar.exe" + ], "icon": [ "icons/32x32.png", "icons/128x128.png", diff --git a/scripts/tauri-prebuild.ps1 b/scripts/tauri-prebuild.ps1 new file mode 100644 index 0000000..0dbb93f --- /dev/null +++ b/scripts/tauri-prebuild.ps1 @@ -0,0 +1,52 @@ +param( + [ValidateSet("Release", "Debug")] + [string]$Configuration = "Release", + [string]$Runtime = "win-x64" +) + +$ErrorActionPreference = "Stop" + +$repoRoot = Resolve-Path (Join-Path $PSScriptRoot "..") +$sidecarProject = Join-Path $repoRoot "Journal.Sidecar\Journal.Sidecar.csproj" +$publishOutputDir = Join-Path $repoRoot "output" +$publishedSidecarExe = Join-Path $publishOutputDir "Journal.Sidecar.exe" +$tauriBinDir = Join-Path $repoRoot "Journal.App\src-tauri\bin" +$tauriBundledSidecarExe = Join-Path $tauriBinDir "Journal.Sidecar.exe" + +if (-not (Get-Command dotnet -ErrorAction SilentlyContinue)) { + throw "dotnet was not found in PATH. Install .NET SDK and retry." +} + +if (-not (Test-Path $sidecarProject)) { + throw "Sidecar project not found: $sidecarProject" +} + +New-Item -ItemType Directory -Force -Path $publishOutputDir | Out-Null + +$publishArgs = @( + "publish", $sidecarProject, + "-c", $Configuration, + "-r", $Runtime, + "--self-contained", + "-p:PublishSingleFile=true", + "-p:IncludeNativeLibrariesForSelfExtract=true", + "-p:RestoreIgnoreFailedSources=true", + "-p:NuGetAudit=false", + "-o", $publishOutputDir +) + +Write-Host "Publishing Journal.Sidecar for Tauri bundle..." -ForegroundColor Cyan +Write-Host "> dotnet $($publishArgs -join ' ')" -ForegroundColor DarkGray +& dotnet @publishArgs + +if ($LASTEXITCODE -ne 0) { + throw "dotnet publish failed with exit code $LASTEXITCODE." +} + +if (-not (Test-Path $publishedSidecarExe)) { + throw "Sidecar publish output not found: $publishedSidecarExe" +} + +New-Item -ItemType Directory -Force -Path $tauriBinDir | Out-Null +Copy-Item -Force $publishedSidecarExe $tauriBundledSidecarExe +Write-Host "Staged sidecar for Tauri: $tauriBundledSidecarExe" -ForegroundColor Green