Automate sidecar bundling and simplify sidecar path resolution

This commit is contained in:
Jacob Schmidt 2026-02-28 15:05:02 -06:00
parent 4e2eaf9059
commit f8760155e4
4 changed files with 76 additions and 25 deletions

View File

@ -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",

View File

@ -110,8 +110,8 @@ struct ManagedSidecar {
}
impl ManagedSidecar {
fn start(root: &Path) -> Result<Self, String> {
let sidecar_path = resolve_sidecar_path(root)?;
fn start(root: &Path, resource_dir: Option<&Path>) -> Result<Self, String> {
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<Option<ManagedSidecar>>,
root_override: Mutex<Option<PathBuf>>,
config_path: PathBuf,
resource_dir: Option<PathBuf>,
}
fn load_settings(path: &Path) -> AppSettings {
@ -221,44 +222,37 @@ fn effective_root(root_override: &Option<PathBuf>) -> Result<PathBuf, String> {
auto_detect_root()
}
fn resolve_sidecar_path(root: &Path) -> Result<PathBuf, String> {
fn resolve_sidecar_path(root: &Path, resource_dir: Option<&Path>) -> Result<PathBuf, String> {
#[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<PathBuf> {
@ -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(())
})

View File

@ -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",

View File

@ -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