Some need changes and fixed the updater

This commit is contained in:
WolverinDEV 2019-10-27 22:39:59 +01:00
parent 4ded40e7ba
commit d0687a26ba
10 changed files with 137 additions and 70 deletions

16
main.ts
View File

@ -4,12 +4,28 @@ import * as rhelper from "./modules/shared/require";
console.log("Native module path: %s", rhelper.native_module_path());
import * as crash_handler from "./modules/crash_handler";
import * as child_process from "child_process";
import {app} from "electron";
const is_electron_run = process.argv[0].endsWith("electron") || process.argv[0].endsWith("electron.exe");
const process_arguments = is_electron_run ? process.argv.slice(2) : process.argv.slice(1);
if(process_arguments.length > 0 && process_arguments[0] === "crash-handler") {
/* crash handler callback */
crash_handler.handle_crash_callback(process_arguments.slice(1));
} else if(process_arguments.length > 0 && process_arguments[0] === "dtest") {
console.log("Executing installer");
try {
let pipe = child_process.spawn("\"C:\\Program Files (x86)\\TeaSpeak\\update-installer.exe\"", [], {
detached: true,
shell: true,
cwd: "C:\\Program Files (x86)\\TeaSpeak",
stdio: "ignore"
});
} catch(error) {
console.dir(error);
}
setTimeout(() => app.exit(0), 2000);
} else {
if(process_arguments.length > 0 && process_arguments[0] == "--main-crash-handler")
crash_handler.initialize_handler("main", is_electron_run);

View File

@ -118,31 +118,27 @@ export async function newest_version(current_version: Version, channel?: string)
return undefined;
}
export async function extract_updater(update_file: string) {
/**
* @param update_file The input file from where the update will get installed
* @return The target executable file
*/
export async function extract_updater(update_file: string) : Promise<string> {
if(!fs.existsSync(update_file)) throw "Missing update file!";
let parent_path = app.getAppPath();
if(parent_path.endsWith(".asar")) {
parent_path = path.join(parent_path, "..", "..");
parent_path = fs.realpathSync(parent_path);
}
let post_path;
if(os.platform() == "linux")
post_path = parent_path + "/update-installer";
else
post_path = parent_path + "/update-installer.exe";
let update_installer = app.getPath('temp') + "/teaclient-update-installer-" + Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
if(os.platform() == "win32")
update_installer += ".exe";
const source = fs.createReadStream(update_file);
const extract = tar.extract();
await new Promise(resolve => {
await new Promise((resolve, reject) => {
let updater_found = false;
source.on('end', () => {
if(!updater_found) {
console.error("Failed to extract the updater (Updater hasn't been found!)");
resolve(); //FIXME use reject!
reject("Updater hasn't been found in bundle");
}
resolve();
});
@ -151,13 +147,16 @@ export async function extract_updater(update_file: string) {
console.log("Got entry " + header.name);
if(header.name == "./update-installer" || header.name == "./update-installer.exe") {
console.log("Found updater! (" + header.size + ")");
console.log("Extracting to %s", post_path);
const s = fs.createWriteStream(post_path);
console.log("Found updater! (" + header.size + " bytes)");
console.log("Extracting to %s", update_installer);
const s = fs.createWriteStream(update_installer);
stream.pipe(s).on('finish', event => {
console.log("Updater extracted and written!");
updater_found = true;
resolve();
}).on('error', event => {
console.error("Failed write update file: %o", event);
reject("failed to write file")
});
} else {
stream.resume(); //Drain the stream
@ -167,6 +166,8 @@ export async function extract_updater(update_file: string) {
source.pipe(extract);
});
return update_installer;
}
export async function update_updater() : Promise<void> {
@ -466,6 +467,7 @@ export async function execute_update(update_file: string, restart_callback: (cal
console.log("Using update file: %s", update_file);
const temp_directory = path.join(app.getPath("temp"), "teaclient_update_" + Math.random().toString(36).substring(7));
let updater_executable;
{
console.log("Preparing update source directory at %s", temp_directory);
try {
@ -486,8 +488,9 @@ export async function execute_update(update_file: string, restart_callback: (cal
if(header.type == "directory") {
await fs.mkdirp(target_file);
} else if(header.type == "file") {
const target_finfo = path.parse(target_file);
{
const directory = path.parse(target_file).dir;
const directory = target_finfo.dir;
console.debug("Testing for directory: %s", directory);
if(!(await util.promisify(ofs.exists)(directory)) || !(await util.promisify(ofs.stat)(directory)).isDirectory()) {
console.log("Creating directory %s", directory);
@ -506,6 +509,12 @@ export async function execute_update(update_file: string, restart_callback: (cal
.on('error', reject)
.on('finish', resolve);
});
if(target_finfo.name === "update-installer" || target_finfo.name === "update-installer.exe") {
updater_executable = target_file;
console.log("Found update installer: %s", target_file);
}
return; /* success */
} catch(error) {
console.error("Failed to extract update file %s: %o", header.name, error);
@ -534,6 +543,10 @@ export async function execute_update(update_file: string, restart_callback: (cal
throw "update unpacking failed";
}
}
if(typeof(updater_executable) !== "string" || !(await fs.pathExists(updater_executable)))
throw "missing update installer executable within update package";
/* the "new" environment should now be available at 'temp_directory' */
console.log("Update unpacked successfully. Building update extractor file.");
@ -555,22 +568,14 @@ export async function execute_update(update_file: string, restart_callback: (cal
throw "failed to write update install config file";
}
const update_installer = path.join(application_path, "update-installer" + (os.platform() === "win32" ? ".exe" : ""));
if(!(await fs.pathExists(update_installer))) {
console.error("Missing update installer! Supposed to be at %s", update_installer);
throw "Missing update installer!";
} else {
console.log("Using update installer located at %s", update_installer);
}
if(os.platform() == "linux") {
console.log("Executing update install on linux");
//We have to unpack it later
const rest_callback = () => {
console.log("Executing command %s with args %o", update_installer, [log_file, config_file]);
console.log("Executing command %s with args %o", updater_executable, [log_file, config_file]);
try {
let result = child_process.spawnSync(update_installer, [log_file, config_file]);
let result = child_process.spawnSync(updater_executable, [log_file, config_file]);
if(result.status != 0) {
console.error("Failed to execute update installer! Return code: %d", result.status);
dialog.showMessageBox({
@ -621,13 +626,21 @@ export async function execute_update(update_file: string, restart_callback: (cal
//We have to unpack it later
const rest_callback = () => {
let pipe = child_process.spawn(update_installer, [log_file, config_file], {
detached: true,
cwd: application_path,
stdio: 'ignore',
});
pipe.unref();
app.quit();
console.log("Executing command %s with args %o", updater_executable, [log_file, config_file]);
try {
const pipe = child_process.spawn(updater_executable, [log_file, config_file], {
detached: true,
shell: true,
cwd: path.dirname(app.getAppPath()),
stdio: "ignore"
});
pipe.unref();
app.quit();
} catch(error) {
console.dir(error);
electron.dialog.showErrorBox("Failed to finalize update", "Failed to finalize update.\nInvoking the update-installer.exe failed.\nLookup the console for more details.");
}
};
restart_callback(rest_callback);
}

View File

@ -0,0 +1,21 @@
import * as electron from "electron";
import * as main_window from "./main_window";
export function handle_second_instance_call(argv: string[], work_dir: string) {
const original_args = argv.slice(1).filter(e => !e.startsWith("--original-process-start-time=") && e != "--allow-file-access-from-files");
console.log("Second instance: %o", original_args);
if(!main_window.main_window) {
console.warn("Ignoring second instance call because we haven't yet started");
return;
}
main_window.main_window.focus();
{
const connect_url = argv.find(e => e.startsWith("teaclient://"));
if(connect_url) {
console.log("Received connect url: %s", connect_url);
main_window.main_window.webContents.send('connect', connect_url);
}
}
}

View File

@ -1,6 +1,7 @@
// Quit when all windows are closed.
import * as electron from "electron";
import * as app_updater from "./app-updater";
import * as instance_handler from "./instance_handler";
import { app } from "electron";
import MessageBoxOptions = electron.MessageBoxOptions;
@ -35,8 +36,6 @@ async function execute_app() {
}
}
if(process_args.has_value("update-execute")) {
console.log("Executing update " + process_args.value("update-execute"));
await app_updater.execute_update(process_args.value("update-execute"), callback => {
@ -153,6 +152,12 @@ async function execute_app() {
}
}
/* register client a teaclient:// handler */
if(app.getAppPath().endsWith(".asar")) {
if(!app.setAsDefaultProtocolClient("teaclient", app.getPath("exe")))
console.error("Failed to setup default teaclient protocol handler");
}
try {
{
const version = await app_updater.current_version();
@ -176,32 +181,26 @@ async function execute_app() {
function main() {
process.on('uncaughtException', err => {
console.error(err, 'Uncaught Exception thrown');
console.error('Uncaught Exception thrown:');
console.dir(err);
process.exit(1);
});
/*
if(false) {
SegfaultHandler = require('segfault-handler');
SegfaultHandler.registerHandler("crash.log"); // With no argument, SegfaultHandler will generate a generic log file name
}
const SegfaultHandler = require('segfault-handler');
SegfaultHandler.registerHandler("crash.log"); // With no argument, SegfaultHandler will generate a generic log file name
*/
if(app) { //We're executed!
parse_arguments();
if(process_args.has_value(Arguments.DISABLE_HARDWARE_ACCELERATION))
app.disableHardwareAcceleration();
if(process_args.has_value(Arguments.DUMMY_CRASH_MAIN))
crash_handler.handler.crash();
if(!process_args.has_value(Arguments.DEBUG) && !process_args.has_value(Arguments.NO_SINGLE_INSTANCE)) {
if(!app.requestSingleInstanceLock()) {
console.log("Another instance is already running. Closing this instance");
app.exit(0);
}
app.on('second-instance', (event, argv, workingDirectory) => instance_handler.handle_second_instance_call(argv, workingDirectory));
}
app.on('ready', execute_app);
}

View File

@ -44,6 +44,7 @@ function spawn_main_window(entry_point: string) {
});
main_window.on('closed', () => {
app.releaseSingleInstanceLock();
require("./url-preview").close();
main_window = null;
prevent_instant_close = false;

View File

@ -0,0 +1,9 @@
window["require_setup"](module);
import * as electron from "electron";
const remote = electron.remote;
electron.ipcRenderer.on('connect', (event, url) => {
console.log(tr("Received connect event to %s"), url);
console.error(tr("Dropping connect event (Currently not supported)"));
});

View File

@ -200,6 +200,13 @@ const load_modules = async () => {
console.dir(error);
throw error;
}
try {
require("./app_backend");
} catch(error) {
console.error("Failed to load renderer app backend");
console.dir(error);
throw error;
}
try {
const helper = require("./icon-helper");
await helper.initialize();

View File

@ -40,7 +40,7 @@ export function parse_arguments() {
const minimist: <T> (args, opts) => T = require("./minimist") as any;
let args = minimist<Arguments>(is_electron_run ? process.argv.slice(2) : process.argv.slice(1), {
boolean: true,
stopEarly: true
"--": true
}) as Arguments;
args.has_flag = (...keys) => {
for(const key of [].concat(...Array.of(...keys).map(e => Array.isArray(e) ? Array.of(...e) : [e])))

View File

@ -57,7 +57,7 @@ namespace minimist {
const parse = (args: string[], options: minimist.Opts) => {
options = options || {};
var flags = { bools : {}, strings : {}, unknownFn: null, allBools: false };
let flags = { bools : {}, strings : {}, unknownFn: null, allBools: false };
if (typeof options['unknown'] === 'function') {
flags.unknownFn = options['unknown'];
@ -71,7 +71,7 @@ const parse = (args: string[], options: minimist.Opts) => {
});
}
var aliases = {};
let aliases = {};
Object.keys(options.alias || {}).forEach(function (key) {
aliases[key] = [].concat(options.alias[key]);
aliases[key].forEach(function (x) {
@ -88,14 +88,14 @@ const parse = (args: string[], options: minimist.Opts) => {
}
});
var defaults = options['default'] || {};
let defaults = options['default'] || {};
var argv = { _ : [] };
let argv = { _ : [] };
Object.keys(flags.bools).forEach(function (key) {
setArg(key, defaults[key] === undefined ? false : defaults[key]);
});
var notFlags = [];
let notFlags = [];
if (args.indexOf('--') !== -1) {
notFlags = args.slice(args.indexOf('--')+1);
@ -147,16 +147,16 @@ const parse = (args: string[], options: minimist.Opts) => {
});
}
for (var i = 0; i < args.length; i++) {
var arg = args[i];
for (let i = 0; i < args.length; i++) {
let arg = args[i];
if (/^--.+=/.test(arg)) {
// Using [\s\S] instead of . because js doesn't support the
// 'dotall' regex modifier. See:
// http://stackoverflow.com/a/1068308/13216
var m = arg.match(/^--([^=]+)=([\s\S]*)$/);
var key = m[1];
var value: any = m[2];
let m = arg.match(/^--([^=]+)=([\s\S]*)$/);
let key = m[1];
let value: any = m[2];
if (flags.bools[key]) {
value = value !== 'false';
}
@ -169,8 +169,8 @@ const parse = (args: string[], options: minimist.Opts) => {
}
*/
else if (/^--.+/.test(arg)) {
var key = arg.match(/^--(.+)/)[1];
var next = args[i + 1];
let key = arg.match(/^--(.+)/)[1];
let next = args[i + 1];
if (next !== undefined && !/^-/.test(next)
&& !flags.bools[key]
&& !flags.allBools
@ -187,11 +187,11 @@ const parse = (args: string[], options: minimist.Opts) => {
}
}
else if (/^-[^-]+/.test(arg)) {
var letters = arg.slice(1,-1).split('');
let letters = arg.slice(1,-1).split('');
var broken = false;
for (var j = 0; j < letters.length; j++) {
var next = arg.slice(j+2);
let broken = false;
for (let j = 0; j < letters.length; j++) {
let next = arg.slice(j+2);
if (next === '-') {
setArg(letters[j], next, arg)
@ -221,7 +221,7 @@ const parse = (args: string[], options: minimist.Opts) => {
}
}
var key = arg.slice(-1)[0];
let key = arg.slice(-1)[0];
if (!broken && key !== '-') {
if (args[i+1] && !/^(-|--)[^-]/.test(args[i+1])
&& !flags.bools[key]
@ -262,7 +262,7 @@ const parse = (args: string[], options: minimist.Opts) => {
});
if (options['--']) {
argv['--'] = new Array();
argv['--'] = [];
notFlags.forEach(function(key) {
argv['--'].push(key);
});
@ -277,12 +277,12 @@ const parse = (args: string[], options: minimist.Opts) => {
};
function hasKey (obj, keys) {
var o = obj;
let o = obj;
keys.slice(0,-1).forEach(function (key) {
o = (o[key] || {});
});
var key = keys[keys.length - 1];
let key = keys[keys.length - 1];
return key in o;
}

View File

@ -12,6 +12,7 @@
"start-d1": "electron . --disable-hardware-acceleration --debug -t --gdb -su http://clientapi.teaspeak.dev/ --updater-ui-loader_type=0",
"start-n": "electron . -t --disable-hardware-acceleration --no-single-instance -u=https://clientapi.teaspeak.de/ -d --updater-ui-loader_type=0",
"start-01": "electron . --updater-channel=test -u=http://dev.clientapi.teaspeak.de/ -d --updater-ui-loader_type=0 --updater-local-version=1.0.1",
"dtest": "electron . dtest",
"compile-sass": "sass --update .:.",
"compile-tsc": "tsc",
"build-linux-64": "node installer/build.js linux",