Skip to content

Commit 2935399

Browse files
committed
Add launcher auto-update feature.
1 parent 013ddd3 commit 2935399

File tree

7 files changed

+305
-38
lines changed

7 files changed

+305
-38
lines changed

src/App.vue

+37
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,14 @@ ipcRenderer.on('silent-installer-failed', (event, arg) => {
502502
vue_this.openDialog('InstallFailed', true);
503503
});
504504

505+
ipcRenderer.on('launcher-auto-update-failed', (event, arg) => {
506+
vue_this.$store.commit('mutate', {
507+
property: 'currentNotice',
508+
with: arg
509+
});
510+
vue_this.openDialog('UpdateLauncherFailed', true);
511+
});
512+
505513
ipcRenderer.on('check-for-updates-failed', (event, arg) => {
506514
vue_this.disableUpdateButton = false;
507515
vue_this.resetUpdateButton();
@@ -546,6 +554,16 @@ ipcRenderer.on('checked-for-updates-on-launch', (event, arg) => {
546554
vue_this.openDialog('UpdateAvailableOnLaunch', true);
547555
});
548556

557+
ipcRenderer.on('checked-for-launcher-updates', (event, arg) => {
558+
if (arg.updateAvailable === true) {
559+
vue_this.openDialog('LauncherUpdateAvailable', true);
560+
}
561+
});
562+
563+
ipcRenderer.on('launcher-auto-updater-running', (event, arg) => {
564+
vue_this.showOverlay = true;
565+
});
566+
549567
/* Debug COMMANDS */
550568

551569
function clearSelectedInterface() {
@@ -571,6 +589,7 @@ import FailedMetadata from './components/Dialogs/FailedMetadata'
571589
import FirstTimeUser from './components/Dialogs/FirstTimeUser'
572590
import InstallComplete from './components/Dialogs/InstallComplete'
573591
import InstallFailed from './components/Dialogs/InstallFailed'
592+
import LauncherUpdateAvailable from './components/Dialogs/LauncherUpdateAvailable'
574593
import LaunchFailedInterfaceRunning from './components/Dialogs/LaunchFailedInterfaceRunning'
575594
import LaunchOptions from './components/Dialogs/LaunchOptions'
576595
import NoInstallerFound from './components/Dialogs/NoInstallerFound'
@@ -581,6 +600,7 @@ import ReportAnIssue from './components/Dialogs/ReportAnIssue'
581600
import SelectVersion from './components/Dialogs/SelectVersion'
582601
import UpdateAvailable from './components/Dialogs/UpdateAvailable'
583602
import UpdateAvailableOnLaunch from './components/Dialogs/UpdateAvailableOnLaunch'
603+
import UpdateLauncherFailed from './components/Dialogs/UpdateLauncherFailed'
584604
import WantToClose from './components/Dialogs/WantToClose'
585605

586606
export default {
@@ -600,6 +620,7 @@ export default {
600620
FirstTimeUser,
601621
InstallComplete,
602622
InstallFailed,
623+
LauncherUpdateAvailable,
603624
LaunchFailedInterfaceRunning,
604625
LaunchOptions,
605626
NoUpdateAvailable,
@@ -610,6 +631,7 @@ export default {
610631
SelectVersion,
611632
UpdateAvailable,
612633
UpdateAvailableOnLaunch,
634+
UpdateLauncherFailed,
613635
WantToClose
614636
},
615637
methods: {
@@ -701,6 +723,17 @@ export default {
701723
this.openDialog('CancelDownload', true);
702724
}
703725
},
726+
downloadLauncher: function () {
727+
if (this.$store.state.updateLauncherOnNextLaunch === true) {
728+
this.$store.commit('mutate', {
729+
property: 'updateLauncherOnNextLaunch',
730+
with: false
731+
});
732+
}
733+
734+
// This function also auto-installs the update to the launcher.
735+
ipcRenderer.send('request-launcher-auto-update');
736+
},
704737
installInterface: function () {
705738
ipcRenderer.send('install-vircadia');
706739
},
@@ -748,9 +781,13 @@ export default {
748781
ipcRenderer.send('request-close');
749782
}
750783

784+
751785
ipcRenderer.send('load-state');
752786
ipcRenderer.send('get-library-folder');
753787
},
788+
mounted: function () {
789+
ipcRenderer.send('check-for-launcher-updates');
790+
},
754791
computed: {
755792
interfaceSelected () {
756793
return this.$store.state.selectedInterface;

src/background.js

+76-36
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
*/
1010

1111
'use strict'
12+
require('./global.js');
1213

1314
// import * as Sentry from '@sentry/electron'
1415
// import { init } from '@sentry/electron/dist/main';
@@ -21,19 +22,17 @@ import {
2122
installVueDevtools,
2223
createProtocol,
2324
} from 'vue-cli-plugin-electron-builder/lib';
24-
require('./global.js');
2525
import path from 'path';
26-
const { shell } = require('electron');
27-
const { dialog } = require('electron');
26+
const { shell, dialog } = require('electron');
2827
const electronDlMain = require('electron-dl');
2928
const { readdirSync } = require('fs');
3029
const { forEach } = require('p-iteration');
3130
const hasha = require('hasha');
3231
const compareVersions = require('compare-versions');
33-
const isAdmin = require('is-admin');
3432
const glob = require('glob');
3533
const cp = require('child_process');
3634
const log = require('electron-log');
35+
import { autoUpdater } from 'electron-updater'
3736
const tasklist = require('tasklist'); // This is specific to Windows.
3837
// For tasklist to work correctly on some systems...
3938
// if (process.platform === "win32") {
@@ -43,6 +42,7 @@ const tasklist = require('tasklist'); // This is specific to Windows.
4342
import * as versionPaths from './electron_modules/versionPaths.js';
4443
import * as migrateLauncher from './electron_modules/migrateLauncher.js';
4544
import * as download from './electron_modules/networking/download.js';
45+
import * as privileges from './electron_modules/privileges.js'
4646

4747
function initialize () {
4848
if (pantheonConfig.app.storagePath) {
@@ -111,7 +111,9 @@ function createWindow () {
111111
if (process.env.WEBPACK_DEV_SERVER_URL) {
112112
// Load the url of the dev server if in development mode
113113
win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
114-
if (!process.env.IS_TEST) win.webContents.openDevTools()
114+
if (!process.env.IS_TEST) {
115+
win.webContents.openDevTools()
116+
}
115117
} else {
116118
createProtocol('app')
117119
// Load the index.html when not in development
@@ -338,6 +340,28 @@ function setLibraryDialog() {
338340
})
339341
}
340342

343+
function requestLauncherAsAdmin() {
344+
var appPathSplit = app.getPath('exe').split('\\');
345+
var appPathCleaned = appPathSplit.slice(0, appPathSplit.length - 1).join('\\');
346+
347+
var pathToLauncher = appPathCleaned + '\\' + PRODUCT_NAME;
348+
var pathToElevator = '"' + appPathCleaned + '\\resources\\elevate.exe' + '"';
349+
var launchParameter = '-k "' + pathToLauncher + '"';
350+
var interface_exe = require('child_process').spawn;
351+
352+
// console.info(dialog.showMessageBox({ message: pathToElevator }))
353+
// console.info(dialog.showMessageBox({ message: launchParameter }))
354+
// console.info(dialog.showMessageBox({ message: appPathCleaned }))
355+
356+
var elevateExe = interface_exe(pathToElevator, [launchParameter], {
357+
windowsVerbatimArguments: true,
358+
shell: true,
359+
detached: true
360+
});
361+
362+
app.exit();
363+
}
364+
341365
// async function getCurrentInterfaceJSON() {
342366
// var interfacePackageJSON = storagePath.interfaceSettings + '/interface_package.json';
343367
//
@@ -384,16 +408,6 @@ async function checkForInterfaceUpdates() {
384408
}
385409
}
386410

387-
async function isRunningAsAdministrator() {
388-
var requestIsAdmin = await isAdmin();
389-
390-
if (requestIsAdmin) {
391-
return true;
392-
} else {
393-
return false;
394-
}
395-
}
396-
397411
async function checkForUpdates (checkSilently) {
398412
if (storagePath.interfaceSettings) {
399413
// This means to update because an interface exists and is selected.
@@ -683,7 +697,7 @@ async function silentInstall(useOldInstaller) {
683697
var executablePath; // This is the location that the installer exe is located in after being downloaded.
684698
var exeLocToInstall; // This is what gets installed.
685699
var checkPrereqs = await checkRunningApps();
686-
var isAdmin = await isRunningAsAdministrator();
700+
var isAdmin = await privileges.isRunningAsAdministrator();
687701

688702
if (checkPrereqs.sandbox === true) {
689703
win.webContents.send('silent-installer-failed', { "message": 'Your server sandbox is running. Please close it before proceeding.' });
@@ -970,7 +984,7 @@ ipcMain.on('download-vircadia', async (event, arg) => {
970984
var libraryPath;
971985
var downloadURL = await getDownloadURL();
972986
var vircadiaMetaJSON = await download.cdn.meta();
973-
var isAdmin = await isRunningAsAdministrator();
987+
var isAdmin = await privileges.isRunningAsAdministrator();
974988
var checkPrereqs = await checkRunningApps();
975989
var installerName = pantheonConfig.manager.preInstallerName;
976990
var installerNamePost = pantheonConfig.manager.postInstallerName;
@@ -1107,27 +1121,53 @@ ipcMain.on('request-close', async (event, arg) => {
11071121
});
11081122

11091123
ipcMain.on('request-launcher-as-admin', async (event, arg) => {
1110-
var appPathSplit = app.getPath('exe').split('\\');
1111-
var appPathCleaned = appPathSplit.slice(0, appPathSplit.length - 1).join('\\');
1112-
1113-
var pathToLauncher = appPathCleaned + '\\' + PRODUCT_NAME;
1114-
var pathToElevator = '"' + appPathCleaned + '\\resources\\elevate.exe' + '"';
1115-
var launchParameter = '-k "' + pathToLauncher + '"';
1116-
var interface_exe = require('child_process').spawn;
1117-
1118-
// console.info(dialog.showMessageBox({ message: pathToElevator }))
1119-
// console.info(dialog.showMessageBox({ message: launchParameter }))
1120-
// console.info(dialog.showMessageBox({ message: appPathCleaned }))
1121-
1122-
var elevateExe = interface_exe(pathToElevator, [launchParameter], {
1123-
windowsVerbatimArguments: true,
1124-
shell: true,
1125-
detached: true
1126-
});
1127-
1128-
app.exit();
1124+
requestLauncherAsAdmin();
1125+
});
1126+
1127+
ipcMain.on('get-is-launcher-admin', async (event, arg) => {
1128+
var isAdmin = await privileges.isRunningAsAdministrator();
1129+
win.webContents.send('send-is-launcher-admin', isAdmin);
11291130
});
11301131

11311132
ipcMain.on('close-launcher', (event, arg) => {
11321133
app.exit();
11331134
});
1135+
1136+
// LAUNCHER AUTO UPDATER //
1137+
1138+
autoUpdater.autoDownload = false;
1139+
const autoUpdaterLoggingPrefix = '[Launcher AutoUpdater]';
1140+
1141+
autoUpdater.on('checking-for-update', () => {
1142+
console.info(autoUpdaterLoggingPrefix, 'checking-for-update');
1143+
})
1144+
autoUpdater.on('update-available', (ev, info) => {
1145+
console.info(autoUpdaterLoggingPrefix, 'update-available', info);
1146+
win.webContents.send('checked-for-launcher-updates', { 'updateAvailable': true });
1147+
})
1148+
autoUpdater.on('update-not-available', (ev, info) => {
1149+
console.info(autoUpdaterLoggingPrefix, 'update-not-available', info);
1150+
win.webContents.send('checked-for-launcher-updates', { 'updateAvailable': false });
1151+
})
1152+
autoUpdater.on('error', (ev, err) => {
1153+
console.info(autoUpdaterLoggingPrefix, 'error', err);
1154+
})
1155+
autoUpdater.on('download-progress', (ev, progressObj) => {
1156+
console.info(autoUpdaterLoggingPrefix, 'download-progress', progressObj);
1157+
})
1158+
autoUpdater.on('update-downloaded', (ev, info) => {
1159+
console.info(autoUpdaterLoggingPrefix, 'update-downloaded');
1160+
console.info(autoUpdaterLoggingPrefix, 'Attempting to install.');
1161+
autoUpdater.quitAndInstall(true, true);
1162+
});
1163+
1164+
ipcMain.on('check-for-launcher-updates', async (event, arg) => {
1165+
autoUpdater.checkForUpdates();
1166+
});
1167+
1168+
ipcMain.on('request-launcher-auto-update', async (event, arg) => {
1169+
win.webContents.send('launcher-auto-updater-running');
1170+
autoUpdater.downloadUpdate();
1171+
});
1172+
1173+
// END LAUNCHER AUTO UPDATER //
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<!--
2+
// LauncherUpdateAvailable.vue
3+
//
4+
// Created by Kalila L. on Mar 5 2021.
5+
// Copyright 2021 Vircadia contributors.
6+
//
7+
// Distributed under the Apache License, Version 2.0.
8+
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
9+
-->
10+
<template>
11+
<v-dialog
12+
width="500"
13+
persistent
14+
v-model="showLauncherUpdateAvailable"
15+
>
16+
<v-card>
17+
<v-card-title
18+
class="headline"
19+
primary-title
20+
dark
21+
>
22+
<v-icon color="green" class="mr-2">mdi-comment-question</v-icon>
23+
Launcher Update Available
24+
</v-card-title>
25+
26+
<v-card-text>
27+
An update is available for your launcher. <br />
28+
Would you like to download and install it?
29+
</v-card-text>
30+
31+
<v-divider></v-divider>
32+
33+
<v-card-actions>
34+
<v-btn
35+
color="primary"
36+
@click="cancelUpdate(); $emit('hideDialog')"
37+
>
38+
No
39+
</v-btn>
40+
<v-spacer></v-spacer>
41+
<v-btn
42+
color="primary"
43+
@click="continueUpdate(); $emit('hideDialog')"
44+
>
45+
Yes
46+
</v-btn>
47+
</v-card-actions>
48+
</v-card>
49+
</v-dialog>
50+
</template>
51+
52+
53+
<script>
54+
const { ipcRenderer } = require('electron');
55+
56+
export default {
57+
name: 'LauncherUpdateAvailable',
58+
methods: {
59+
continueUpdate: function () {
60+
ipcRenderer.send('request-launcher-auto-update');
61+
},
62+
cancelUpdate: function () {
63+
// Nothing to do.
64+
},
65+
},
66+
data: () => ({
67+
showLauncherUpdateAvailable: true,
68+
}),
69+
};
70+
</script>

0 commit comments

Comments
 (0)