From bd875d1aeb0363fd5016141eeec1e1ac512d5e9c Mon Sep 17 00:00:00 2001 From: a7m-1st Date: Thu, 18 Sep 2025 21:14:30 +0300 Subject: [PATCH] refactor: use class to organize install --- electron/main/install-deps.ts | 240 +++++++++++++++++++--------------- 1 file changed, 135 insertions(+), 105 deletions(-) diff --git a/electron/main/install-deps.ts b/electron/main/install-deps.ts index 5797cc366..3875da7e4 100644 --- a/electron/main/install-deps.ts +++ b/electron/main/install-deps.ts @@ -4,6 +4,7 @@ import log from 'electron-log' import { getMainWindow } from './init' import fs from 'node:fs' import { getBackendPath, getBinaryPath, getCachePath, isBinaryExists, runInstallScript } from './utils/process' +import { spawn } from 'child_process' const userData = app.getPath('userData'); const versionFile = path.join(userData, 'version.txt'); @@ -76,7 +77,7 @@ export async function checkAndInstallDepsOnUpdate(win:BrowserWindow): Promise { - const ensureInstalled = async (toolName: 'uv' | 'bun', scriptName: string): Promise => { + const ensureInstalled = async (toolName: 'uv' | 'bun', scriptName: string): Promise => { if (await isBinaryExists(toolName)) { return true; } @@ -115,120 +116,149 @@ export async function installCommandTool() { }) } -export async function installDependencies() { - const { spawn } = await import('child_process'); - return new Promise(async (resolve, reject) => { - console.log('start install dependencies') +let uv_path:string; +const mainWindow = getMainWindow(); +const backendPath = getBackendPath(); +const installingLockPath = path.join(backendPath, 'uv_installing.lock') +const installedLockPath = path.join(backendPath, 'uv_installed.lock') +// const proxyArgs = ['--default-index', 'https://pypi.tuna.tsinghua.edu.cn/simple'] +const proxyArgs = ['--default-index', 'https://mirrors.aliyun.com/pypi/simple/'] - const mainWindow = getMainWindow(); - if (mainWindow && !mainWindow.isDestroyed()) { - mainWindow.webContents.send('install-dependencies-start'); +class InstallLogs { + private node_process; + + constructor(extraArgs:string[]) { + console.log('start install dependencies', extraArgs) + this.node_process = spawn(uv_path, [ + 'sync', + '--no-dev', + '--cache-dir', getCachePath('uv_cache'), + ...extraArgs], { + cwd: backendPath, + env: { + ...process.env, + UV_TOOL_DIR: getCachePath('uv_tool'), + UV_PYTHON_INSTALL_DIR: getCachePath('uv_python'), + } + }) + } + + /**Handle stdout data */ + onStdout() { + this.node_process.stdout.on('data', (data:any) => { + log.info(`Script output: ${data}`) + if (mainWindow && !mainWindow.isDestroyed()) { + mainWindow.webContents.send('install-dependencies-log', { type: 'stdout', data: data.toString() }); + } + }) } - const isInstalCommandTool = await installCommandTool() - if (!isInstalCommandTool) { - resolve(false) - return + /**Handle stderr data */ + onStderr() { + this.node_process.stderr.on('data', (data:any) => { + log.error(`Script error: ${data}`) + if (mainWindow && !mainWindow.isDestroyed()) { + mainWindow.webContents.send('install-dependencies-log', { type: 'stderr', data: data.toString() }); + } + }) } - const uv_path = await getBinaryPath('uv') - const backendPath = getBackendPath() - - if (!fs.existsSync(backendPath)) { - fs.mkdirSync(backendPath, { recursive: true }) + /**Handle process close event */ + onClose(resolveInner:(code: number | null) => void) { + this.node_process.on('close', resolveInner); } - const installingLockPath = path.join(backendPath, 'uv_installing.lock') - fs.writeFileSync(installingLockPath, '') - - const installedLockPath = path.join(backendPath, 'uv_installed.lock') - // const proxyArgs = ['--default-index', 'https://pypi.tuna.tsinghua.edu.cn/simple'] - const proxyArgs = ['--default-index', 'https://mirrors.aliyun.com/pypi/simple/'] - const runInstall = (extraArgs: string[]) => { - return new Promise((resolveInner, rejectInner) => { - try { - const node_process = spawn(uv_path, [ - 'sync', - '--no-dev', - '--cache-dir', getCachePath('uv_cache'), - ...extraArgs], { - cwd: backendPath, - env: { - ...process.env, - UV_TOOL_DIR: getCachePath('uv_tool'), - UV_PYTHON_INSTALL_DIR: getCachePath('uv_python'), - } - }) - console.log('start install dependencies', extraArgs) - node_process.stdout.on('data', (data) => { - - log.info(`Script output: ${data}`) - if (mainWindow && !mainWindow.isDestroyed()) { - mainWindow.webContents.send('install-dependencies-log', { type: 'stdout', data: data.toString() }); - } - }) - - node_process.stderr.on('data', (data) => { - log.error(`Script error: ${data}`) - if (mainWindow && !mainWindow.isDestroyed()) { - mainWindow.webContents.send('install-dependencies-log', { type: 'stderr', data: data.toString() }); - } - }) - - node_process.on('close', (code) => { - console.log('install dependencies end', code === 0) - resolveInner(code === 0) - }) - }catch(err) { - log.error('run install failed', err) - // Clean up uv_installing.lock file if installation fails - if (fs.existsSync(installingLockPath)) { - fs.unlinkSync(installingLockPath); - } - rejectInner(err) - } - - }) + /** + * Set installing Lock Path + * Creates uv_installing.lock file to indicate installation in progress + * Creates backend directory if not exists + */ + static setLockPath() { + if (!fs.existsSync(backendPath)) { + fs.mkdirSync(backendPath, { recursive: true }) + } + fs.writeFileSync(installingLockPath, '') } - // try default install - const installSuccess = await runInstall([]) - - if (installSuccess) { - fs.unlinkSync(installingLockPath) - fs.writeFileSync(installedLockPath, '') - log.info('Script completed successfully') - console.log('end install dependencies') - spawn(uv_path, ['run', 'task', 'babel'], { cwd: backendPath }) - resolve(true) - return + /**Clean installing Lock Path */ + static cleanLockPath() { + if (fs.existsSync(installingLockPath)) { + fs.unlinkSync(installingLockPath); + } } +} - // try mirror install - const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone - let mirrorInstallSuccess = false - - if (timezone === 'Asia/Shanghai') { - mirrorInstallSuccess = await runInstall(proxyArgs) - } else { - mirrorInstallSuccess = await runInstall([]) - } - - - fs.existsSync(installingLockPath) && fs.unlinkSync(installingLockPath) - - if (mirrorInstallSuccess) { - fs.writeFileSync(installedLockPath, '') - log.info('Mirror script completed successfully') - console.log('end install dependencies (mirror)') - spawn(uv_path, ['run', 'task', 'babel'], { cwd: backendPath }) - resolve(true) - } else { - log.error('Both default and mirror install failed') - if (mainWindow && !mainWindow.isDestroyed()) { - mainWindow.webContents.send('install-dependencies-complete', { success: false, error: 'Both default and mirror install failed' }); - } - resolve(false) - } +const runInstall = (extraArgs: string[]) => { + const installLogs = new InstallLogs(extraArgs); + return new Promise((resolveInner, rejectInner) => { + try { + installLogs.onStdout(); + installLogs.onStderr(); + installLogs.onClose((code) => { + console.log('install dependencies end', code === 0) + InstallLogs.cleanLockPath() + resolveInner(code === 0) + }) + } catch (err) { + log.error('run install failed', err) + // Clean up uv_installing.lock file if installation fails + InstallLogs.cleanLockPath(); + rejectInner(err) + } + }) +} + +export async function installDependencies() { + uv_path = await getBinaryPath('uv'); + const handleCompletion = { + spawnBabel: (type:"mirror"|"main"="main") => { + fs.writeFileSync(installedLockPath, '') + log.info('Script completed successfully') + console.log(`Install Dependencies completed ${type}`) + spawn(uv_path, ['run', 'task', 'babel'], { cwd: backendPath }) + } + } + + return new Promise(async (resolve, reject) => { + console.log('start install dependencies') + if (mainWindow && !mainWindow.isDestroyed()) { + mainWindow.webContents.send('install-dependencies-start'); + } else { + resolve(false) + return + } + + const isInstalCommandTool = await installCommandTool() + if (!isInstalCommandTool) { + resolve(false) + return + } + + // Set Installing Lock Files + InstallLogs.setLockPath(); + + // try default install + const installSuccess = await runInstall([]) + if (installSuccess) { + handleCompletion.spawnBabel() + resolve(true) + return + } + + // try mirror install + const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone + let mirrorInstallSuccess = false + mirrorInstallSuccess = (timezone === 'Asia/Shanghai')? await runInstall(proxyArgs) :await runInstall([]) + + if (mirrorInstallSuccess) { + handleCompletion.spawnBabel("mirror") + resolve(true) + } else { + log.error('Both default and mirror install failed') + if (mainWindow && !mainWindow.isDestroyed()) { + mainWindow.webContents.send('install-dependencies-complete', { success: false, error: 'Both default and mirror install failed' }); + } + resolve(false) + } }) }