mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-17 13:13:35 +08:00
fix(desktop): stop sidecars before updater install
This commit is contained in:
parent
0ef020ebf8
commit
55c5958b55
4
.github/workflows/release-desktop.yml
vendored
4
.github/workflows/release-desktop.yml
vendored
@ -55,7 +55,7 @@ jobs:
|
||||
# Windows x64
|
||||
- platform: windows-latest
|
||||
rust_target: x86_64-pc-windows-msvc
|
||||
tauri_args: '--bundles msi'
|
||||
tauri_args: '--bundles nsis'
|
||||
label: Windows-x64
|
||||
asset_name_pattern: 'Claude-Code-Haha_[version]_windows_x64_[bundle][ext]'
|
||||
|
||||
@ -176,6 +176,6 @@ jobs:
|
||||
releaseBody: ${{ steps.release_notes.outputs.body }}
|
||||
releaseDraft: ${{ github.event_name == 'workflow_dispatch' && inputs.draft || false }}
|
||||
prerelease: false
|
||||
updaterJsonPreferNsis: false
|
||||
updaterJsonPreferNsis: true
|
||||
releaseAssetNamePattern: ${{ matrix.asset_name_pattern }}
|
||||
args: ${{ matrix.tauri_args }} --config src-tauri/tauri.release-ci.json
|
||||
|
||||
@ -112,6 +112,22 @@ fn restart_adapters_sidecar(app: AppHandle) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn prepare_for_update_install(app: AppHandle) -> Result<(), String> {
|
||||
stop_server_sidecar(&app);
|
||||
stop_adapters_sidecar(&app);
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
kill_windows_sidecars();
|
||||
}
|
||||
|
||||
// Give Windows a short moment to release executable file handles before the
|
||||
// updater starts replacing bundled sidecars in the install directory.
|
||||
std::thread::sleep(Duration::from_millis(750));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn terminal_spawn(
|
||||
app: AppHandle,
|
||||
@ -721,6 +737,21 @@ fn stop_adapters_sidecar(app: &AppHandle) {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn kill_windows_sidecars() {
|
||||
for image_name in [
|
||||
"claude-sidecar-x86_64-pc-windows-msvc.exe",
|
||||
"claude-sidecar-aarch64-pc-windows-msvc.exe",
|
||||
"claude-sidecar.exe",
|
||||
] {
|
||||
let _ = StdCommand::new("taskkill")
|
||||
.args(["/F", "/T", "/IM", image_name])
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.status();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{decode_terminal_output, default_utf8_locale, ensure_utf8_locale, parse_env_block};
|
||||
@ -817,6 +848,7 @@ pub fn run() {
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
get_server_url,
|
||||
restart_adapters_sidecar,
|
||||
prepare_for_update_install,
|
||||
terminal_spawn,
|
||||
terminal_write,
|
||||
terminal_resize,
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
!macro NSIS_HOOK_PREINSTALL
|
||||
DetailPrint "Stopping running Claude Code Haha processes..."
|
||||
nsExec::ExecToLog 'taskkill /F /T /IM claude-code-desktop.exe'
|
||||
DetailPrint "Stopping running Claude Code Haha sidecars..."
|
||||
nsExec::ExecToLog 'taskkill /F /T /IM claude-sidecar-x86_64-pc-windows-msvc.exe'
|
||||
Pop $0
|
||||
nsExec::ExecToLog 'taskkill /F /T /IM claude-sidecar-aarch64-pc-windows-msvc.exe'
|
||||
Pop $0
|
||||
nsExec::ExecToLog 'taskkill /F /T /IM claude-sidecar.exe'
|
||||
Pop $0
|
||||
@ -11,6 +13,10 @@
|
||||
DetailPrint "Stopping running Claude Code Haha processes..."
|
||||
nsExec::ExecToLog 'taskkill /F /T /IM claude-code-desktop.exe'
|
||||
Pop $0
|
||||
nsExec::ExecToLog 'taskkill /F /T /IM claude-sidecar-x86_64-pc-windows-msvc.exe'
|
||||
Pop $0
|
||||
nsExec::ExecToLog 'taskkill /F /T /IM claude-sidecar-aarch64-pc-windows-msvc.exe'
|
||||
Pop $0
|
||||
nsExec::ExecToLog 'taskkill /F /T /IM claude-sidecar.exe'
|
||||
Pop $0
|
||||
Sleep 1000
|
||||
|
||||
@ -2,6 +2,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const check = vi.fn()
|
||||
const relaunch = vi.fn()
|
||||
const invoke = vi.fn()
|
||||
|
||||
vi.mock('@tauri-apps/plugin-updater', () => ({
|
||||
check,
|
||||
@ -11,10 +12,15 @@ vi.mock('@tauri-apps/plugin-process', () => ({
|
||||
relaunch,
|
||||
}))
|
||||
|
||||
vi.mock('@tauri-apps/api/core', () => ({
|
||||
invoke,
|
||||
}))
|
||||
|
||||
describe('updateStore', () => {
|
||||
beforeEach(() => {
|
||||
check.mockReset()
|
||||
relaunch.mockReset()
|
||||
invoke.mockReset()
|
||||
window.localStorage.clear()
|
||||
Object.defineProperty(window, '__TAURI_INTERNALS__', {
|
||||
configurable: true,
|
||||
@ -89,20 +95,23 @@ describe('updateStore', () => {
|
||||
expect(useUpdateStore.getState().shouldPrompt).toBe(true)
|
||||
})
|
||||
|
||||
it('computes download progress from content length and relaunches after install', async () => {
|
||||
const downloadAndInstall = vi.fn(async (onEvent?: (event: unknown) => void) => {
|
||||
it('downloads, stops sidecars, installs, and relaunches', async () => {
|
||||
const download = vi.fn(async (onEvent?: (event: unknown) => void) => {
|
||||
onEvent?.({ event: 'Started', data: { contentLength: 200 } })
|
||||
onEvent?.({ event: 'Progress', data: { chunkLength: 50 } })
|
||||
onEvent?.({ event: 'Progress', data: { chunkLength: 150 } })
|
||||
onEvent?.({ event: 'Finished' })
|
||||
})
|
||||
const install = vi.fn().mockResolvedValue(undefined)
|
||||
|
||||
check.mockResolvedValue({
|
||||
version: '0.2.0',
|
||||
body: 'Notes',
|
||||
downloadAndInstall,
|
||||
download,
|
||||
install,
|
||||
close: vi.fn().mockResolvedValue(undefined),
|
||||
})
|
||||
invoke.mockResolvedValue(undefined)
|
||||
relaunch.mockResolvedValue(undefined)
|
||||
|
||||
vi.resetModules()
|
||||
@ -111,7 +120,14 @@ describe('updateStore', () => {
|
||||
await useUpdateStore.getState().checkForUpdates()
|
||||
await useUpdateStore.getState().installUpdate()
|
||||
|
||||
expect(downloadAndInstall).toHaveBeenCalledTimes(1)
|
||||
expect(download).toHaveBeenCalledTimes(1)
|
||||
expect(invoke).toHaveBeenCalledWith('prepare_for_update_install')
|
||||
expect(install).toHaveBeenCalledTimes(1)
|
||||
const prepareCallOrder = invoke.mock.invocationCallOrder[0]
|
||||
const installCallOrder = install.mock.invocationCallOrder[0]
|
||||
expect(prepareCallOrder).toBeDefined()
|
||||
expect(installCallOrder).toBeDefined()
|
||||
expect(prepareCallOrder!).toBeLessThan(installCallOrder!)
|
||||
expect(useUpdateStore.getState().progressPercent).toBe(100)
|
||||
expect(useUpdateStore.getState().status).toBe('restarting')
|
||||
expect(relaunch).toHaveBeenCalledTimes(1)
|
||||
|
||||
@ -191,11 +191,12 @@ export const useUpdateStore = create<UpdateStore>((set, get) => ({
|
||||
|
||||
try {
|
||||
writeDismissedUpdateVersion(null)
|
||||
const { invoke } = await import('@tauri-apps/api/core')
|
||||
const { relaunch } = await import('@tauri-apps/plugin-process')
|
||||
let totalBytes: number | null = null
|
||||
let downloadedBytes = 0
|
||||
|
||||
await update.downloadAndInstall((event) => {
|
||||
await update.download((event) => {
|
||||
if (event.event === 'Started') {
|
||||
totalBytes = event.data.contentLength ?? null
|
||||
downloadedBytes = 0
|
||||
@ -226,6 +227,9 @@ export const useUpdateStore = create<UpdateStore>((set, get) => ({
|
||||
}
|
||||
})
|
||||
|
||||
await invoke('prepare_for_update_install')
|
||||
await update.install()
|
||||
|
||||
set((state) => ({
|
||||
...state,
|
||||
status: 'restarting',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user