electron/lib/browser/api/auto-updater/squirrel-update-win.ts

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

92 lines
3 KiB
TypeScript
Raw Normal View History

2020-07-01 19:27:12 +00:00
import * as fs from 'fs';
import * as path from 'path';
import { spawn, ChildProcessWithoutNullStreams } from 'child_process';
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// i.e. my-app/app-0.1.13/
const appFolder = path.dirname(process.execPath);
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// i.e. my-app/Update.exe
const updateExe = path.resolve(appFolder, '..', 'Update.exe');
const exeName = path.basename(process.execPath);
2020-07-01 19:27:12 +00:00
let spawnedArgs: string[] = [];
let spawnedProcess: ChildProcessWithoutNullStreams | undefined;
2020-07-01 19:27:12 +00:00
const isSameArgs = (args: string[]) => args.length === spawnedArgs.length && args.every((e, i) => e === spawnedArgs[i]);
2016-01-12 02:40:23 +00:00
2016-01-14 18:44:21 +00:00
// Spawn a command and invoke the callback when it completes with an error
// and the output from standard out.
const spawnUpdate = async function (args: string[], options: { detached: boolean }): Promise<string> {
return new Promise((resolve, reject) => {
// Ensure we don't spawn multiple squirrel processes
2022-07-05 15:49:56 +00:00
// 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)) {
throw new Error(`AutoUpdater process with arguments ${args} is already running`);
} else if (!spawnedProcess) {
spawnedProcess = spawn(updateExe, args, {
detached: options.detached,
2017-12-02 01:18:37 +00:00
windowsHide: true
});
spawnedArgs = args || [];
}
let stdout = '';
let stderr = '';
spawnedProcess.stdout.on('data', (data) => { stdout += data; });
spawnedProcess.stderr.on('data', (data) => { stderr += data; });
2016-01-12 02:40:23 +00:00
spawnedProcess.on('error', (error) => {
spawnedProcess = undefined;
spawnedArgs = [];
reject(error);
});
2016-01-12 02:40:23 +00:00
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);
}
});
});
};
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// Start an instance of the installed app.
2020-07-01 19:27:12 +00:00
export function processStart () {
spawnUpdate(['--processStartAndWait', exeName], { detached: true });
2020-07-01 19:27:12 +00:00
}
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// Download the releases specified by the URL and write new results to stdout.
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}`);
}
2020-07-01 19:27:12 +00:00
}
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// Update the application to the latest remote version specified by URL.
export async function update (updateURL: string): Promise<void> {
await spawnUpdate(['--update', updateURL], { detached: false });
2020-07-01 19:27:12 +00:00
}
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// Is the Update.exe installed with the current application?
2020-07-01 19:27:12 +00:00
export function supported () {
2016-01-12 02:40:23 +00:00
try {
2020-07-01 19:27:12 +00:00
fs.accessSync(updateExe, fs.constants.R_OK);
return true;
} catch {
return false;
2016-01-12 02:40:23 +00:00
}
2020-07-01 19:27:12 +00:00
}