From 8f8aac9cd30e889973472d779aa493dcc7453972 Mon Sep 17 00:00:00 2001 From: Bruce MacDonald Date: Thu, 5 Dec 2024 16:17:35 -0800 Subject: [PATCH] macapp: add error handling for symlink operations Refactor the installer to do a better job handling errors when creating symlinks. It checks if paths are valid before trying to use them, safely creates directories if they don't exist, and clearly tells you what went wrong if something fails. It also uses TypeScript to catch mistakes early and makes sure paths work correctly in all cases. --- macapp/src/install.ts | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/macapp/src/install.ts b/macapp/src/install.ts index d8036542b..07a09dc06 100644 --- a/macapp/src/install.ts +++ b/macapp/src/install.ts @@ -1,21 +1,35 @@ import * as fs from 'fs' -import { exec as cbExec } from 'child_process' +import { spawn } from 'child_process' import * as path from 'path' -import { promisify } from 'util' const app = process && process.type === 'renderer' ? require('@electron/remote').app : require('electron').app const ollama = app.isPackaged ? path.join(process.resourcesPath, 'ollama') : path.resolve(process.cwd(), '..', 'ollama') -const exec = promisify(cbExec) const symlinkPath = '/usr/local/bin/ollama' -export function installed() { +export function installed(): boolean { return fs.existsSync(symlinkPath) && fs.readlinkSync(symlinkPath) === ollama } -export async function install() { - const command = `do shell script "mkdir -p ${path.dirname( - symlinkPath - )} && ln -F -s \\"${ollama}\\" \\"${symlinkPath}\\"" with administrator privileges` - - await exec(`osascript -e '${command}'`) +function validPath(targetPath: string): boolean { + const normalized = path.normalize(targetPath) + return !(/[;&|`$(){}[\]<>]/.test(normalized) || normalized.includes('..')) +} + +export async function install(): Promise { + if (!validPath(ollama) || !validPath(symlinkPath)) { + throw new Error('Invalid path format') + } + + await fs.promises.mkdir(path.dirname(symlinkPath), { recursive: true }) + .catch(err => err.code === 'EEXIST' ? null : Promise.reject(err)) + + const process = spawn('osascript', [ + '-e', + `do shell script "ln -F -s '${path.normalize(ollama)}' '${path.normalize(symlinkPath)}'" with administrator privileges` + ]) + + await new Promise((resolve, reject) => { + process.on('error', reject) + process.on('close', code => code === 0 ? resolve() : reject(new Error(`Failed with code ${code}`))) + }) }