Fix upgrade not working (#1286)

* Attempt to get upgrade working right; in progress

* Got it fixed in Mac

* Fix some linting errors

* Finish fixing upgrade + tests

* Integration testing for global shortcuts

* Regenerate shrinkwrap

* Get rid of deprecated rmdirSync

* Remove instead of rm for 12.x support

* Make dereferencing platform dependent

* Fix folder copy funkiness

* Whoops

* Whoops 2: Extra Whoops

* Update Electron to 13.5.1; Fix auth manual tests

* Rework relock

* Add a request for help.

* Update @types/node to 14
This commit is contained in:
Adam Weeden 2021-11-29 12:01:20 -05:00 committed by GitHub
parent adcf7c4c0c
commit b9c5e2b464
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1165 additions and 841 deletions

View File

@ -235,9 +235,9 @@
"dev": true "dev": true
}, },
"node_modules/debug": { "node_modules/debug": {
"version": "4.3.2", "version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"ms": "2.1.2" "ms": "2.1.2"
@ -1375,9 +1375,9 @@
"dev": true "dev": true
}, },
"debug": { "debug": {
"version": "4.3.2", "version": "4.3.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz",
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"ms": "2.1.2" "ms": "2.1.2"

1699
npm-shrinkwrap.json generated

File diff suppressed because it is too large Load Diff

View File

@ -43,7 +43,7 @@
"lint": "eslint shared app src --ext .ts", "lint": "eslint shared app src --ext .ts",
"list-outdated-deps": "npm out; cd app && npm out; true", "list-outdated-deps": "npm out; cd app && npm out; true",
"prepare": "cd app && npm ci && cd .. && npm run build", "prepare": "cd app && npm ci && cd .. && npm run build",
"relock": "rm -rf ./node_modules/ ./app/node_modules/ ./npm-shrinkwrap.json ./app/npm-shrinkwrap.json; npm install --ignore-scripts --package-lock && mv package-lock.json npm-shrinkwrap.json && npm out; cd app && npm install --ignore-scripts --package-lock && mv package-lock.json npm-shrinkwrap.json && npm out; cd .. && true", "relock": "rm -rf ./node_modules/ ./app/node_modules/ ./npm-shrinkwrap.json ./app/npm-shrinkwrap.json && npm install --ignore-scripts --package-lock && mv package-lock.json npm-shrinkwrap.json && npm out && cd app && npm install --ignore-scripts --package-lock && mv package-lock.json npm-shrinkwrap.json && npm out",
"test:integration": "jest --testRegex '.*integration-test.js'", "test:integration": "jest --testRegex '.*integration-test.js'",
"test:manual": "npm run build && ./.github/manual-test", "test:manual": "npm run build && ./.github/manual-test",
"test:unit": "jest", "test:unit": "jest",
@ -55,6 +55,7 @@
"dependencies": { "dependencies": {
"axios": "^0.24.0", "axios": "^0.24.0",
"electron-packager": "^15.2.0", "electron-packager": "^15.2.0",
"fs-extra": "^10.0.0",
"gitcloud": "^0.2.3", "gitcloud": "^0.2.3",
"hasbin": "^1.2.3", "hasbin": "^1.2.3",
"loglevel": "^1.7.1", "loglevel": "^1.7.1",
@ -67,10 +68,11 @@
}, },
"devDependencies": { "devDependencies": {
"@types/debug": "^4.1.6", "@types/debug": "^4.1.6",
"@types/fs-extra": "^9.0.13",
"@types/hasbin": "^1.2.0", "@types/hasbin": "^1.2.0",
"@types/jest": "^27.0.1", "@types/jest": "^27.0.1",
"@types/ncp": "^2.0.5", "@types/ncp": "^2.0.5",
"@types/node": "^12.20.16", "@types/node": "14.14.20",
"@types/page-icon": "^0.3.4", "@types/page-icon": "^0.3.4",
"@types/tmp": "^0.2.1", "@types/tmp": "^0.2.1",
"@typescript-eslint/eslint-plugin": "^5.3.0", "@typescript-eslint/eslint-plugin": "^5.3.0",

View File

@ -183,7 +183,7 @@ export type RawOptions = {
singleInstance?: boolean; singleInstance?: boolean;
targetUrl?: string; targetUrl?: string;
titleBarStyle?: TitleBarValue; titleBarStyle?: TitleBarValue;
tray: TrayValue; tray?: TrayValue;
upgrade?: string | boolean; upgrade?: string | boolean;
upgradeFrom?: string; upgradeFrom?: string;
userAgent?: string; userAgent?: string;

View File

@ -1,12 +1,13 @@
import * as path from 'path'; import * as path from 'path';
import * as electronGet from '@electron/get'; import * as electronGet from '@electron/get';
import * as chalk from 'chalk';
import electronPackager from 'electron-packager'; import electronPackager from 'electron-packager';
import * as fs from 'fs-extra';
import * as log from 'loglevel'; import * as log from 'loglevel';
import { convertIconIfNecessary } from './buildIcon'; import { convertIconIfNecessary } from './buildIcon';
import { import {
copyFileOrDir,
getTempDir, getTempDir,
hasWine, hasWine,
isWindows, isWindows,
@ -49,7 +50,7 @@ async function copyIconsIfNecessary(
log.debug('Copying icon for tray application'); log.debug('Copying icon for tray application');
const trayIconFileName = `tray-icon.png`; const trayIconFileName = `tray-icon.png`;
const destIconPath = path.join(appPath, 'icon.png'); const destIconPath = path.join(appPath, 'icon.png');
await copyFileOrDir( await fs.copy(
`${path.dirname(options.packager.icon)}/${trayIconFileName}`, `${path.dirname(options.packager.icon)}/${trayIconFileName}`,
destIconPath, destIconPath,
); );
@ -64,7 +65,7 @@ async function copyIconsIfNecessary(
const destIconPath = path.join(appPath, destFileName); const destIconPath = path.join(appPath, destFileName);
log.debug(`Copying icon ${options.packager.icon} to`, destIconPath); log.debug(`Copying icon ${options.packager.icon} to`, destIconPath);
await copyFileOrDir(options.packager.icon, destIconPath); await fs.copy(options.packager.icon, destIconPath);
} }
/** /**
@ -129,12 +130,22 @@ function trimUnprocessableOptions(options: AppOptions): void {
export async function buildNativefierApp( export async function buildNativefierApp(
rawOptions: RawOptions, rawOptions: RawOptions,
): Promise<string | undefined> { ): Promise<string | undefined> {
log.warn(
new chalk.Instance().yellowBright.bold(
'\n\n Hi! Nativefier is minimally maintained these days, and needs more hands.\n' +
' If you have the time & motivation, help with bugfixes and maintenance is VERY welcome.\n' +
' Please go to https://github.com/nativefier/nativefier and help how you can. Thanks.\n\n',
),
);
log.info('\nProcessing options...'); log.info('\nProcessing options...');
let finalOutDirectory = rawOptions.out ?? process.cwd();
if (isUpgrade(rawOptions)) { if (isUpgrade(rawOptions)) {
log.debug('Attempting to upgrade from', rawOptions.upgradeFrom); log.debug('Attempting to upgrade from', rawOptions.upgradeFrom);
const oldApp = findUpgradeApp(rawOptions.upgradeFrom as string); const oldApp = findUpgradeApp(rawOptions.upgradeFrom as string);
if (oldApp === null) { if (!oldApp) {
throw new Error( throw new Error(
`Could not find an old Nativfier app in "${ `Could not find an old Nativfier app in "${
rawOptions.upgradeFrom as string rawOptions.upgradeFrom as string
@ -143,7 +154,8 @@ export async function buildNativefierApp(
} }
rawOptions = useOldAppOptions(rawOptions, oldApp); rawOptions = useOldAppOptions(rawOptions, oldApp);
if (rawOptions.out === undefined && rawOptions.overwrite) { if (rawOptions.out === undefined && rawOptions.overwrite) {
rawOptions.out = path.dirname(rawOptions.upgradeFrom as string); finalOutDirectory = oldApp.appRoot;
rawOptions.out = getTempDir('appUpgrade', 0o755);
} }
} }
log.debug('rawOptions', rawOptions); log.debug('rawOptions', rawOptions);
@ -172,7 +184,7 @@ export async function buildNativefierApp(
await prepareElectronApp(options.packager.dir, tmpPath, options); await prepareElectronApp(options.packager.dir, tmpPath, options);
log.info('\nConverting icons...'); log.info('\nConverting icons...');
options.packager.dir = tmpPath; // const optionsWithTmpPath = { ...options, dir: tmpPath }; options.packager.dir = tmpPath;
convertIconIfNecessary(options); convertIconIfNecessary(options);
await copyIconsIfNecessary(options, tmpPath); await copyIconsIfNecessary(options, tmpPath);
@ -184,20 +196,64 @@ export async function buildNativefierApp(
const appPathArray = await electronPackager(options.packager); const appPathArray = await electronPackager(options.packager);
log.info('\nFinalizing build...'); log.info('\nFinalizing build...');
const appPath = getAppPath(appPathArray); let appPath = getAppPath(appPathArray);
if (appPath) { if (!appPath) {
let osRunHelp = ''; throw new Error('App Path could not be determined.');
if (options.packager.platform === 'win32') {
osRunHelp = `the contained .exe file.`;
} else if (options.packager.platform === 'linux') {
osRunHelp = `the contained executable file (prefixing with ./ if necessary)\nMenu/desktop shortcuts are up to you, because Nativefier cannot know where you're going to move the app. Search for "linux .desktop file" for help, or see https://wiki.archlinux.org/index.php/Desktop_entries`;
} else if (options.packager.platform === 'darwin') {
osRunHelp = `the app bundle.`;
}
log.info(
`App built to ${appPath}, move to wherever it makes sense for you and run ${osRunHelp}`,
);
} }
if (
options.packager.upgrade &&
options.packager.upgradeFrom &&
options.packager.overwrite
) {
if (options.packager.platform === 'darwin') {
try {
// This is needed due to a funky thing that happens when copying Squirrel.framework
// over where it gets into a circular file reference somehow.
await fs.remove(
path.join(
finalOutDirectory,
`${options.packager.name ?? ''}.app`,
'Contents',
'Frameworks',
),
);
} catch (err: unknown) {
log.warn(
'Encountered an error when attempting to pre-delete old frameworks:',
err,
);
}
await fs.copy(
path.join(appPath, `${options.packager.name ?? ''}.app`),
path.join(finalOutDirectory, `${options.packager.name ?? ''}.app`),
{
overwrite: options.packager.overwrite,
preserveTimestamps: true,
},
);
} else {
await fs.copy(appPath, finalOutDirectory, {
overwrite: options.packager.overwrite,
preserveTimestamps: true,
});
}
await fs.remove(appPath);
appPath = finalOutDirectory;
}
let osRunHelp = '';
if (options.packager.platform === 'win32') {
osRunHelp = `the contained .exe file.`;
} else if (options.packager.platform === 'linux') {
osRunHelp = `the contained executable file (prefixing with ./ if necessary)\nMenu/desktop shortcuts are up to you, because Nativefier cannot know where you're going to move the app. Search for "linux .desktop file" for help, or see https://wiki.archlinux.org/index.php/Desktop_entries`;
} else if (options.packager.platform === 'darwin') {
osRunHelp = `the app bundle.`;
}
log.info(
`App built to ${appPath}, move to wherever it makes sense for you and run ${osRunHelp}`,
);
return appPath; return appPath;
} }

View File

@ -1,11 +1,10 @@
import * as crypto from 'crypto'; import * as crypto from 'crypto';
import * as fs from 'fs'; import * as fs from 'fs-extra';
import * as path from 'path'; import * as path from 'path';
import { promisify } from 'util';
import * as log from 'loglevel'; import * as log from 'loglevel';
import { copyFileOrDir, generateRandomSuffix } from '../helpers/helpers'; import { generateRandomSuffix } from '../helpers/helpers';
import { import {
AppOptions, AppOptions,
OutputOptions, OutputOptions,
@ -14,8 +13,6 @@ import {
import { parseJson } from '../utils/parseUtils'; import { parseJson } from '../utils/parseUtils';
import { DEFAULT_APP_NAME } from '../constants'; import { DEFAULT_APP_NAME } from '../constants';
const writeFileAsync = promisify(fs.writeFile);
/** /**
* Only picks certain app args to pass to nativefier.json * Only picks certain app args to pass to nativefier.json
*/ */
@ -133,7 +130,7 @@ async function maybeCopyScripts(
const destFileName = `inject-${postFixHash}${path.extname(src)}`; const destFileName = `inject-${postFixHash}${path.extname(src)}`;
const destPath = path.join(dest, 'inject', destFileName); const destPath = path.join(dest, 'inject', destFileName);
log.debug(`Copying injection file "${src}" to "${destPath}"`); log.debug(`Copying injection file "${src}" to "${destPath}"`);
await copyFileOrDir(src, destPath); await fs.copy(src, destPath);
} }
} }
@ -186,7 +183,7 @@ export async function prepareElectronApp(
): Promise<void> { ): Promise<void> {
log.debug(`Copying electron app from ${src} to ${dest}`); log.debug(`Copying electron app from ${src} to ${dest}`);
try { try {
await copyFileOrDir(src, dest); await fs.copy(src, dest);
} catch (err: unknown) { } catch (err: unknown) {
throw `Error copying electron app from ${src} to temp dir ${dest}. Error: ${ throw `Error copying electron app from ${src} to temp dir ${dest}. Error: ${
(err as Error).message (err as Error).message
@ -194,16 +191,14 @@ export async function prepareElectronApp(
} }
const appJsonPath = path.join(dest, '/nativefier.json'); const appJsonPath = path.join(dest, '/nativefier.json');
log.debug(`Writing app config to ${appJsonPath}`); const pickedOptions = pickElectronAppArgs(options);
await writeFileAsync( log.debug(`Writing app config to ${appJsonPath}`, pickedOptions);
appJsonPath, await fs.writeFile(appJsonPath, JSON.stringify(pickedOptions));
JSON.stringify(pickElectronAppArgs(options), null, 2),
);
if (options.nativefier.bookmarksMenu) { if (options.nativefier.bookmarksMenu) {
const bookmarksJsonPath = path.join(dest, '/bookmarks.json'); const bookmarksJsonPath = path.join(dest, '/bookmarks.json');
try { try {
await copyFileOrDir(options.nativefier.bookmarksMenu, bookmarksJsonPath); await fs.copy(options.nativefier.bookmarksMenu, bookmarksJsonPath);
} catch (err: unknown) { } catch (err: unknown) {
log.error('Error copying bookmarks menu config file.', err); log.error('Error copying bookmarks menu config file.', err);
} }

View File

@ -638,6 +638,26 @@ if (require.main === module) {
...parsedArgs, ...parsedArgs,
}; };
if (options.verbose) {
log.setLevel('trace');
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
require('debug').enable('electron-packager');
} catch (err: unknown) {
log.debug(
'Failed to enable electron-packager debug output. This should not happen,',
'and suggests their internals changed. Please report an issue.',
);
}
log.debug(
'Running in verbose mode! This will produce a mountain of logs and',
'is recommended only for troubleshooting or if you like Shakespeare.',
);
} else {
log.setLevel('info');
}
checkInternet(); checkInternet();
buildNativefierApp(options).catch((error) => { buildNativefierApp(options).catch((error) => {

View File

@ -7,7 +7,6 @@ import axios from 'axios';
import * as dns from 'dns'; import * as dns from 'dns';
import * as hasbin from 'hasbin'; import * as hasbin from 'hasbin';
import * as log from 'loglevel'; import * as log from 'loglevel';
import { ncp } from 'ncp';
import * as tmp from 'tmp'; import * as tmp from 'tmp';
import { parseJson } from '../utils/parseUtils'; import { parseJson } from '../utils/parseUtils';
@ -58,20 +57,6 @@ export function getTempDir(prefix: string, mode?: number): string {
}).name; }).name;
} }
export async function copyFileOrDir(
sourceFileOrDir: string,
dest: string,
): Promise<void> {
return new Promise((resolve, reject) => {
ncp(sourceFileOrDir, dest, (error: Error[] | null): void => {
if (error) {
reject(error);
}
resolve();
});
});
}
export function downloadFile( export function downloadFile(
fileUrl: string, fileUrl: string,
): Promise<DownloadResult | undefined> { ): Promise<DownloadResult | undefined> {

View File

@ -36,7 +36,6 @@ function getExecutableArch(
case 'darwin': case 'darwin':
case 'mas': case 'mas':
// https://opensource.apple.com/source/xnu/xnu-2050.18.24/EXTERNAL_HEADERS/mach-o/loader.h // https://opensource.apple.com/source/xnu/xnu-2050.18.24/EXTERNAL_HEADERS/mach-o/loader.h
switch ((exeBytes[0x04] << 8) + exeBytes[0x05]) { switch ((exeBytes[0x04] << 8) + exeBytes[0x05]) {
case 0x0700: case 0x0700:
return 'x64'; return 'x64';
@ -45,7 +44,7 @@ function getExecutableArch(
default: default:
return undefined; return undefined;
} }
case 'windows': case 'win32':
// https://en.wikibooks.org/wiki/X86_Disassembly/Windows_Executable_Files#COFF_Header // https://en.wikibooks.org/wiki/X86_Disassembly/Windows_Executable_Files#COFF_Header
switch ((exeBytes[0x7d] << 8) + exeBytes[0x7c]) { switch ((exeBytes[0x7d] << 8) + exeBytes[0x7c]) {
case 0x014c: case 0x014c:
@ -118,7 +117,7 @@ export function getOptionsFromExecutable(
log.debug('This looks like a Windows app...'); log.debug('This looks like a Windows app...');
if (newOptions.platform === undefined) { if (newOptions.platform === undefined) {
newOptions.platform = 'windows'; newOptions.platform = 'win32';
} }
executablePath = path.join( executablePath = path.join(
appRoot, appRoot,

View File

@ -14,6 +14,7 @@ import { parseJson } from '../../utils/parseUtils';
export type UpgradeAppInfo = { export type UpgradeAppInfo = {
appResourcesDir: string; appResourcesDir: string;
appRoot: string;
options: NativefierOptions; options: NativefierOptions;
}; };
@ -40,6 +41,25 @@ function findUpgradeAppResourcesDir(searchDir: string): string | null {
return null; return null;
} }
function getAppRoot(
appResourcesDir: string,
options: NativefierOptions,
): string {
switch (options.platform) {
case 'darwin':
return path.resolve(path.join(appResourcesDir, '..', '..', '..', '..'));
case 'linux':
case 'win32':
return path.resolve(path.join(appResourcesDir, '..', '..'));
default:
throw new Error(
`Could not find the app root for platform: ${
options.platform ?? 'undefined'
}`,
);
}
}
function getIconPath(appResourcesDir: string): string | undefined { function getIconPath(appResourcesDir: string): string | undefined {
const icnsPath = path.join(appResourcesDir, '..', 'electron.icns'); const icnsPath = path.join(appResourcesDir, '..', 'electron.icns');
if (fileExists(icnsPath)) { if (fileExists(icnsPath)) {
@ -163,7 +183,7 @@ export function findUpgradeApp(upgradeFrom: string): UpgradeAppInfo | null {
const nativefierJSONPath = path.join(appResourcesDir, 'nativefier.json'); const nativefierJSONPath = path.join(appResourcesDir, 'nativefier.json');
log.debug(`Loading ${nativefierJSONPath}`); log.debug(`Loading ${nativefierJSONPath}`);
const options = parseJson<NativefierOptions>( let options = parseJson<NativefierOptions>(
fs.readFileSync(nativefierJSONPath, 'utf8'), fs.readFileSync(nativefierJSONPath, 'utf8'),
); );
@ -175,11 +195,18 @@ export function findUpgradeApp(upgradeFrom: string): UpgradeAppInfo | null {
options.electronVersion = undefined; options.electronVersion = undefined;
options = {
...options,
...getOptionsFromExecutable(appResourcesDir, options),
};
const appRoot = getAppRoot(appResourcesDir, options);
return { return {
appResourcesDir, appResourcesDir,
appRoot,
options: { options: {
...options, ...options,
...getOptionsFromExecutable(appResourcesDir, options),
...getInfoPListOptions(appResourcesDir, options), ...getInfoPListOptions(appResourcesDir, options),
asar: options.asar !== undefined ? options.asar : isAsar(appResourcesDir), asar: options.asar !== undefined ? options.asar : isAsar(appResourcesDir),
icon: getIconPath(appResourcesDir), icon: getIconPath(appResourcesDir),
@ -197,7 +224,6 @@ export function useOldAppOptions(
rawOptions.out = rawOptions.targetUrl; rawOptions.out = rawOptions.targetUrl;
} }
log.debug('rawOptions', rawOptions);
log.debug('oldApp', oldApp); log.debug('oldApp', oldApp);
const combinedOptions = { ...rawOptions, ...oldApp.options }; const combinedOptions = { ...rawOptions, ...oldApp.options };

View File

@ -14,6 +14,7 @@ export const supportedPlatforms = [
'mac', 'mac',
'mas', 'mas',
'osx', 'osx',
'win32',
'windows', 'windows',
]; ];

View File

@ -10,7 +10,11 @@ import { getLatestSafariVersion } from './infer/browsers/inferSafariVersion';
import { inferArch } from './infer/inferOs'; import { inferArch } from './infer/inferOs';
import { buildNativefierApp } from './main'; import { buildNativefierApp } from './main';
import { userAgent } from './options/fields/userAgent'; import { userAgent } from './options/fields/userAgent';
import { NativefierOptions, RawOptions } from '../shared/src/options/model'; import {
GlobalShortcut,
NativefierOptions,
RawOptions,
} from '../shared/src/options/model';
import { parseJson } from './utils/parseUtils'; import { parseJson } from './utils/parseUtils';
async function checkApp( async function checkApp(
@ -27,25 +31,13 @@ async function checkApp(
).toBe(appRoot); ).toBe(appRoot);
} }
let relativeAppFolder: string; let relativeResourcesDir = 'resources';
switch (inputOptions.platform) { if (inputOptions.platform === 'darwin') {
case 'darwin': relativeResourcesDir = path.join('Google.app', 'Contents', 'Resources');
relativeAppFolder = path.join('Google.app', 'Contents/Resources/app');
break;
case 'linux':
relativeAppFolder = 'resources/app';
break;
case 'win32':
relativeAppFolder = 'resources/app';
break;
default:
throw new Error(
`Unknown app platform: ${new String(inputOptions.platform).toString()}`,
);
} }
const appPath = path.join(appRoot, relativeAppFolder); const appPath = path.join(appRoot, relativeResourcesDir, 'app');
const configPath = path.join(appPath, 'nativefier.json'); const configPath = path.join(appPath, 'nativefier.json');
const nativefierConfig: NativefierOptions | undefined = const nativefierConfig: NativefierOptions | undefined =
@ -59,7 +51,11 @@ async function checkApp(
// Test icon writing // Test icon writing
const iconFile = const iconFile =
inputOptions.platform === 'darwin' ? '../electron.icns' : 'icon.png'; inputOptions.platform === 'darwin'
? path.join('..', 'electron.icns')
: inputOptions.platform === 'linux'
? 'icon.png'
: 'icon.ico';
const iconPath = path.join(appPath, iconFile); const iconPath = path.join(appPath, iconFile);
expect(fs.existsSync(iconPath)).toEqual(true); expect(fs.existsSync(iconPath)).toEqual(true);
expect(fs.statSync(iconPath).size).toBeGreaterThan(1000); expect(fs.statSync(iconPath).size).toBeGreaterThan(1000);
@ -93,6 +89,21 @@ async function checkApp(
// Test lang // Test lang
expect(nativefierConfig?.lang).toEqual(inputOptions.lang); expect(nativefierConfig?.lang).toEqual(inputOptions.lang);
// Test global shortcuts
if (inputOptions.globalShortcuts) {
let shortcutData: GlobalShortcut[] | undefined = [];
if (typeof inputOptions.globalShortcuts === 'string') {
shortcutData = parseJson<GlobalShortcut[]>(
fs.readFileSync(inputOptions.globalShortcuts, 'utf8'),
);
} else {
shortcutData = inputOptions.globalShortcuts;
}
expect(nativefierConfig?.globalShortcuts).toStrictEqual(shortcutData);
}
} }
describe('Nativefier', () => { describe('Nativefier', () => {
@ -108,7 +119,6 @@ describe('Nativefier', () => {
overwrite: true, overwrite: true,
platform, platform,
targetUrl: 'https://google.com/', targetUrl: 'https://google.com/',
tray: 'false',
}; };
const appPath = await buildNativefierApp(options); const appPath = await buildNativefierApp(options);
expect(appPath).not.toBeUndefined(); expect(appPath).not.toBeUndefined();
@ -117,6 +127,34 @@ describe('Nativefier', () => {
); );
}); });
function generateShortcutsFile(dir: string): string {
const shortcuts = [
{
key: 'MediaPlayPause',
inputEvents: [
{
type: 'keyDown',
keyCode: 'Space',
},
],
},
{
key: 'MediaNextTrack',
inputEvents: [
{
type: 'keyDown',
keyCode: 'Right',
},
],
},
];
const filename = path.join(dir, 'shortcuts.json');
fs.writeFileSync(filename, JSON.stringify(shortcuts));
return filename;
}
describe('Nativefier upgrade', () => { describe('Nativefier upgrade', () => {
jest.setTimeout(300000); jest.setTimeout(300000);
@ -135,12 +173,13 @@ describe('Nativefier upgrade', () => {
'can upgrade a Nativefier app for platform/arch: %s', 'can upgrade a Nativefier app for platform/arch: %s',
async (baseAppOptions) => { async (baseAppOptions) => {
const tempDirectory = getTempDir('integtestUpgrade1'); const tempDirectory = getTempDir('integtestUpgrade1');
const shortcuts = generateShortcutsFile(tempDirectory);
const options: RawOptions = { const options: RawOptions = {
electronVersion: '11.2.3', electronVersion: '11.2.3',
globalShortcuts: shortcuts,
out: tempDirectory, out: tempDirectory,
overwrite: true, overwrite: true,
targetUrl: 'https://google.com/', targetUrl: 'https://google.com/',
tray: 'false',
...baseAppOptions, ...baseAppOptions,
}; };
const appPath = await buildNativefierApp(options); const appPath = await buildNativefierApp(options);
@ -148,9 +187,8 @@ describe('Nativefier upgrade', () => {
await checkApp(appPath as string, options); await checkApp(appPath as string, options);
const upgradeOptions: RawOptions = { const upgradeOptions: RawOptions = {
upgrade: appPath, upgrade: appPath as string,
overwrite: true, overwrite: true,
tray: 'false',
}; };
const upgradeAppPath = await buildNativefierApp(upgradeOptions); const upgradeAppPath = await buildNativefierApp(upgradeOptions);

View File

@ -226,11 +226,18 @@ export async function getOptions(rawOptions: RawOptions): Promise<AppOptions> {
} }
if (rawOptions.globalShortcuts) { if (rawOptions.globalShortcuts) {
log.debug('Using global shortcuts file at', rawOptions.globalShortcuts); if (typeof rawOptions.globalShortcuts === 'string') {
const globalShortcuts = parseJson<GlobalShortcut[]>( // This is a file we got over the command line
fs.readFileSync(rawOptions.globalShortcuts as string).toString(), log.debug('Using global shortcuts file at', rawOptions.globalShortcuts);
); const globalShortcuts = parseJson<GlobalShortcut[]>(
options.nativefier.globalShortcuts = globalShortcuts; fs.readFileSync(rawOptions.globalShortcuts).toString(),
);
options.nativefier.globalShortcuts = globalShortcuts;
} else {
// This is an object we got from an existing config in an upgrade
log.debug('Using global shortcuts object', rawOptions.globalShortcuts);
options.nativefier.globalShortcuts = rawOptions.globalShortcuts;
}
} }
await asyncConfig(options); await asyncConfig(options);