Skip to content

Commit

Permalink
refactor: AutoUpdater for Windows using async/await (electron#40289)
Browse files Browse the repository at this point in the history
  • Loading branch information
miniak authored and MrHuangJser committed Dec 11, 2023
1 parent aa61aff commit 1282b9d
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 73 deletions.
29 changes: 13 additions & 16 deletions lib/browser/api/auto-updater/auto-updater-win.ts
Expand Up @@ -34,7 +34,7 @@ class AutoUpdater extends EventEmitter implements Electron.AutoUpdater {
this.updateURL = updateURL;
}

checkForUpdates () {
async checkForUpdates () {
const url = this.updateURL;
if (!url) {
return this.emitError(new Error('Update URL is not set'));
Expand All @@ -43,27 +43,24 @@ class AutoUpdater extends EventEmitter implements Electron.AutoUpdater {
return this.emitError(new Error('Can not find Squirrel'));
}
this.emit('checking-for-update');
squirrelUpdate.checkForUpdate(url, (error, update) => {
if (error != null) {
return this.emitError(error);
}
try {
const update = await squirrelUpdate.checkForUpdate(url);
if (update == null) {
return this.emit('update-not-available');
}
this.updateAvailable = true;
this.emit('update-available');
squirrelUpdate.update(url, (error) => {
if (error != null) {
return this.emitError(error);
}
const { releaseNotes, version } = update;
// Date is not available on Windows, so fake it.
const date = new Date();
this.emit('update-downloaded', {}, releaseNotes, version, date, this.updateURL, () => {
this.quitAndInstall();
});

await squirrelUpdate.update(url);
const { releaseNotes, version } = update;
// Date is not available on Windows, so fake it.
const date = new Date();
this.emit('update-downloaded', {}, releaseNotes, version, date, this.updateURL, () => {
this.quitAndInstall();
});
});
} catch (error) {
this.emitError(error as Error);
}
}

// Private: Emit both error object and message, this is to keep compatibility
Expand Down
92 changes: 35 additions & 57 deletions lib/browser/api/auto-updater/squirrel-update-win.ts
Expand Up @@ -15,89 +15,67 @@ const isSameArgs = (args: string[]) => args.length === spawnedArgs.length && arg

// Spawn a command and invoke the callback when it completes with an error
// and the output from standard out.
const spawnUpdate = function (args: string[], detached: boolean, callback: Function) {
let error: Error, errorEmitted: boolean, stderr: string, stdout: string;

try {
const spawnUpdate = async function (args: string[], options: { detached: boolean }): Promise<string> {
return new Promise((resolve, reject) => {
// Ensure we don't spawn multiple squirrel processes
// Process spawned, same args: Attach events to already running process
// Process spawned, different args: Return with error
// No process spawned: Spawn new process
if (spawnedProcess && !isSameArgs(args)) {
return callback(`AutoUpdater process with arguments ${args} is already running`);
throw new Error(`AutoUpdater process with arguments ${args} is already running`);
} else if (!spawnedProcess) {
spawnedProcess = spawn(updateExe, args, {
detached: detached,
detached: options.detached,
windowsHide: true
});
spawnedArgs = args || [];
}
} catch (error1) {
error = error1 as Error;

// Shouldn't happen, but still guard it.
process.nextTick(function () {
return callback(error);
});
return;
}
stdout = '';
stderr = '';

spawnedProcess.stdout.on('data', (data) => { stdout += data; });
spawnedProcess.stderr.on('data', (data) => { stderr += data; });
let stdout = '';
let stderr = '';

errorEmitted = false;
spawnedProcess.on('error', (error) => {
errorEmitted = true;
callback(error);
});

return spawnedProcess.on('exit', function (code, signal) {
spawnedProcess = undefined;
spawnedArgs = [];

// We may have already emitted an error.
if (errorEmitted) {
return;
}
spawnedProcess.stdout.on('data', (data) => { stdout += data; });
spawnedProcess.stderr.on('data', (data) => { stderr += data; });

// Process terminated with error.
if (code !== 0) {
return callback(`Command failed: ${signal ?? code}\n${stderr}`);
}
spawnedProcess.on('error', (error) => {
reject(error);
});

// Success.
callback(null, stdout);
spawnedProcess.on('exit', function (code, signal) {
spawnedProcess = undefined;
spawnedArgs = [];

if (code !== 0) {
// Process terminated with error.
reject(new Error(`Command failed: ${signal ?? code}\n${stderr}`));
} else {
// Success.
resolve(stdout);
}
});
});
};

// Start an instance of the installed app.
export function processStart () {
return spawnUpdate(['--processStartAndWait', exeName], true, function () {});
spawnUpdate(['--processStartAndWait', exeName], { detached: true });
}

// Download the releases specified by the URL and write new results to stdout.
export function checkForUpdate (updateURL: string, callback: (error: Error | null, update?: any) => void) {
return spawnUpdate(['--checkForUpdate', updateURL], false, function (error: Error, stdout: string) {
let ref, ref1, update;
if (error != null) {
return callback(error);
}
try {
// Last line of output is the JSON details about the releases
const json = stdout.trim().split('\n').pop();
update = (ref = JSON.parse(json!)) != null ? (ref1 = ref.releasesToApply) != null ? typeof ref1.pop === 'function' ? ref1.pop() : undefined : undefined : undefined;
} catch {
return callback(new Error(`Invalid result:\n${stdout}`));
}
return callback(null, update);
});
export async function checkForUpdate (updateURL: string): Promise<any> {
const stdout = await spawnUpdate(['--checkForUpdate', updateURL], { detached: false });
try {
// Last line of output is the JSON details about the releases
const json = stdout.trim().split('\n').pop();
return JSON.parse(json!)?.releasesToApply?.pop?.();
} catch {
throw new Error(`Invalid result:\n${stdout}`);
}
}

// Update the application to the latest remote version specified by URL.
export function update (updateURL: string, callback: (error: Error) => void) {
return spawnUpdate(['--update', updateURL], false, callback);
export async function update (updateURL: string): Promise<void> {
await spawnUpdate(['--update', updateURL], { detached: false });
}

// Is the Update.exe installed with the current application?
Expand Down

0 comments on commit 1282b9d

Please sign in to comment.