Addon folder name should start with "ofx" (got "${name}").`); + event.sender.send('cloneAddonDone'); + return; + } + + const destDir = path.join(addonsDir, name); + if (fs.existsSync(destDir)) { + event.sender.send('sendUIMessage', `\nError...
${name} already exists in your addons folder.`); + event.sender.send('cloneAddonDone'); + return; + } + + event.sender.send('consoleMessage', `
Cloning ${url} into ${destDir}...
`); + + const child = spawn('git', ['clone', '--recursive', url, destDir], { cwd: addonsDir, windowsHide: true }); + let stderr = ''; + child.stdout.on('data', (chunk) => event.sender.send('consoleMessage', chunk.toString())); + child.stderr.on('data', (chunk) => { + stderr += chunk.toString(); + event.sender.send('consoleMessage', chunk.toString()); + }); + + child.on('error', (error) => { + event.sender.send('sendUIMessage', `\nError...
Could not run git. Make sure git is installed and on PATH.
${error.message}`); + event.sender.send('cloneAddonDone'); + }); + + child.on('close', (code) => { + if (code !== 0) { + event.sender.send('sendUIMessage', `\nError...
git clone failed (exit code ${code}).
Added addon ${name}.`); + event.sender.send('cloneAddonDone'); + }; + + if (ref) { + const checkout = spawn('git', ['checkout', ref], { cwd: destDir, windowsHide: true }); + checkout.stdout.on('data', (chunk) => event.sender.send('consoleMessage', chunk.toString())); + checkout.stderr.on('data', (chunk) => event.sender.send('consoleMessage', chunk.toString())); + checkout.on('close', (checkoutCode) => { + if (checkoutCode !== 0) { + event.sender.send('consoleMessage', `
Warning: cloned ${name} but could not check out "${ref}"
`); + } + finish(); + }); + } else { + finish(); + } + }); +}); + ipcMain.on('refreshTemplateList', (event, arg) => { console.log("refreshTemplateList"); const { selectedPlatforms, ofPath, bMulti } = arg; @@ -827,20 +882,18 @@ ipcMain.on('getRandomSketchName', (event, projectPath) => { // event.sender.send('setGenerateMode', 'createMode'); // it's a new sketch name, we are in create mode }); +// raw executable path, no quoting - always launched via execFile/spawn argv, never a shell function getPgPath() { let pgApp = ""; try { if (hostplatform == "linux" || hostplatform == "linux64") { // ???: when appear there linux64? pgApp = path.join(defaultOfPath, "apps/projectGenerator/commandLine/bin/projectGenerator"); - //pgApp = "projectGenerator"; } else { pgApp = path.normalize(path.join(__dirname, "app", "projectGenerator")); } - if (hostplatform == 'osx' || hostplatform == 'linux' || hostplatform == 'linux64') { - pgApp = pgApp.replace(/ /g, '\\ '); - } else { - pgApp = "\"" + pgApp + "\""; + if (hostplatform == "windows") { + pgApp += ".exe"; } } catch (error) { console.error("Error determining project generator path:", error); @@ -849,6 +902,55 @@ function getPgPath() { return pgApp; } +// runs PG via argv (no shell), streaming stdout/stderr to the console panel as it arrives +function runPG(args, event, callback) { + const pgApp = getPgPath(); + const displayCommand = [pgApp, ...args].join(' '); + + let child; + try { + child = spawn(pgApp, args, { windowsHide: true }); + } catch (error) { + callback(error, '', error.message); + return; + } + + let stdout = ''; + let stderr = ''; + + child.stdout.on('data', (chunk) => { + const text = chunk.toString(); + stdout += text; + if (event && event.sender && !event.sender.isDestroyed()) { + event.sender.send('consoleMessage', text); + } + }); + + child.stderr.on('data', (chunk) => { + const text = chunk.toString(); + stderr += text; + if (event && event.sender && !event.sender.isDestroyed()) { + event.sender.send('consoleMessage', text); + } + }); + + child.on('error', (error) => { + callback(error, stdout, stderr || error.message); + }); + + child.on('close', (code) => { + if (event && event.sender && !event.sender.isDestroyed()) { + event.sender.send('consoleMessage', `
(command used: ${displayCommand})
`); + } + if (code === 0) { + callback(null, stdout, stderr); + } else { + const error = new Error(stderr || `projectGenerator exited with code ${code}`); + callback(error, stdout, stderr); + } + }); +} + /** @typedef {{ * updatePath: string, @@ -866,13 +968,6 @@ function getPgPath() { function updateFunction(event, update) { console.log(update); - let updatePathString = ""; - let pathString = ""; - let platformString = ""; - let templateString = ""; - let recursiveString = ""; - let verboseString = ""; - const { updatePath, platformList, @@ -882,68 +977,51 @@ function updateFunction(event, update) { verbose } = update; - if (updatePath != null) { - updatePathString = `"${updatePath}"`; - } + const args = []; - if (platformList != null) { - platformString = `-p"${platformList.join(",")}"`; + if (updateRecursive == true) { + args.push('-r'); } - if (templateList != null) { - const uniqueTemplates = [...new Set(templateList)]; - templateString = `-t"${uniqueTemplates.join(",")}"`; + if (verbose == true) { + args.push('-v'); } if (ofPath != null) { - pathString = `-o"${ofPath}"`; + args.push(`-o${ofPath}`); } - if (updateRecursive == true) { - recursiveString = "-r"; + if (platformList != null) { + args.push(`-p${platformList.join(",")}`); } - if (verbose == true) { - verboseString = "-v"; + if (templateList != null) { + const uniqueTemplates = [...new Set(templateList)]; + args.push(`-t${uniqueTemplates.join(",")}`); } - const pgApp = getPgPath(); - - const wholeString = [ - pgApp, - recursiveString, - verboseString, - pathString, - platformString, - templateString, - updatePathString - ].join(" "); - - exec(wholeString, { maxBuffer : Infinity }, (error, stdout, stderr) => { + if (updatePath != null) { + args.push(updatePath); + } + + runPG(args, event, (error, stdout, stderr) => { if (error === null) { - event.sender.send('consoleMessage', "" + wholeString + "
" + stdout); event.sender.send('sendUIMessage', 'Success!
' + 'Updating your project was successful! ' + updatePath + '
' + '
' + - '
" + error.message); event.sender.send('sendUIMessage', 'Error...
' + 'There was a problem updating your project... ' + updatePath + '' + - '
${stdout}`); event.sender.send('sendUIMessage', 'Success!
' + 'Your can now find your project in ' + fullPath + '
' + '
' - + '
${error.message}`); // note: stderr mostly seems to be also included in error.message - // also available: error.code, error.killed, error.signal, error.cmd - // info: error.code=127 means commandLinePG was not found + // info: error.code=ENOENT means commandLinePG was not found event.sender.send('sendUIMessage', 'Error...
' + 'There was a problem generating your project... ' + fullPath + '' @@ -1058,18 +1112,15 @@ function generateFunction(event, generate) { + '