Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,12 @@ jobs:
with:
tagName: v__VERSION__
releaseName: "RelWave v__VERSION__"
releaseBody: "See the assets to download this version and install."
# Let GitHub auto-generate the changelog from merged PRs and commits
# since the last release tag. The generated markdown is injected into
# latest.json as the `notes` field, which the in-app updater reads and
# displays in the What's New dialog after update.
releaseBody: ""
generateReleaseNotes: true
releaseDraft: true
prerelease: false
args: ${{ matrix.args }}
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@

_A high-performance, cross-platform desktop suite for developers who demand more from their database tools._

[![Version](https://img.shields.io/badge/version-0.6.0--beta.1-0066ff?style=for-the-badge&logo=semver)](https://github.com/Relwave/relwave-app/releases)
[![Version](https://img.shields.io/badge/version-0.6.0--beta.5-0066ff?style=for-the-badge&logo=semver)](https://github.com/Relwave/relwave-app/releases)
[![License](https://img.shields.io/badge/license-MIT-00cc66?style=for-the-badge)](LICENSE)
[![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20Linux-lightgray?style=for-the-badge&logo=windows)](https://github.com/Relwave/relwave-app/releases)
[![Tauri](https://img.shields.io/badge/built%20with-Tauri-FFC131?style=for-the-badge&logo=tauri)](https://tauri.app/)

[**🚀 Quick Start**](#quick-start) • [**📥 Download**](https://github.com/Relwave/relwave-app/releases)
[**🚀 Quick Start**](INSTALLATION.md) • [**📥 Download**](https://github.com/Relwave/relwave-app/releases)

---

Expand Down
18 changes: 15 additions & 3 deletions src-tauri/nsis/installer-hooks.nsi
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
; RelWave NSIS Installer Hooks
; Kill bridge.exe before install/update to prevent "Error opening file for writing" errors
; Kill bridge and Node processes before install/update to prevent
; "Error opening file for writing" errors caused by open file handles on
; better_sqlite3.node and other bundled native resources.

!macro NSIS_HOOK_PREINSTALL
; Kill any running bridge process (ignore errors if not running)
; 1. Kill the bridge binary (pkg-compiled production binary)
nsExec::ExecToLog 'taskkill /F /IM "bridge-x86_64-pc-windows-msvc.exe" /T'

; 2. Kill node.exe in case the bridge is running as a Node.js script
; (dev mode or script-mode fallback). /T kills child processes too.
nsExec::ExecToLog 'taskkill /F /IM "node.exe" /T'

; 3. Kill the main app window (covers the case where the user launched
; the installer without closing RelWave first)
nsExec::ExecToLog 'taskkill /F /IM "RelWave.exe" /T'
Sleep 1000

; 4. Give the OS time to fully release all file handles before the
; installer tries to overwrite better_sqlite3.node and other resources.
Sleep 1500
!macroend
15 changes: 15 additions & 0 deletions src-tauri/src/bridge/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,21 @@ pub fn bridge_write(data: String, state: State<'_, BridgeProcess>) -> Result<(),
Ok(())
}

/// Kill the bridge process and wait for it to fully exit.
/// Called before applying an update so the bridge releases file handles on
/// bundled resources (e.g. better_sqlite3.node) before the installer overwrites them.
#[tauri::command]
pub fn bridge_kill(state: State<'_, BridgeProcess>) -> Result<(), String> {
let mut guard = state.0.lock().unwrap();
if let Some(mut child) = guard.take() {
child.kill().map_err(|e| format!("failed to kill bridge: {}", e))?;
// wait() is essential — it ensures the OS fully closes all file handles
// before we return, giving the installer a clean shot at the files.
child.wait().map_err(|e| format!("failed to wait for bridge exit: {}", e))?;
}
Ok(())
}

/// Restart the bridge process
#[tauri::command]
pub fn bridge_restart(
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/src/bridge/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ mod process;
mod commands;

pub use process::BridgeProcess;
pub use commands::{bridge_write, bridge_restart, bridge_status};
pub use commands::{bridge_write, bridge_kill, bridge_restart, bridge_status};

use tauri::AppHandle;
use std::process::Child;
Expand Down
3 changes: 2 additions & 1 deletion src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
mod bridge;
mod devtools;

use bridge::{bridge_restart, bridge_status, bridge_write, BridgeProcess};
use bridge::{bridge_restart, bridge_kill, bridge_status, bridge_write, BridgeProcess};
use devtools::{close_devtools, is_devtools_open, navigate_back, navigate_forward, open_devtools, reload_webview};
use tauri::Manager;

Expand All @@ -21,6 +21,7 @@ fn main() {
})
.invoke_handler(tauri::generate_handler![
bridge_write,
bridge_kill,
bridge_restart,
bridge_status,
open_devtools,
Expand Down
12 changes: 12 additions & 0 deletions src/components/shared/WhatsNewDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,18 @@ function parseReleaseBody(body?: string): NoteSection[] {
return FALLBACK_SECTIONS;
}

// GitHub generates a default `notes` value when no release notes are written.
// Treat it (and similar content-free strings) as "no notes" so we fall back
// to the generic highlights instead of surfacing a useless placeholder.
const KNOWN_PLACEHOLDERS = [
"see the assets to download this version and install",
"no release notes provided",
];
const normalized = body.trim().toLowerCase();
if (KNOWN_PLACEHOLDERS.some((p) => normalized === p || normalized.startsWith(p))) {
return FALLBACK_SECTIONS;
}

const lines = body
.split(/\r?\n/)
.map((line) => line.trim())
Expand Down
13 changes: 13 additions & 0 deletions src/features/settings/hooks/useUpdater.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useState, useEffect, useCallback } from "react";
import { check, Update } from "@tauri-apps/plugin-updater";
import { relaunch } from "@tauri-apps/plugin-process";
import { invoke } from "@tauri-apps/api/core";

const LAST_INSTALLED_UPDATE_KEY = "relwave:last-installed-update";
const RELEASES_URL = "https://github.com/Relwave/relwave-app/releases/latest";
Expand Down Expand Up @@ -109,6 +110,18 @@ export function useUpdater(): UseUpdaterReturn {
try {
setStatus("downloading");
setDownloadProgress(0);

// Kill the bridge process BEFORE the installer runs so that it releases
// its file-handle on better_sqlite3.node (and other bundled resources).
// Without this, the NSIS installer throws "Error opening file for writing"
// because the native module is still memory-mapped by the bridge process.
try {
await invoke("bridge_kill");
// Give the OS a moment to fully release the file handles.
await new Promise((r) => setTimeout(r, 800));
} catch {
// Non-fatal: if the bridge is already dead, continue with the update.
}

let downloaded = 0;
let contentLength = 0;
Expand Down
Loading