Skip to content

Commit 0c32990

Browse files
committed
chore: linux electron app update working
1 parent 4c5785b commit 0c32990

1 file changed

Lines changed: 155 additions & 22 deletions

File tree

src/extensionsIntegrated/appUpdater/update-electron.js

Lines changed: 155 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ define(function (require, exports, module) {
4646
KEY_UPDATE_AVAILABLE = "PH_UPDATE_AVAILABLE";
4747

4848
const PREFS_AUTO_UPDATE = "autoUpdate";
49+
const MAX_LOG_LINES = 500;
4950
let isAutoUpdateFlow = true;
5051
let updateScheduled = false;
5152
let cachedUpdateDetails = null;
@@ -146,7 +147,6 @@ define(function (require, exports, module) {
146147
*/
147148
async function isUpgradableLocation() {
148149
try {
149-
return true; //todo remove
150150
const isPackaged = await window.electronAPI.isPackaged();
151151
if (!isPackaged) {
152152
return false;
@@ -259,41 +259,171 @@ define(function (require, exports, module) {
259259
}
260260
}
261261

262-
async function launchLinuxUpdater() {
263-
const stageValue = Phoenix.config.environment;
264-
console.log('Stage:', stageValue);
265-
let execCommand = 'wget -qO- https://updates.phcode.io/linux/installer.sh | bash -s -- --upgrade';
266-
if(stageValue === 'dev' || stageValue === 'stage'){
267-
execCommand = "wget -qO- https://updates.phcode.io/linux/installer-latest-experimental-build.sh" +
268-
" | bash -s -- --upgrade";
269-
}
270-
const result = await window.electronAPI.runShellCommand(execCommand);
271-
if(result.code !== 0){
272-
throw new Error("Update script exit with non-0 exit code: " + result.code);
273-
}
262+
/**
263+
* Launches the Linux updater using spawnProcess with streaming output
264+
* @param {function} onOutput - Callback for stdout/stderr lines
265+
* @returns {Promise} Resolves when update completes, rejects on error
266+
*/
267+
function launchLinuxUpdater(onOutput) {
268+
return new Promise((resolve, reject) => {
269+
const stageValue = Phoenix.config.environment;
270+
console.log('Stage:', stageValue);
271+
let scriptUrl = 'https://updates.phcode.io/linux/installer.sh';
272+
if(stageValue === 'dev' || stageValue === 'stage'){
273+
scriptUrl = "https://updates.phcode.io/linux/installer-latest-experimental-build.sh";
274+
}
275+
276+
// Use spawnProcess to run bash with the wget|bash command
277+
const command = '/bin/bash';
278+
const args = ['-c', `wget -qO- ${scriptUrl} | bash -s -- --upgrade`];
279+
280+
window.electronAppAPI.spawnProcess(command, args)
281+
.then(instanceId => {
282+
// Set up output handlers
283+
window.electronAppAPI.onProcessStdout((id, line) => {
284+
if (id === instanceId && onOutput) {
285+
onOutput('stdout', line);
286+
}
287+
});
288+
window.electronAppAPI.onProcessStderr((id, line) => {
289+
if (id === instanceId && onOutput) {
290+
onOutput('stderr', line);
291+
}
292+
});
293+
window.electronAppAPI.onProcessClose((id, data) => {
294+
if (id === instanceId) {
295+
if (data.code === 0) {
296+
resolve();
297+
} else {
298+
reject(new Error(`Update script exited with code: ${data.code}`));
299+
}
300+
}
301+
});
302+
window.electronAppAPI.onProcessError((id, err) => {
303+
if (id === instanceId) {
304+
reject(new Error(`Update process error: ${err}`));
305+
}
306+
});
307+
})
308+
.catch(reject);
309+
});
274310
}
275311

276312
async function quitTimeAppUpdateHandler() {
277313
if(!updateScheduled){
278314
return;
279315
}
280316
console.log("Installing update at quit time");
281-
return new Promise(resolve=>{
317+
return new Promise(resolve => {
282318
let dialog;
319+
let logLines = [];
320+
321+
function appendLogLine(text) {
322+
// Split text into lines and add each
323+
const lines = text.split('\n').filter(l => l.trim());
324+
for (const line of lines) {
325+
logLines.push(line);
326+
// Keep only last MAX_LOG_LINES
327+
if (logLines.length > MAX_LOG_LINES) {
328+
logLines.shift();
329+
}
330+
}
331+
// Update the log display
332+
const logElement = document.getElementById('update-log-output');
333+
if (logElement) {
334+
logElement.textContent = logLines.join('\n');
335+
logElement.scrollTop = logElement.scrollHeight;
336+
}
337+
}
338+
283339
function failUpdateDialogAndExit(err) {
284340
console.error("error updating: ", err);
285341
dialog && dialog.close();
286-
Dialogs.showInfoDialog(Strings.UPDATE_FAILED_TITLE, Strings.UPDATE_FAILED_VISIT_SITE_MESSAGE)
287-
.done(()=>{
288-
NativeApp.openURLInDefaultBrowser(Phoenix.config.update_download_page)
289-
.catch(console.error)
290-
.finally(resolve);
291-
});
342+
// Build full log text for copying
343+
const fullLogText = logLines.join('\n') + '\n\nError: ' + (err.message || err);
344+
// Show failure dialog with log output and hover copy icon
345+
const failContent = `
346+
<p>${Strings.UPDATE_FAILED_VISIT_SITE_MESSAGE}</p>
347+
<div id="update-fail-log-container" style="
348+
position: relative;
349+
margin-top: 10px;
350+
">
351+
<pre id="update-fail-log" style="
352+
background: #1e1e1e;
353+
color: #d4d4d4;
354+
padding: 10px;
355+
border-radius: 4px;
356+
font-family: 'Consolas', 'Monaco', monospace;
357+
font-size: 11px;
358+
height: 200px;
359+
overflow-y: auto;
360+
white-space: pre-wrap;
361+
word-wrap: break-word;
362+
margin: 0;
363+
">${fullLogText}</pre>
364+
<i id="update-log-copy-btn" class="fa-solid fa-copy" title="${Strings.CMD_COPY}" style="
365+
position: absolute;
366+
top: 8px;
367+
right: 8px;
368+
color: #888;
369+
cursor: pointer;
370+
padding: 5px;
371+
border-radius: 3px;
372+
opacity: 0;
373+
transition: opacity 0.2s;
374+
"></i>
375+
</div>
376+
`;
377+
const failDialog = Dialogs.showModalDialog(
378+
DefaultDialogs.DIALOG_ID_ERROR,
379+
Strings.UPDATE_FAILED_TITLE,
380+
failContent,
381+
[{ className: Dialogs.DIALOG_BTN_CLASS_PRIMARY, id: Dialogs.DIALOG_BTN_OK, text: Strings.OK }]
382+
);
383+
// Set up hover and click handlers for copy icon
384+
const $container = $('#update-fail-log-container');
385+
const $copyBtn = $('#update-log-copy-btn');
386+
$container.on('mouseenter', () => $copyBtn.css('opacity', '1'));
387+
$container.on('mouseleave', () => $copyBtn.css('opacity', '0'));
388+
$copyBtn.on('click', () => {
389+
Phoenix.app.copyToClipboard(fullLogText);
390+
$copyBtn.removeClass('fa-copy').addClass('fa-check');
391+
setTimeout(() => {
392+
$copyBtn.removeClass('fa-check').addClass('fa-copy');
393+
}, 1500);
394+
});
395+
$copyBtn.on('mouseenter', () => $copyBtn.css({ 'background': '#333', 'color': '#fff' }));
396+
$copyBtn.on('mouseleave', () => $copyBtn.css({ 'background': 'transparent', 'color': '#888' }));
397+
398+
failDialog.done(() => {
399+
NativeApp.openURLInDefaultBrowser(Phoenix.config.update_download_page)
400+
.catch(console.error)
401+
.finally(resolve);
402+
});
292403
}
404+
405+
// Create dialog with terminal-style log output
406+
const dialogContent = `
407+
<p>${Strings.UPDATE_INSTALLING_MESSAGE}</p>
408+
<pre id="update-log-output" style="
409+
background: #1e1e1e;
410+
color: #d4d4d4;
411+
padding: 10px;
412+
border-radius: 4px;
413+
font-family: 'Consolas', 'Monaco', monospace;
414+
font-size: 11px;
415+
height: 200px;
416+
overflow-y: auto;
417+
white-space: pre-wrap;
418+
word-wrap: break-word;
419+
margin-top: 10px;
420+
"></pre>
421+
`;
422+
293423
dialog = Dialogs.showModalDialog(
294424
DefaultDialogs.DIALOG_ID_INFO,
295425
Strings.UPDATE_INSTALLING,
296-
Strings.UPDATE_INSTALLING_MESSAGE,
426+
dialogContent,
297427
[
298428
{
299429
className: "forced-hidden",
@@ -303,7 +433,10 @@ define(function (require, exports, module) {
303433
],
304434
false
305435
);
306-
launchLinuxUpdater()
436+
437+
launchLinuxUpdater((type, text) => {
438+
appendLogLine(text);
439+
})
307440
.then(resolve)
308441
.catch(failUpdateDialogAndExit);
309442
});

0 commit comments

Comments
 (0)