Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions docs/generatedApiDocs/features/TaskManager-API.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ TaskManager module deals with managing long running tasks in phcode. It handles
bar where the user can see all running tasks, monitor its progress and close/pause the execution of the task if
supported by the task.

## renderSpinnerIcon

determines what the spinner icon to show(green-for success), red-fail, blue normal based on the active
tasks in list and renders. IF the active tasks has already been notified, it wont notify again.

### Parameters

* `newTaskAdded`

## TaskObject

Type: [Object][1]
Expand All @@ -21,8 +30,8 @@ Type: [Object][1]
* `getProgressPercent` **function (): [number][3]** Returns the task's current progress percentage.
* `setFailed` **function (): void** Marks the task as failed.
* `isFailed` **function (): [boolean][4]** Returns true if the task is marked as failed.
* `setSucceeded` **function (): void** Marks the task as succeeded.
* `isSucceeded` **function (): [boolean][4]** Returns true if the task is marked as succeeded.
* `setSucceded` **function (): void** Marks the task as succeeded.
* `isSucceded` **function (): [boolean][4]** Returns true if the task is marked as succeeded.
* `showStopIcon` **function ([string][2]): void** Shows the stop icon with an optional tooltip message.
* `hideStopIcon` **function (): void** Hides the stop icon.
* `showPlayIcon` **function ([string][2]): void** Shows the play icon with an optional tooltip message.
Expand Down
93 changes: 82 additions & 11 deletions src-node/download-file.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ const { pipeline } = require('stream/promises');
const { Transform } = require('stream');
const fs = require('fs');
const path = require('path');
const os = require('os');
const { exec } = require('child_process');

const args = process.argv.slice(2); // Skip the first two elements
const {downloadURL, appdataDir} = JSON.parse(args[0]);
Expand All @@ -14,6 +16,7 @@ const EVENT_INSTALL_PATH= "InstallerPath:";
const fileName = path.basename(new URL(downloadURL).pathname);
const installerFolder = path.join(appdataDir, 'installer');
const savePath = path.join(appdataDir, 'installer', fileName);
let extractPath;

