fix: stop Galaxy before replacing app

This commit is contained in:
2026-08-16 00:57:08 -05:00
parent b5ad2a4ab0
commit 04bc7f7055
@@ -2,7 +2,7 @@
import React, { useState, useEffect, useCallback } from "react"; import React, { useState, useEffect, useCallback } from "react";
import { render, Text, Box } from "ink"; import { render, Text, Box } from "ink";
import Spinner from "ink-spinner"; import Spinner from "ink-spinner";
import { spawn, execSync } from "child_process"; import { spawn, execFileSync, execSync } from "child_process";
import path from "path"; import path from "path";
import fs from "fs"; import fs from "fs";
@@ -59,15 +59,48 @@ function runCommandStreaming(
}); });
} }
function isAppRunning(appName: string): boolean { function isAppRunning(processName: string): boolean {
try { try {
execSync(`pgrep -x "${appName}"`, { stdio: "ignore" }); execFileSync("pgrep", ["-x", processName], { stdio: "ignore" });
return true; return true;
} catch { } catch {
return false; return false;
} }
} }
async function waitForAppToExit(processName: string, timeoutMs: number): Promise<boolean> {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
if (!isAppRunning(processName)) return true;
await new Promise((resolve) => setTimeout(resolve, 100));
}
return !isAppRunning(processName);
}
async function stopApp(processName: string, onLog: (line: string) => void): Promise<void> {
if (!isAppRunning(processName)) return;
onLog(`${processName} is running. Stopping it before replacing the app...`);
try {
execFileSync("pkill", ["-TERM", "-x", processName], { stdio: "ignore" });
} catch {
// The process may exit between the running check and the signal.
}
if (await waitForAppToExit(processName, 2000)) return;
onLog(`${processName} did not exit after SIGTERM. Force killing it...`);
try {
execFileSync("pkill", ["-KILL", "-x", processName], { stdio: "ignore" });
} catch {
// Verify the process state below instead of relying on pkill's exit status.
}
if (!(await waitForAppToExit(processName, 1000))) {
throw new Error(`Unable to stop ${processName} before updating /Applications`);
}
}
// ─── UI Component ──────────────────────────────────────────────────────────── // ─── UI Component ────────────────────────────────────────────────────────────
function StepLine({ step }: { step: Step }) { function StepLine({ step }: { step: Step }) {
@@ -158,15 +191,7 @@ function App() {
throw new Error(`Built app not found at ${appPath}`); throw new Error(`Built app not found at ${appPath}`);
} }
if (isAppRunning("Galaxy")) { await stopApp("Galaxy", (line) => appendLog(1, line));
appendLog(1, "Galaxy is running. Quitting it before replacing...");
try {
execSync(`osascript -e 'quit app "Galaxy"'`, { stdio: "ignore" });
await new Promise((r) => setTimeout(r, 2000));
} catch {
// ignore
}
}
if (fs.existsSync(destPath)) { if (fs.existsSync(destPath)) {
appendLog(1, `Removing existing ${destPath}`); appendLog(1, `Removing existing ${destPath}`);