ing
This commit is contained in:
parent
5e221b0753
commit
60e7dd9ca9
|
@ -85,6 +85,7 @@
|
|||
"electron-connect-webpack-plugin": "^0.1.1",
|
||||
"electron-debug": "^2.0.0",
|
||||
"electron-log": "^2.2.17",
|
||||
"electron-updater": "^3.1.2",
|
||||
"fs-extra": "^7.0.0",
|
||||
"ip-cidr": "^2.0.0",
|
||||
"jasmine-core": "~2.99.1",
|
||||
|
@ -113,4 +114,4 @@
|
|||
"webpack-node-externals": "^1.7.2",
|
||||
"zone.js": "^0.8.26"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
138
src/electron/electron-updater.ts
Normal file
138
src/electron/electron-updater.ts
Normal file
|
@ -0,0 +1,138 @@
|
|||
import { app, Menu, ipcMain, BrowserWindow, shell } from 'electron';
|
||||
import * as log from 'electron-log';
|
||||
import { autoUpdater } from 'electron-updater';
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Logging
|
||||
//
|
||||
// THIS SECTION IS NOT REQUIRED
|
||||
//
|
||||
// This logging setup is not required for auto-updates to work,
|
||||
// but it sure makes debugging easier :)
|
||||
// -------------------------------------------------------------------
|
||||
autoUpdater.logger = log;
|
||||
log.info('App starting...');
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Define the menu
|
||||
//
|
||||
// THIS SECTION IS NOT REQUIRED
|
||||
// -------------------------------------------------------------------
|
||||
const template = [];
|
||||
if (process.platform === 'darwin') {
|
||||
// OS X
|
||||
const name = app.getName();
|
||||
template.unshift({
|
||||
label: name,
|
||||
submenu: [
|
||||
{
|
||||
label: 'About ' + name,
|
||||
role: 'about'
|
||||
},
|
||||
{
|
||||
label: 'Quit',
|
||||
accelerator: 'Command+Q',
|
||||
click() { app.quit(); }
|
||||
},
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Open a window that displays the version
|
||||
//
|
||||
// THIS SECTION IS NOT REQUIRED
|
||||
//
|
||||
// This isn't required for auto-updates to work, but it's easier
|
||||
// for the app to show a window than to have to click "About" to see
|
||||
// that updates are working.
|
||||
// -------------------------------------------------------------------
|
||||
let win;
|
||||
|
||||
function sendStatusToWindow(text) {
|
||||
log.info(text);
|
||||
win.webContents.send('message', text);
|
||||
}
|
||||
function createDefaultWindow() {
|
||||
win = new BrowserWindow();
|
||||
win.webContents.openDevTools();
|
||||
win.on('closed', () => {
|
||||
win = null;
|
||||
});
|
||||
win.loadURL(`file://${__dirname}/version.html#v${app.getVersion()}`);
|
||||
return win;
|
||||
}
|
||||
autoUpdater.on('checking-for-update', () => {
|
||||
sendStatusToWindow('Checking for update...');
|
||||
});
|
||||
autoUpdater.on('update-available', (info) => {
|
||||
sendStatusToWindow('Update available.');
|
||||
});
|
||||
autoUpdater.on('update-not-available', (info) => {
|
||||
sendStatusToWindow('Update not available.');
|
||||
});
|
||||
autoUpdater.on('error', (err) => {
|
||||
sendStatusToWindow('Error in auto-updater. ' + err);
|
||||
});
|
||||
autoUpdater.on('download-progress', (progressObj) => {
|
||||
let log_message = 'Download speed: ' + progressObj.bytesPerSecond;
|
||||
log_message = log_message + ' - Downloaded ' + progressObj.percent + '%';
|
||||
log_message = log_message + ' (' + progressObj.transferred + '/' + progressObj.total + ')';
|
||||
sendStatusToWindow(log_message);
|
||||
});
|
||||
autoUpdater.on('update-downloaded', (info) => {
|
||||
sendStatusToWindow('Update downloaded');
|
||||
});
|
||||
app.on('ready', function () {
|
||||
// Create the Menu
|
||||
const menu = Menu.buildFromTemplate(template);
|
||||
Menu.setApplicationMenu(menu);
|
||||
|
||||
createDefaultWindow();
|
||||
});
|
||||
app.on('window-all-closed', () => {
|
||||
app.quit();
|
||||
});
|
||||
|
||||
//
|
||||
// CHOOSE one of the following options for Auto updates
|
||||
//
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Auto updates - Option 1 - Simplest version
|
||||
//
|
||||
// This will immediately download an update, then install when the
|
||||
// app quits.
|
||||
// -------------------------------------------------------------------
|
||||
app.on('ready', function () {
|
||||
autoUpdater.checkForUpdatesAndNotify();
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Auto updates - Option 2 - More control
|
||||
//
|
||||
// For details about these events, see the Wiki:
|
||||
// https://github.com/electron-userland/electron-builder/wiki/Auto-Update#events
|
||||
//
|
||||
// The app doesn't need to listen to any events except `update-downloaded`
|
||||
//
|
||||
// Uncomment any of the below events to listen for them. Also,
|
||||
// look in the previous section to see them being used.
|
||||
// -------------------------------------------------------------------
|
||||
// app.on('ready', function() {
|
||||
// autoUpdater.checkForUpdates();
|
||||
// });
|
||||
// autoUpdater.on('checking-for-update', () => {
|
||||
// })
|
||||
// autoUpdater.on('update-available', (info) => {
|
||||
// })
|
||||
// autoUpdater.on('update-not-available', (info) => {
|
||||
// })
|
||||
// autoUpdater.on('error', (err) => {
|
||||
// })
|
||||
// autoUpdater.on('download-progress', (progressObj) => {
|
||||
// })
|
||||
// autoUpdater.on('update-downloaded', (info) => {
|
||||
// autoUpdater.quitAndInstall();
|
||||
// })
|
|
@ -18,8 +18,6 @@ import { IMenuItem } from '@overflow/core/menu-item';
|
|||
import { root } from './app-path';
|
||||
import { AppWindow } from './app-window';
|
||||
|
||||
import { handleSquirrelEvent } from './squirrel-updater';
|
||||
|
||||
import { openDirectorySafe } from './shell';
|
||||
import { buildDefaultMenu } from './menu/build-default';
|
||||
import { MenuEvent } from '../commons/type';
|
||||
|
|
|
@ -1,156 +0,0 @@
|
|||
import * as Path from 'path';
|
||||
import * as Os from 'os';
|
||||
|
||||
import { pathExists, ensureDir, writeFile } from 'fs-extra';
|
||||
import { spawn, getPathSegments, setPathSegments } from '@overflow/core/process/win32';
|
||||
|
||||
const appFolder = Path.resolve(process.execPath, '..');
|
||||
const rootAppDir = Path.resolve(appFolder, '..');
|
||||
const updateDotExe = Path.resolve(Path.join(rootAppDir, 'Update.exe'));
|
||||
const exeName = Path.basename(process.execPath);
|
||||
|
||||
// A lot of this code was cargo-culted from our Atom comrades:
|
||||
// https://github.com/atom/atom/blob/7c9f39e3f1d05ee423e0093e6b83f042ce11c90a/src/main-process/squirrel-update.coffee.
|
||||
|
||||
/**
|
||||
* Handle Squirrel.Windows app lifecycle events.
|
||||
*
|
||||
* Returns a promise which will resolve when the work is done.
|
||||
*/
|
||||
export function handleSquirrelEvent(eventName: string): Promise<void> | null {
|
||||
switch (eventName) {
|
||||
case '--squirrel-install':
|
||||
return handleInstalled();
|
||||
|
||||
case '--squirrel-updated':
|
||||
return handleUpdated();
|
||||
|
||||
case '--squirrel-uninstall':
|
||||
return handleUninstall();
|
||||
|
||||
case '--squirrel-obsolete':
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async function handleInstalled(): Promise<void> {
|
||||
await createShortcut(['StartMenu', 'Desktop']);
|
||||
await installCLI();
|
||||
}
|
||||
|
||||
async function handleUpdated(): Promise<void> {
|
||||
await updateShortcut();
|
||||
await installCLI();
|
||||
}
|
||||
|
||||
async function installCLI(): Promise<void> {
|
||||
const binPath = getBinPath();
|
||||
await ensureDir(binPath);
|
||||
await writeBatchScriptCLITrampoline(binPath);
|
||||
await writeShellScriptCLITrampoline(binPath);
|
||||
const paths = await getPathSegments();
|
||||
if (paths.indexOf(binPath) < 0) {
|
||||
await setPathSegments([...paths, binPath]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path for the `bin` directory which exists in our `AppData` but
|
||||
* outside path which includes the installed app version.
|
||||
*/
|
||||
function getBinPath(): string {
|
||||
return Path.resolve(process.execPath, '../../bin');
|
||||
}
|
||||
|
||||
function resolveVersionedPath(binPath: string, relativePath: string): string {
|
||||
const _appFolder = Path.resolve(process.execPath, '..');
|
||||
return Path.relative(binPath, Path.join(_appFolder, relativePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Here's the problem: our app's path contains its version number. So each time
|
||||
* we update, the path to our app changes. So it's Real Hard to add our path
|
||||
* directly to `Path`. We'd have to detect and remove stale entries, etc.
|
||||
*
|
||||
* So instead, we write a trampoline out to a fixed path, still inside our
|
||||
* `AppData` directory but outside the version-specific path. That trampoline
|
||||
* just launches the current version's CLI tool. Then, whenever we update, we
|
||||
* rewrite the trampoline to point to the new, version-specific path. Bingo
|
||||
* bango Bob's your uncle.
|
||||
*/
|
||||
function writeBatchScriptCLITrampoline(binPath: string): Promise<void> {
|
||||
const versionedPath = resolveVersionedPath(
|
||||
binPath,
|
||||
'resources/app/static/github.bat'
|
||||
);
|
||||
|
||||
const trampoline = `@echo off\n"%~dp0\\${versionedPath}" %*`;
|
||||
const trampolinePath = Path.join(binPath, 'github.bat');
|
||||
|
||||
return writeFile(trampolinePath, trampoline);
|
||||
}
|
||||
|
||||
function writeShellScriptCLITrampoline(binPath: string): Promise<void> {
|
||||
const versionedPath = resolveVersionedPath(
|
||||
binPath,
|
||||
'resources/app/static/github.sh'
|
||||
);
|
||||
|
||||
const trampoline = `#!/usr/bin/env bash
|
||||
DIR="$( cd "$( dirname "\$\{BASH_SOURCE[0]\}" )" && pwd )"
|
||||
sh "$DIR/${versionedPath}" "$@"`;
|
||||
const trampolinePath = Path.join(binPath, 'github');
|
||||
|
||||
return writeFile(trampolinePath, trampoline, { encoding: 'utf8', mode: 755 });
|
||||
}
|
||||
|
||||
/** Spawn the Squirrel.Windows `Update.exe` with a command. */
|
||||
async function spawnSquirrelUpdate(
|
||||
commands: ReadonlyArray<string>
|
||||
): Promise<void> {
|
||||
await spawn(updateDotExe, commands);
|
||||
}
|
||||
|
||||
type ShortcutLocations = ReadonlyArray<'StartMenu' | 'Desktop'>;
|
||||
|
||||
function createShortcut(locations: ShortcutLocations): Promise<void> {
|
||||
return spawnSquirrelUpdate([
|
||||
'--createShortcut',
|
||||
exeName,
|
||||
'-l',
|
||||
locations.join(','),
|
||||
]);
|
||||
}
|
||||
|
||||
async function handleUninstall(): Promise<void> {
|
||||
await removeShortcut();
|
||||
|
||||
const paths = await getPathSegments();
|
||||
const binPath = getBinPath();
|
||||
const pathsWithoutBinPath = paths.filter(p => p !== binPath);
|
||||
return setPathSegments(pathsWithoutBinPath);
|
||||
}
|
||||
|
||||
function removeShortcut(): Promise<void> {
|
||||
return spawnSquirrelUpdate(['--removeShortcut', exeName]);
|
||||
}
|
||||
|
||||
async function updateShortcut(): Promise<void> {
|
||||
const homeDirectory = Os.homedir();
|
||||
if (homeDirectory) {
|
||||
const desktopShortcutPath = Path.join(
|
||||
homeDirectory,
|
||||
'Desktop',
|
||||
'overFlow Scanner.lnk'
|
||||
);
|
||||
const exists = await pathExists(desktopShortcutPath);
|
||||
const locations: ShortcutLocations = exists
|
||||
? ['StartMenu', 'Desktop']
|
||||
: ['StartMenu'];
|
||||
return createShortcut(locations);
|
||||
} else {
|
||||
return createShortcut(['StartMenu', 'Desktop']);
|
||||
}
|
||||
}
|
22
yarn.lock
22
yarn.lock
|
@ -1630,7 +1630,7 @@ buffer@^4.3.0:
|
|||
ieee754 "^1.1.4"
|
||||
isarray "^1.0.0"
|
||||
|
||||
builder-util-runtime@4.4.1, builder-util-runtime@^4.4.1:
|
||||
builder-util-runtime@4.4.1, builder-util-runtime@^4.4.1, builder-util-runtime@~4.4.1:
|
||||
version "4.4.1"
|
||||
resolved "https://nexus.loafle.net/repository/npm-all/builder-util-runtime/-/builder-util-runtime-4.4.1.tgz#2770d03241e51fde46acacc7ed3ed8a9f45f02cb"
|
||||
integrity sha512-8L2pbL6D3VdI1f8OMknlZJpw0c7KK15BRz3cY77AOUElc4XlCv2UhVV01jJM7+6Lx7henaQh80ALULp64eFYAQ==
|
||||
|
@ -3154,6 +3154,21 @@ electron-to-chromium@^1.3.47:
|
|||
resolved "https://nexus.loafle.net/repository/npm-all/electron-to-chromium/-/electron-to-chromium-1.3.79.tgz#774718f06284a4bf8f578ac67e74508fe659f13a"
|
||||
integrity sha512-LQdY3j4PxuUl6xfxiFruTSlCniTrTrzAd8/HfsLEMi0PUpaQ0Iy+Pr4N4VllDYjs0Hyu2lkTbvzqlG+PX9NsNw==
|
||||
|
||||
electron-updater@^3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://nexus.loafle.net/repository/npm-all/electron-updater/-/electron-updater-3.1.2.tgz#30d9790d5b16048e4afc74a31748790348b153bf"
|
||||
integrity sha512-y3n37O01pdynMJHhJbOd2UVhVrmDW6zLvR2SOZ+gk3S6r16872+0nNbC48GXWwc26lTeus/Zja/XUpiqrvdw4A==
|
||||
dependencies:
|
||||
bluebird-lst "^1.0.5"
|
||||
builder-util-runtime "~4.4.1"
|
||||
electron-is-dev "^0.3.0"
|
||||
fs-extra-p "^4.6.1"
|
||||
js-yaml "^3.12.0"
|
||||
lazy-val "^1.0.3"
|
||||
lodash.isequal "^4.5.0"
|
||||
semver "^5.5.1"
|
||||
source-map-support "^0.5.9"
|
||||
|
||||
electron-window-state@^5.0.1:
|
||||
version "5.0.2"
|
||||
resolved "https://nexus.loafle.net/repository/npm-all/electron-window-state/-/electron-window-state-5.0.2.tgz#dfc4f7dd0ca2d7116d1e246acf1683b0bdfd45c2"
|
||||
|
@ -5545,6 +5560,11 @@ lodash.find@^4.6.0:
|
|||
resolved "https://nexus.loafle.net/repository/npm-all/lodash.find/-/lodash.find-4.6.0.tgz#cb0704d47ab71789ffa0de8b97dd926fb88b13b1"
|
||||
integrity sha1-ywcE1Hq3F4n/oN6Ll92Sb7iLE7E=
|
||||
|
||||
lodash.isequal@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://nexus.loafle.net/repository/npm-all/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
|
||||
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
|
||||
|
||||
lodash.max@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://nexus.loafle.net/repository/npm-all/lodash.max/-/lodash.max-4.0.1.tgz#8735566c618b35a9f760520b487ae79658af136a"
|
||||
|
|
Loading…
Reference in New Issue
Block a user