async function getFileSize(url) {
try {
Expand Down Expand Up @@ -73,6 +76,60 @@ async function downloadFile(url, outputPath) {
console.log(`File has been downloaded and saved to ${outputPath}`);
}

/**
* Extracts a .tar.gz file using the tar CLI utility available on macOS/linux.
*
* @param {string} filePath - The path to the .tar.gz file.
* @param {string} absoluteExtractPath - The directory to extract the files into.
*/
function extractTar(filePath, absoluteExtractPath) {
return new Promise((resolve, reject)=>{
const command = `tar -xzf "${filePath}" -C "${absoluteExtractPath}"`;

exec(command, (error, stdout, stderr) => {
if (error) {
console.error(`Extraction error: ${error.message}`);
reject(error.message);
return;
}
if (stderr) {
console.error(`Extraction stderr: ${stderr}`);
reject(stderr);
return;
}
console.log(`Extraction completed to ${absoluteExtractPath}`);
resolve();
});
});
}

function removeQuarantineAttributeIfMac(extractPath) {
return new Promise((resolve)=>{
if (os.platform() === 'darwin') {
const command = `xattr -rd com.apple.quarantine "${extractPath}"`;

exec(command, (error, stdout, stderr) => {
// we always resolve as the user will be promted by macos if this fails here.
if (error) {
console.error(`Error removing quarantine attribute: ${error.message}`);
resolve();
return;
}
if (stderr) {
console.error(`Error output: ${stderr}`);
resolve();
return;
}
console.log(`Quarantine attribute removed successfully for ${extractPath}`);
resolve();
});
} else {
console.log("Platform is not macOS, no need to remove quarantine attribute.");
resolve();
}
});
}

async function downloadFileIfNeeded() {
try {

Expand All @@ -88,24 +145,38 @@ async function downloadFileIfNeeded() {
console.log('File already downloaded and complete.');
const totalSize = Math.floor(totalBytes/1024/1024);
console.log(`${EVENT_PROGRESS}${100}:${totalSize}`);
return;
} else {
// if we are here, then it is a fresh installer download or there is a partial corrupt download or
// a new version installer has to be downloaded while the old outdated installer exists.
// we have to clean the installerFolder.
await fs.promises.rm(installerFolder, { recursive: true, force: true });
fs.mkdirSync(installerFolder, { recursive: true });
console.log(`Downloading installer to ${savePath}...`);
await downloadFile(downloadURL, savePath);
}

// if we are here, then it is a fresh installer download or there is a partial corrupt download or
// a new version installer has to be downloaded while the old outdated installer exists.
// we have to clean the installerFolder.
await fs.promises.rm(installerFolder, { recursive: true, force: true });
fs.mkdirSync(installerFolder, { recursive: true });
console.log('Downloading installer...');
await downloadFile(downloadURL, savePath);

extractPath = path.join(appdataDir, 'installer', "extracted");
await fs.promises.rm(extractPath, { recursive: true, force: true });
fs.mkdirSync(extractPath, { recursive: true });
if(savePath.endsWith(".tar.gz")){
await extractTar(savePath, extractPath);
}
const dirContents = fs.readdirSync(extractPath);
console.log("extracted dir contents: ", dirContents);
if(dirContents.length === 1){
extractPath = path.join(extractPath, dirContents[0]);
}
await removeQuarantineAttributeIfMac(extractPath);
} catch (error) {
console.error('An error occurred:', error);
}
}

downloadFileIfNeeded()
.then(()=>{
console.log(`${EVENT_INSTALL_PATH}${savePath}`); // do not change this name
if(extractPath){
console.log(`${EVENT_INSTALL_PATH}${extractPath}`);
return;
}
console.log(`${EVENT_INSTALL_PATH}${savePath}`);
})
.catch(()=>process.exit(1));
17 changes: 13 additions & 4 deletions src/extensionsIntegrated/appUpdater/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ define(function (require, exports, module) {
updatePlatform: updatePlatformKey
};
try{
if(!updaterWindow){
updaterWindow = window.__TAURI__.window.WebviewWindow.getByLabel(TAURI_UPDATER_WINDOW_LABEL);
}
const updateMetadata = await fetchJSON(brackets.config.app_update_url);
const phoenixBinaryVersion = await NodeUtils.getPhoenixBinaryVersion();
const phoenixLoadedAppVersion = Phoenix.metadata.apiVersion;
Expand All @@ -150,7 +153,11 @@ define(function (require, exports, module) {
updateDetails.downloadURL = updateMetadata.platforms[updatePlatformKey].url;
}
} else if(semver.eq(updateMetadata.version, phoenixBinaryVersion) &&
!semver.eq(phoenixLoadedAppVersion, phoenixBinaryVersion)){
!semver.eq(phoenixLoadedAppVersion, phoenixBinaryVersion) && updaterWindow){
// the updaterWindow check is here so that it only makes sense to show restart dialog if the update
// was actually done. We have a version number mismatch of 0.0.1 between phoenix-desktop and phoenix
// repo, and that means that this can get triggered on statup on development builds. Wont happen in
// actual pipeline generated build tho.
console.log("Updates applied, waiting for app restart: ", phoenixBinaryVersion, phoenixLoadedAppVersion);
updateDetails.updatePendingRestart = true;
PreferencesManager.setViewState(KEY_UPDATE_AVAILABLE, true);
Expand Down Expand Up @@ -296,14 +303,16 @@ define(function (require, exports, module) {
updateTask.setTitle(Strings.UPDATE_DONE);
updateTask.setMessage(Strings.UPDATE_RESTART);
Dialogs.showInfoDialog(Strings.UPDATE_READY_RESTART_TITLE, Strings.UPDATE_READY_RESTART_MESSAGE);
} else if(data === UPDATE_STATUS.INSTALLER_DOWNLOADED && !updateInstalledDialogShown){
updateInstalledDialogShown = true;
} else if(data === UPDATE_STATUS.INSTALLER_DOWNLOADED){
Metrics.countEvent(Metrics.EVENT_TYPE.UPDATES, 'downloaded', Phoenix.platform);
updatePendingRestart = true;
updateTask.setSucceded();
updateTask.setTitle(Strings.UPDATE_DONE);
updateTask.setMessage(Strings.UPDATE_RESTART_INSTALL);
Dialogs.showInfoDialog(Strings.UPDATE_READY_RESTART_TITLE, Strings.UPDATE_READY_RESTART_INSTALL_MESSAGE);
if(!updateInstalledDialogShown){
Dialogs.showInfoDialog(Strings.UPDATE_READY_RESTART_TITLE, Strings.UPDATE_READY_RESTART_INSTALL_MESSAGE);
updateInstalledDialogShown = true;
}
_sendUpdateCommand(UPDATE_COMMANDS.GET_INSTALLER_LOCATION);
} else if(data === UPDATE_STATUS.DOWNLOADING){
updateTask.setMessage(Strings.UPDATE_DOWNLOADING);
Expand Down
22 changes: 4 additions & 18 deletions src/tauri-updater.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,6 @@
}
sendUpdateEvent(UPDATE_EVENT.STATUS, status);

async function updateMacOS() {
try {
await __TAURI__.updater.installUpdate();
status = UPDATE_STATUS.INSTALLED;
sendUpdateEvent(UPDATE_EVENT.STATUS, UPDATE_STATUS.INSTALLED);
} catch (e) {
status = UPDATE_STATUS.FAILED;
sendUpdateEvent(UPDATE_EVENT.LOG_ERROR, e.stack);
sendUpdateEvent(UPDATE_EVENT.STATUS, UPDATE_STATUS.FAILED);
}
}

function getQueryStringParam(paramName) {
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
Expand Down Expand Up @@ -86,7 +74,7 @@
}
}

async function updateWindows() {
async function downloadUpdate() {
const EVENT_PROGRESS= "progress:";
const EVENT_INSTALL_PATH= "InstallerPath:";
const downloadURL = decodeURIComponent(getQueryStringParam('downloadURL'));
Expand Down Expand Up @@ -134,12 +122,10 @@

async function update() {
const platform = await __TAURI__.os.platform();
if(platform === 'darwin'){
await updateMacOS();
} else if(platform === 'linux'){
if(platform === 'linux'){
await updateLinux();
} else if(platform === 'win32'){
await updateWindows();
} else if(platform === 'win32' || platform === 'darwin'){
await downloadUpdate();
} else {
status = UPDATE_STATUS.FAILED_UNKNOWN_OS;
sendUpdateEvent(UPDATE_EVENT.STATUS, UPDATE_STATUS.FAILED_UNKNOWN_OS);
Expand Down