From 669a7fc7489aabb8123e6c05e2e8c649ff9f2a4d Mon Sep 17 00:00:00 2001 From: Ryan Ward Date: Tue, 29 Sep 2026 10:45:58 -0500 Subject: [PATCH 1/2] install script changes --- .../src/index.tsx | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/script/build-and-install-to-applications/src/index.tsx b/script/build-and-install-to-applications/src/index.tsx index 2e747e78..a681fda8 100644 --- a/script/build-and-install-to-applications/src/index.tsx +++ b/script/build-and-install-to-applications/src/index.tsx @@ -27,13 +27,14 @@ const MAX_LOG_LINES = 15; function runCommandStreaming( cmd: string, cwd: string, - onLog: (line: string) => void + onLog: (line: string) => void, + interactive = false ): Promise { return new Promise((resolve, reject) => { const child = spawn(cmd, { cwd, shell: true, - stdio: ["ignore", "pipe", "pipe"], + stdio: interactive ? "inherit" : ["ignore", "pipe", "pipe"], }); const createChunkProcessor = () => { @@ -233,16 +234,36 @@ function App() { await stopApp("Galaxy", (line) => appendLog(3, line)); + const isWritable = (target: string) => { + try { + fs.accessSync(target, fs.constants.W_OK); + return true; + } catch { + return false; + } + }; + const useSudo = + !isWritable("/Applications") || + (fs.existsSync(destPath) && + (!isWritable(destPath) || fs.statSync(destPath).uid !== process.getuid?.())); + const privilege = useSudo ? "sudo " : ""; + if (fs.existsSync(destPath)) { appendLog(3, `Removing existing ${destPath}`); - execSync(`rm -rf "${destPath}"`, { stdio: "pipe" }); + await runCommandStreaming( + `${privilege}rm -rf "${destPath}"`, + WORKSPACE_ROOT, + (line) => appendLog(3, line), + useSudo + ); } appendLog(3, `Copying ${appPath} → ${destPath}`); await runCommandStreaming( - `ditto "${appPath}" "${destPath}"`, + `${privilege}ditto "${appPath}" "${destPath}"`, WORKSPACE_ROOT, - (line) => appendLog(3, line) + (line) => appendLog(3, line), + useSudo ); updateStep(3, { status: "done", detail: destPath }); From 4a75395c408790a30259392c43b21ecc0dd8dcae Mon Sep 17 00:00:00 2001 From: Ryan Ward Date: Wed, 30 Sep 2026 09:18:20 -0500 Subject: [PATCH 2/2] Updating make install script --- .../src/index.tsx | 46 +++++++++---------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/script/build-and-install-to-applications/src/index.tsx b/script/build-and-install-to-applications/src/index.tsx index a681fda8..0f30de30 100644 --- a/script/build-and-install-to-applications/src/index.tsx +++ b/script/build-and-install-to-applications/src/index.tsx @@ -195,11 +195,14 @@ function App() { // ─── Step 0: Check working directory ─────────────────────────── updateStep(0, { status: "running" }); - await runCommandStreaming( - `if [ -n "$(git status --porcelain)" ]; then git status --short; exit 1; fi`, - WORKSPACE_ROOT, - (line) => appendLog(0, line) - ); + const changes = execFileSync("git", ["status", "--short"], { + cwd: WORKSPACE_ROOT, + encoding: "utf8", + }).trimEnd(); + if (changes) { + for (const line of changes.split("\n")) appendLog(0, line); + throw new Error("Working directory has uncommitted changes. Commit or stash them before installing."); + } updateStep(0, { status: "done" }); // ─── Step 1: Pull current branch ──────────────────────────────── @@ -234,36 +237,28 @@ function App() { await stopApp("Galaxy", (line) => appendLog(3, line)); - const isWritable = (target: string) => { - try { - fs.accessSync(target, fs.constants.W_OK); - return true; - } catch { - return false; - } - }; - const useSudo = - !isWritable("/Applications") || - (fs.existsSync(destPath) && - (!isWritable(destPath) || fs.statSync(destPath).uid !== process.getuid?.())); - const privilege = useSudo ? "sudo " : ""; + appendLog(3, "Administrator password required to install in /Applications"); + await runCommandStreaming( + "sudo -k && sudo -v", + WORKSPACE_ROOT, + (line) => appendLog(3, line), + true + ); if (fs.existsSync(destPath)) { appendLog(3, `Removing existing ${destPath}`); await runCommandStreaming( - `${privilege}rm -rf "${destPath}"`, + `sudo -n rm -rf "${destPath}"`, WORKSPACE_ROOT, - (line) => appendLog(3, line), - useSudo + (line) => appendLog(3, line) ); } appendLog(3, `Copying ${appPath} → ${destPath}`); await runCommandStreaming( - `${privilege}ditto "${appPath}" "${destPath}"`, + `sudo -n ditto "${appPath}" "${destPath}"`, WORKSPACE_ROOT, - (line) => appendLog(3, line), - useSudo + (line) => appendLog(3, line) ); updateStep(3, { status: "done", detail: destPath }); @@ -273,6 +268,7 @@ function App() { execSync(`open "${destPath}"`, { stdio: "ignore" }); updateStep(4, { status: "done" }); } catch (err: any) { + process.exitCode = 1; setSteps((prev) => prev.map((s) => s.status === "running" @@ -284,8 +280,8 @@ function App() { })(); }, []); - const allDone = steps.every((s) => s.status === "done" || s.status === "error"); const hasError = steps.some((s) => s.status === "error"); + const allDone = hasError || steps.every((s) => s.status === "done"); return (