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/
|
2020-03-20 20:28:31 +00:00
|
|
|
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
|
2020-03-20 20:28:31 +00:00
|
|
|
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;
|
2016-04-13 01:09:46 +00:00
|
|
|
|
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.
|
2020-07-01 19:27:12 +00:00
|
|
|
const spawnUpdate = function (args: string[], detached: boolean, callback: Function) {
|
|
|
|
let error: Error, errorEmitted: boolean, stderr: string, stdout: string;
|
2016-04-13 01:09:46 +00:00
|
|
|
|
2016-01-12 02:40:23 +00:00
|
|
|
try {
|
2016-04-13 01:09:46 +00:00
|
|
|
// Ensure we don't spawn multiple squirrel processes
|
|
|
|
// Process spawned, same args: Attach events to alread running process
|
|
|
|
// Process spawned, different args: Return with error
|
|
|
|
// No process spawned: Spawn new process
|
|
|
|
if (spawnedProcess && !isSameArgs(args)) {
|
2017-11-23 21:53:03 +00:00
|
|
|
// Disabled for backwards compatibility:
|
|
|
|
// eslint-disable-next-line standard/no-callback-literal
|
2020-03-20 20:28:31 +00:00
|
|
|
return callback(`AutoUpdater process with arguments ${args} is already running`);
|
2016-04-13 01:09:46 +00:00
|
|
|
} else if (!spawnedProcess) {
|
|
|
|
spawnedProcess = spawn(updateExe, args, {
|
2017-12-02 01:18:37 +00:00
|
|
|
detached: detached,
|
|
|
|
windowsHide: true
|
2020-03-20 20:28:31 +00:00
|
|
|
});
|
|
|
|
spawnedArgs = args || [];
|
2016-04-13 01:09:46 +00:00
|
|
|
}
|
2016-01-12 02:40:23 +00:00
|
|
|
} catch (error1) {
|
2022-01-31 11:37:40 +00:00
|
|
|
error = error1 as Error;
|
2016-01-12 02:40:23 +00:00
|
|
|
|
2016-01-14 18:35:29 +00:00
|
|
|
// Shouldn't happen, but still guard it.
|
2016-03-24 20:15:04 +00:00
|
|
|
process.nextTick(function () {
|
2020-03-20 20:28:31 +00:00
|
|
|
return callback(error);
|
|
|
|
});
|
|
|
|
return;
|
2016-01-12 02:40:23 +00:00
|
|
|
}
|
2020-03-20 20:28:31 +00:00
|
|
|
stdout = '';
|
|
|
|
stderr = '';
|
2017-12-02 01:18:57 +00:00
|
|
|
|
2020-03-20 20:28:31 +00:00
|
|
|
spawnedProcess.stdout.on('data', (data) => { stdout += data; });
|
|
|
|
spawnedProcess.stderr.on('data', (data) => { stderr += data; });
|
2017-12-02 01:18:57 +00:00
|
|
|
|
2020-03-20 20:28:31 +00:00
|
|
|
errorEmitted = false;
|
2017-12-02 01:18:57 +00:00
|
|
|
spawnedProcess.on('error', (error) => {
|
2020-03-20 20:28:31 +00:00
|
|
|
errorEmitted = true;
|
|
|
|
callback(error);
|
|
|
|
});
|
2017-12-02 01:18:57 +00:00
|
|
|
|
2016-03-24 20:15:04 +00:00
|
|
|
return spawnedProcess.on('exit', function (code, signal) {
|
2020-03-20 20:28:31 +00:00
|
|
|
spawnedProcess = undefined;
|
|
|
|
spawnedArgs = [];
|
2016-04-13 01:09:46 +00:00
|
|
|
|
2016-01-14 18:35:29 +00:00
|
|
|
// We may have already emitted an error.
|
2016-01-12 02:40:23 +00:00
|
|
|
if (errorEmitted) {
|
2020-03-20 20:28:31 +00:00
|
|
|
return;
|
2016-01-12 02:40:23 +00:00
|
|
|
}
|
|
|
|
|
2016-01-14 18:35:29 +00:00
|
|
|
// Process terminated with error.
|
2016-01-12 02:40:23 +00:00
|
|
|
if (code !== 0) {
|
2017-11-23 21:53:03 +00:00
|
|
|
// Disabled for backwards compatibility:
|
|
|
|
// eslint-disable-next-line standard/no-callback-literal
|
2020-03-20 20:28:31 +00:00
|
|
|
return callback(`Command failed: ${signal != null ? signal : code}\n${stderr}`);
|
2016-01-12 02:40:23 +00:00
|
|
|
}
|
|
|
|
|
2016-01-14 18:35:29 +00:00
|
|
|
// Success.
|
2020-03-20 20:28:31 +00:00
|
|
|
callback(null, 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 () {
|
2020-03-20 20:28:31 +00:00
|
|
|
return spawnUpdate(['--processStartAndWait', exeName], true, function () {});
|
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.
|
2020-07-01 19:27:12 +00:00
|
|
|
export function checkForUpdate (updateURL: string, callback: (error: Error | null, update?: any) => void) {
|
|
|
|
return spawnUpdate(['--checkForUpdate', updateURL], false, function (error: Error, stdout: string) {
|
2020-03-20 20:28:31 +00:00
|
|
|
let ref, ref1, update;
|
2016-01-12 02:40:23 +00:00
|
|
|
if (error != null) {
|
2020-03-20 20:28:31 +00:00
|
|
|
return callback(error);
|
2016-01-12 02:40:23 +00:00
|
|
|
}
|
|
|
|
try {
|
2016-01-14 18:35:29 +00:00
|
|
|
// Last line of output is the JSON details about the releases
|
2020-03-20 20:28:31 +00:00
|
|
|
const json = stdout.trim().split('\n').pop();
|
2020-07-01 19:27:12 +00:00
|
|
|
update = (ref = JSON.parse(json!)) != null ? (ref1 = ref.releasesToApply) != null ? typeof ref1.pop === 'function' ? ref1.pop() : undefined : undefined : undefined;
|
2019-02-21 09:26:07 +00:00
|
|
|
} catch {
|
2017-11-23 21:53:03 +00:00
|
|
|
// Disabled for backwards compatibility:
|
|
|
|
// eslint-disable-next-line standard/no-callback-literal
|
2020-07-01 19:27:12 +00:00
|
|
|
return callback(new Error(`Invalid result:\n${stdout}`));
|
2016-01-12 02:40:23 +00:00
|
|
|
}
|
2020-03-20 20:28:31 +00:00
|
|
|
return callback(null, update);
|
|
|
|
});
|
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.
|
2020-07-01 19:27:12 +00:00
|
|
|
export function update (updateURL: string, callback: (error: Error) => void) {
|
2020-03-20 20:28:31 +00:00
|
|
|
return spawnUpdate(['--update', updateURL], false, callback);
|
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);
|
2020-03-20 20:28:31 +00:00
|
|
|
return true;
|
2019-02-21 09:26:07 +00:00
|
|
|
} catch {
|
2020-03-20 20:28:31 +00:00
|
|
|
return false;
|
2016-01-12 02:40:23 +00:00
|
|
|
}
|
2020-07-01 19:27:12 +00:00
|
|
|
}
|