mirror of
https://github.com/Llewellynvdm/nativefier.git
synced 2024-12-23 18:48:55 +00:00
This adds a `--upgrade` option to upgrade-in-place an old app, re-using its options it can. Should help fix #1131 Co-authored-by: Ronan Jouchet <ronan@jouchet.fr>
This commit is contained in:
parent
bc6be8445d
commit
9330c8434f
23
docs/api.md
23
docs/api.md
@ -9,6 +9,7 @@
|
|||||||
- [[dest]](#dest)
|
- [[dest]](#dest)
|
||||||
- [Help](#help)
|
- [Help](#help)
|
||||||
- [Version](#version)
|
- [Version](#version)
|
||||||
|
- [[upgrade]](#upgrade)
|
||||||
- [[name]](#name)
|
- [[name]](#name)
|
||||||
- [[platform]](#platform)
|
- [[platform]](#platform)
|
||||||
- [[arch]](#arch)
|
- [[arch]](#arch)
|
||||||
@ -92,9 +93,13 @@ See [PR #744 - Support packaging nativefier applications into Squirrel-based ins
|
|||||||
## Command Line
|
## Command Line
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
nativefier [options] <targetUrl> [dest]
|
nativefier [options] [targetUrl] [dest]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You must provide:
|
||||||
|
- Either a `targetUrl` to generate a new app from it.
|
||||||
|
- Or option `--upgrade <pathOfAppToUpgrade>` to upgrade an existing app.
|
||||||
|
|
||||||
Command line options are listed below.
|
Command line options are listed below.
|
||||||
|
|
||||||
#### Target Url
|
#### Target Url
|
||||||
@ -121,6 +126,22 @@ Prints the usage information.
|
|||||||
|
|
||||||
Prints the version of your `nativefier` install.
|
Prints the version of your `nativefier` install.
|
||||||
|
|
||||||
|
#### [upgrade]
|
||||||
|
|
||||||
|
```
|
||||||
|
--upgrade <pathToExistingApp>
|
||||||
|
```
|
||||||
|
|
||||||
|
*NEW IN 43.1.0*
|
||||||
|
|
||||||
|
This option will attempt to extract all existing options from the old app, and upgrade it using the current Nativefier CLI.
|
||||||
|
|
||||||
|
**IMPORTANT NOTE**
|
||||||
|
|
||||||
|
**This action is an in-place upgrade, and will REPLACE the current application. In case this feature does not work as intended or as the user may wish, it is advised to make a backup of the app to be upgraded before using, or specify an alternate directory as you would when creating a new file.**
|
||||||
|
|
||||||
|
The provided path must be the "executable" of an application packaged with a previous version of Nativefier, and to be upgraded to the latest version of Nativefier. "Executable" means: the `.exe` file on Windows, the executable on Linux, or the `.app` on macOS. The executable must be living in the original context where it was generated (i.e., on Windows and Linux, the exe file must still be in the folder containing the generated `resources` directory).
|
||||||
|
|
||||||
#### [name]
|
#### [name]
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -12,7 +12,8 @@ import {
|
|||||||
isWindows,
|
isWindows,
|
||||||
isWindowsAdmin,
|
isWindowsAdmin,
|
||||||
} from '../helpers/helpers';
|
} from '../helpers/helpers';
|
||||||
import { AppOptions, NativefierOptions } from '../options/model';
|
import { useOldAppOptions, findUpgradeApp } from '../helpers/upgrade/upgrade';
|
||||||
|
import { AppOptions } from '../options/model';
|
||||||
import { getOptions } from '../options/optionsMain';
|
import { getOptions } from '../options/optionsMain';
|
||||||
import { prepareElectronApp } from './prepareElectronApp';
|
import { prepareElectronApp } from './prepareElectronApp';
|
||||||
|
|
||||||
@ -25,28 +26,6 @@ const OPTIONS_REQUIRING_WINDOWS_FOR_WINDOWS_BUILD = [
|
|||||||
'win32metadata',
|
'win32metadata',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks the app path array to determine if packaging completed successfully
|
|
||||||
*/
|
|
||||||
function getAppPath(appPath: string | string[]): string {
|
|
||||||
if (!Array.isArray(appPath)) {
|
|
||||||
return appPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appPath.length === 0) {
|
|
||||||
return null; // directory already exists and `--overwrite` not set
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appPath.length > 1) {
|
|
||||||
log.warn(
|
|
||||||
'Warning: This should not be happening, packaged app path contains more than one element:',
|
|
||||||
appPath,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return appPath[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For Windows & Linux, we have to copy over the icon to the resources/app
|
* For Windows & Linux, we have to copy over the icon to the resources/app
|
||||||
* folder, which the BrowserWindow is hard-coded to read the icon from
|
* folder, which the BrowserWindow is hard-coded to read the icon from
|
||||||
@ -88,6 +67,36 @@ async function copyIconsIfNecessary(
|
|||||||
await copyFileOrDir(options.packager.icon, destIconPath);
|
await copyFileOrDir(options.packager.icon, destIconPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the app path array to determine if packaging completed successfully
|
||||||
|
*/
|
||||||
|
function getAppPath(appPath: string | string[]): string {
|
||||||
|
if (!Array.isArray(appPath)) {
|
||||||
|
return appPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (appPath.length === 0) {
|
||||||
|
return null; // directory already exists and `--overwrite` not set
|
||||||
|
}
|
||||||
|
|
||||||
|
if (appPath.length > 1) {
|
||||||
|
log.warn(
|
||||||
|
'Warning: This should not be happening, packaged app path contains more than one element:',
|
||||||
|
appPath,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return appPath[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
function isUpgrade(rawOptions) {
|
||||||
|
return (
|
||||||
|
rawOptions.upgrade !== undefined &&
|
||||||
|
(rawOptions.upgrade === true ||
|
||||||
|
(typeof rawOptions.upgrade === 'string' && rawOptions.upgrade !== ''))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function trimUnprocessableOptions(options: AppOptions): void {
|
function trimUnprocessableOptions(options: AppOptions): void {
|
||||||
if (
|
if (
|
||||||
options.packager.platform === 'win32' &&
|
options.packager.platform === 'win32' &&
|
||||||
@ -116,11 +125,28 @@ function trimUnprocessableOptions(options: AppOptions): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
export async function buildNativefierApp(
|
export async function buildNativefierApp(rawOptions): Promise<string> {
|
||||||
rawOptions: NativefierOptions,
|
log.info('\nProcessing options...');
|
||||||
): Promise<string> {
|
|
||||||
log.info('Processing options...');
|
if (isUpgrade(rawOptions)) {
|
||||||
|
log.debug('Attempting to upgrade from', rawOptions.upgrade);
|
||||||
|
const oldApp = findUpgradeApp(rawOptions.upgrade.toString());
|
||||||
|
if (oldApp === null) {
|
||||||
|
throw new Error(
|
||||||
|
`Could not find an old Nativfier app in "${
|
||||||
|
rawOptions.upgrade as string
|
||||||
|
}"`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
rawOptions = useOldAppOptions(rawOptions, oldApp);
|
||||||
|
if (rawOptions.out === undefined && rawOptions.overwrite) {
|
||||||
|
rawOptions.out = path.dirname(rawOptions.upgrade);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.debug('rawOptions', rawOptions);
|
||||||
|
|
||||||
const options = await getOptions(rawOptions);
|
const options = await getOptions(rawOptions);
|
||||||
|
log.debug('options', options);
|
||||||
|
|
||||||
if (options.packager.platform === 'darwin' && isWindows()) {
|
if (options.packager.platform === 'darwin' && isWindows()) {
|
||||||
// electron-packager has to extract the desired electron package for the target platform.
|
// electron-packager has to extract the desired electron package for the target platform.
|
||||||
|
@ -17,58 +17,76 @@ function pickElectronAppArgs(options: AppOptions): any {
|
|||||||
return {
|
return {
|
||||||
accessibilityPrompt: options.nativefier.accessibilityPrompt,
|
accessibilityPrompt: options.nativefier.accessibilityPrompt,
|
||||||
alwaysOnTop: options.nativefier.alwaysOnTop,
|
alwaysOnTop: options.nativefier.alwaysOnTop,
|
||||||
|
appBundleId: options.packager.appBundleId,
|
||||||
|
appCategoryType: options.packager.appCategoryType,
|
||||||
appCopyright: options.packager.appCopyright,
|
appCopyright: options.packager.appCopyright,
|
||||||
appVersion: options.packager.appVersion,
|
appVersion: options.packager.appVersion,
|
||||||
|
arch: options.packager.arch,
|
||||||
|
asar: options.packager.asar,
|
||||||
backgroundColor: options.nativefier.backgroundColor,
|
backgroundColor: options.nativefier.backgroundColor,
|
||||||
basicAuthPassword: options.nativefier.basicAuthPassword,
|
basicAuthPassword: options.nativefier.basicAuthPassword,
|
||||||
basicAuthUsername: options.nativefier.basicAuthUsername,
|
basicAuthUsername: options.nativefier.basicAuthUsername,
|
||||||
|
blockExternalUrls: options.nativefier.blockExternalUrls,
|
||||||
bounce: options.nativefier.bounce,
|
bounce: options.nativefier.bounce,
|
||||||
browserwindowOptions: options.nativefier.browserwindowOptions,
|
browserwindowOptions: options.nativefier.browserwindowOptions,
|
||||||
|
buildDate: new Date().getTime(),
|
||||||
buildVersion: options.packager.buildVersion,
|
buildVersion: options.packager.buildVersion,
|
||||||
clearCache: options.nativefier.clearCache,
|
clearCache: options.nativefier.clearCache,
|
||||||
counter: options.nativefier.counter,
|
counter: options.nativefier.counter,
|
||||||
crashReporter: options.nativefier.crashReporter,
|
crashReporter: options.nativefier.crashReporter,
|
||||||
darwinDarkModeSupport: options.packager.darwinDarkModeSupport,
|
darwinDarkModeSupport: options.packager.darwinDarkModeSupport,
|
||||||
|
derefSymlinks: options.packager.derefSymlinks,
|
||||||
disableContextMenu: options.nativefier.disableContextMenu,
|
disableContextMenu: options.nativefier.disableContextMenu,
|
||||||
disableDevTools: options.nativefier.disableDevTools,
|
disableDevTools: options.nativefier.disableDevTools,
|
||||||
disableGpu: options.nativefier.disableGpu,
|
disableGpu: options.nativefier.disableGpu,
|
||||||
|
disableOldBuildWarning: options.nativefier.disableOldBuildWarning,
|
||||||
diskCacheSize: options.nativefier.diskCacheSize,
|
diskCacheSize: options.nativefier.diskCacheSize,
|
||||||
|
download: options.packager.download,
|
||||||
|
electronVersionUsed: options.packager.electronVersion,
|
||||||
enableEs3Apis: options.nativefier.enableEs3Apis,
|
enableEs3Apis: options.nativefier.enableEs3Apis,
|
||||||
|
executableName: options.packager.executableName,
|
||||||
fastQuit: options.nativefier.fastQuit,
|
fastQuit: options.nativefier.fastQuit,
|
||||||
fileDownloadOptions: options.nativefier.fileDownloadOptions,
|
fileDownloadOptions: options.nativefier.fileDownloadOptions,
|
||||||
flashPluginDir: options.nativefier.flashPluginDir,
|
flashPluginDir: options.nativefier.flashPluginDir,
|
||||||
fullScreen: options.nativefier.fullScreen,
|
fullScreen: options.nativefier.fullScreen,
|
||||||
globalShortcuts: options.nativefier.globalShortcuts,
|
globalShortcuts: options.nativefier.globalShortcuts,
|
||||||
height: options.nativefier.height,
|
height: options.nativefier.height,
|
||||||
|
helperBundleId: options.packager.helperBundleId,
|
||||||
hideWindowFrame: options.nativefier.hideWindowFrame,
|
hideWindowFrame: options.nativefier.hideWindowFrame,
|
||||||
ignoreCertificate: options.nativefier.ignoreCertificate,
|
ignoreCertificate: options.nativefier.ignoreCertificate,
|
||||||
ignoreGpuBlacklist: options.nativefier.ignoreGpuBlacklist,
|
ignoreGpuBlacklist: options.nativefier.ignoreGpuBlacklist,
|
||||||
insecure: options.nativefier.insecure,
|
insecure: options.nativefier.insecure,
|
||||||
internalUrls: options.nativefier.internalUrls,
|
internalUrls: options.nativefier.internalUrls,
|
||||||
blockExternalUrls: options.nativefier.blockExternalUrls,
|
isUpgrade: options.packager.upgrade,
|
||||||
maxHeight: options.nativefier.maxHeight,
|
junk: options.packager.junk,
|
||||||
maximize: options.nativefier.maximize,
|
maximize: options.nativefier.maximize,
|
||||||
|
maxHeight: options.nativefier.maxHeight,
|
||||||
maxWidth: options.nativefier.maxWidth,
|
maxWidth: options.nativefier.maxWidth,
|
||||||
minHeight: options.nativefier.minHeight,
|
minHeight: options.nativefier.minHeight,
|
||||||
minWidth: options.nativefier.minWidth,
|
minWidth: options.nativefier.minWidth,
|
||||||
name: options.packager.name,
|
name: options.packager.name,
|
||||||
nativefierVersion: options.nativefier.nativefierVersion,
|
nativefierVersion: options.nativefier.nativefierVersion,
|
||||||
|
osxNotarize: options.packager.osxNotarize,
|
||||||
|
osxSign: options.packager.osxSign,
|
||||||
processEnvs: options.nativefier.processEnvs,
|
processEnvs: options.nativefier.processEnvs,
|
||||||
|
protocols: options.packager.protocols,
|
||||||
proxyRules: options.nativefier.proxyRules,
|
proxyRules: options.nativefier.proxyRules,
|
||||||
|
prune: options.packager.prune,
|
||||||
|
quiet: options.packager.quiet,
|
||||||
showMenuBar: options.nativefier.showMenuBar,
|
showMenuBar: options.nativefier.showMenuBar,
|
||||||
singleInstance: options.nativefier.singleInstance,
|
singleInstance: options.nativefier.singleInstance,
|
||||||
targetUrl: options.packager.targetUrl,
|
targetUrl: options.packager.targetUrl,
|
||||||
titleBarStyle: options.nativefier.titleBarStyle,
|
titleBarStyle: options.nativefier.titleBarStyle,
|
||||||
tray: options.nativefier.tray,
|
tray: options.nativefier.tray,
|
||||||
|
usageDescription: options.packager.usageDescription,
|
||||||
userAgent: options.nativefier.userAgent,
|
userAgent: options.nativefier.userAgent,
|
||||||
|
userAgentOverriden: options.nativefier.userAgentOverriden,
|
||||||
versionString: options.nativefier.versionString,
|
versionString: options.nativefier.versionString,
|
||||||
width: options.nativefier.width,
|
width: options.nativefier.width,
|
||||||
win32metadata: options.packager.win32metadata,
|
win32metadata: options.packager.win32metadata,
|
||||||
disableOldBuildWarning: options.nativefier.disableOldBuildWarning,
|
|
||||||
x: options.nativefier.x,
|
x: options.nativefier.x,
|
||||||
y: options.nativefier.y,
|
y: options.nativefier.y,
|
||||||
zoom: options.nativefier.zoom,
|
zoom: options.nativefier.zoom,
|
||||||
buildDate: new Date().getTime(),
|
|
||||||
// OLD_BUILD_WARNING_TEXT is an undocumented env. var to let *packagers*
|
// OLD_BUILD_WARNING_TEXT is an undocumented env. var to let *packagers*
|
||||||
// tweak the message shown on warning about an old build, to something
|
// tweak the message shown on warning about an old build, to something
|
||||||
// more tailored to their audience (who might not even know Nativefier).
|
// more tailored to their audience (who might not even know Nativefier).
|
||||||
|
20
src/cli.ts
20
src/cli.ts
@ -9,6 +9,7 @@ import * as log from 'loglevel';
|
|||||||
import { isArgFormatInvalid } from './helpers/helpers';
|
import { isArgFormatInvalid } from './helpers/helpers';
|
||||||
import { supportedArchs, supportedPlatforms } from './infer/inferOs';
|
import { supportedArchs, supportedPlatforms } from './infer/inferOs';
|
||||||
import { buildNativefierApp } from './main';
|
import { buildNativefierApp } from './main';
|
||||||
|
import { NativefierOptions } from './options/model';
|
||||||
import { parseBooleanOrString, parseJson } from './utils/parseUtils';
|
import { parseBooleanOrString, parseJson } from './utils/parseUtils';
|
||||||
|
|
||||||
// package.json is `require`d to let tsc strip the `src` folder by determining
|
// package.json is `require`d to let tsc strip the `src` folder by determining
|
||||||
@ -67,12 +68,16 @@ if (require.main === module) {
|
|||||||
const args = commander
|
const args = commander
|
||||||
.name('nativefier')
|
.name('nativefier')
|
||||||
.version(packageJson.version, '-v, --version')
|
.version(packageJson.version, '-v, --version')
|
||||||
.arguments('<targetUrl> [dest]')
|
.arguments('[targetUrl] [dest]')
|
||||||
.action((url, outputDirectory) => {
|
.action((url, outputDirectory) => {
|
||||||
positionalOptions.targetUrl = url;
|
positionalOptions.targetUrl = url;
|
||||||
positionalOptions.out = outputDirectory;
|
positionalOptions.out = outputDirectory;
|
||||||
})
|
})
|
||||||
.option('-n, --name <value>', 'app name')
|
.option('-n, --name <value>', 'app name')
|
||||||
|
.option(
|
||||||
|
'--upgrade <pathToExistingApp>',
|
||||||
|
'Upgrade an app built by an older Nativefier. You must pass the full path to the existing app executable (app will be overwritten with upgraded version by default)',
|
||||||
|
)
|
||||||
.addOption(
|
.addOption(
|
||||||
new commander.Option('-p, --platform <value>').choices(
|
new commander.Option('-p, --platform <value>').choices(
|
||||||
supportedPlatforms,
|
supportedPlatforms,
|
||||||
@ -291,7 +296,18 @@ if (require.main === module) {
|
|||||||
commander.help();
|
commander.help();
|
||||||
}
|
}
|
||||||
checkInternet();
|
checkInternet();
|
||||||
const options = { ...positionalOptions, ...commander.opts() };
|
const options: NativefierOptions = {
|
||||||
|
...positionalOptions,
|
||||||
|
...commander.opts(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!options.targetUrl && !options.upgrade) {
|
||||||
|
console.error(
|
||||||
|
'Nativefier must be called with either a targetUrl or the --upgrade option.',
|
||||||
|
);
|
||||||
|
commander.help();
|
||||||
|
}
|
||||||
|
|
||||||
buildNativefierApp(options).catch((error) => {
|
buildNativefierApp(options).catch((error) => {
|
||||||
log.error('Error during build. Run with --verbose for details.', error);
|
log.error('Error during build. Run with --verbose for details.', error);
|
||||||
});
|
});
|
||||||
|
19
src/helpers/fsHelpers.ts
Normal file
19
src/helpers/fsHelpers.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import * as fs from 'fs';
|
||||||
|
|
||||||
|
export function dirExists(dirName: string): boolean {
|
||||||
|
try {
|
||||||
|
const dirStat = fs.statSync(dirName);
|
||||||
|
return dirStat.isDirectory();
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fileExists(fileName: string): boolean {
|
||||||
|
try {
|
||||||
|
const fileStat = fs.statSync(fileName);
|
||||||
|
return fileStat.isFile();
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
186
src/helpers/upgrade/executableHelpers.ts
Normal file
186
src/helpers/upgrade/executableHelpers.ts
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
import * as log from 'loglevel';
|
||||||
|
|
||||||
|
import { NativefierOptions } from '../../options/model';
|
||||||
|
import { getVersionString } from './rceditGet';
|
||||||
|
import { fileExists } from '../fsHelpers';
|
||||||
|
type ExecutableInfo = {
|
||||||
|
arch?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function getExecutableBytes(executablePath: string): Uint8Array {
|
||||||
|
return fs.readFileSync(executablePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getExecutableArch(
|
||||||
|
exeBytes: Uint8Array,
|
||||||
|
platform: string,
|
||||||
|
): string | undefined {
|
||||||
|
switch (platform) {
|
||||||
|
case 'linux':
|
||||||
|
// https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header
|
||||||
|
switch (exeBytes[0x12]) {
|
||||||
|
case 0x03:
|
||||||
|
return 'ia32';
|
||||||
|
case 0x28:
|
||||||
|
return 'armv7l';
|
||||||
|
case 0x3e:
|
||||||
|
return 'x64';
|
||||||
|
case 0xb7:
|
||||||
|
return 'arm64';
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
case 'darwin':
|
||||||
|
case 'mas':
|
||||||
|
// https://opensource.apple.com/source/xnu/xnu-2050.18.24/EXTERNAL_HEADERS/mach-o/loader.h
|
||||||
|
|
||||||
|
switch ((exeBytes[0x04] << 8) + exeBytes[0x05]) {
|
||||||
|
case 0x0700:
|
||||||
|
return 'x64';
|
||||||
|
case 0x0c00:
|
||||||
|
return 'arm64';
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
case 'windows':
|
||||||
|
// https://en.wikibooks.org/wiki/X86_Disassembly/Windows_Executable_Files#COFF_Header
|
||||||
|
switch ((exeBytes[0x7d] << 8) + exeBytes[0x7c]) {
|
||||||
|
case 0x014c:
|
||||||
|
return 'ia32';
|
||||||
|
case 0x8664:
|
||||||
|
return 'x64';
|
||||||
|
case 0xaa64:
|
||||||
|
return 'arm64';
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getExecutableInfo(
|
||||||
|
executablePath: string,
|
||||||
|
platform: string,
|
||||||
|
): ExecutableInfo {
|
||||||
|
if (!fileExists(executablePath)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const exeBytes = getExecutableBytes(executablePath);
|
||||||
|
return {
|
||||||
|
arch: getExecutableArch(exeBytes, platform),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getOptionsFromExecutable(
|
||||||
|
appResourcesDir: string,
|
||||||
|
priorOptions: NativefierOptions,
|
||||||
|
): NativefierOptions {
|
||||||
|
const newOptions: NativefierOptions = { ...priorOptions };
|
||||||
|
let executablePath: string | undefined = undefined;
|
||||||
|
|
||||||
|
const appRoot = path.resolve(path.join(appResourcesDir, '..', '..'));
|
||||||
|
const children = fs.readdirSync(appRoot, { withFileTypes: true });
|
||||||
|
const looksLikeMacOS =
|
||||||
|
children.filter((c) => c.name === 'MacOS' && c.isDirectory()).length > 0;
|
||||||
|
const looksLikeWindows =
|
||||||
|
children.filter((c) => c.name.toLowerCase().endsWith('.exe') && c.isFile())
|
||||||
|
.length > 0;
|
||||||
|
const looksLikeLinux =
|
||||||
|
children.filter((c) => c.name.toLowerCase().endsWith('.so') && c.isFile())
|
||||||
|
.length > 0;
|
||||||
|
|
||||||
|
if (looksLikeMacOS) {
|
||||||
|
log.debug('This looks like a MacOS app...');
|
||||||
|
if (newOptions.platform === undefined) {
|
||||||
|
newOptions.platform =
|
||||||
|
children.filter((c) => c.name === 'Library' && c.isDirectory()).length >
|
||||||
|
0
|
||||||
|
? 'mas'
|
||||||
|
: 'darwin';
|
||||||
|
}
|
||||||
|
executablePath = path.join(
|
||||||
|
appRoot,
|
||||||
|
'MacOS',
|
||||||
|
fs.readdirSync(path.join(appRoot, 'MacOS'))[0],
|
||||||
|
);
|
||||||
|
} else if (looksLikeWindows) {
|
||||||
|
log.debug('This looks like a Windows app...');
|
||||||
|
|
||||||
|
if (newOptions.platform === undefined) {
|
||||||
|
newOptions.platform = 'windows';
|
||||||
|
}
|
||||||
|
executablePath = path.join(
|
||||||
|
appRoot,
|
||||||
|
children.filter(
|
||||||
|
(c) =>
|
||||||
|
c.name.toLowerCase() === `${newOptions.name.toLowerCase()}.exe` &&
|
||||||
|
c.isFile(),
|
||||||
|
)[0].name,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (newOptions.appVersion === undefined) {
|
||||||
|
// https://github.com/electron/electron-packager/blob/f1c159f4c844d807968078ea504fba40ca7d9c73/src/win32.js#L46-L48
|
||||||
|
newOptions.appVersion = getVersionString(
|
||||||
|
executablePath,
|
||||||
|
'ProductVersion',
|
||||||
|
);
|
||||||
|
log.debug(
|
||||||
|
`Extracted app version from executable: ${newOptions.appVersion}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newOptions.buildVersion === undefined) {
|
||||||
|
//https://github.com/electron/electron-packager/blob/f1c159f4c844d807968078ea504fba40ca7d9c73/src/win32.js#L50-L52
|
||||||
|
newOptions.buildVersion = getVersionString(executablePath, 'FileVersion');
|
||||||
|
|
||||||
|
if (newOptions.appVersion == newOptions.buildVersion) {
|
||||||
|
newOptions.buildVersion = undefined;
|
||||||
|
} else {
|
||||||
|
log.debug(
|
||||||
|
`Extracted build version from executable: ${newOptions.buildVersion}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newOptions.appCopyright === undefined) {
|
||||||
|
// https://github.com/electron/electron-packager/blob/f1c159f4c844d807968078ea504fba40ca7d9c73/src/win32.js#L54-L56
|
||||||
|
newOptions.appCopyright = getVersionString(
|
||||||
|
executablePath,
|
||||||
|
'LegalCopyright',
|
||||||
|
);
|
||||||
|
log.debug(
|
||||||
|
`Extracted app copyright from executable: ${newOptions.appCopyright}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (looksLikeLinux) {
|
||||||
|
log.debug('This looks like a Linux app...');
|
||||||
|
if (newOptions.platform === undefined) {
|
||||||
|
newOptions.platform = 'linux';
|
||||||
|
}
|
||||||
|
executablePath = path.join(
|
||||||
|
appRoot,
|
||||||
|
children.filter((c) => c.name == newOptions.name && c.isFile())[0].name,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug(`Executable path: ${executablePath}`);
|
||||||
|
|
||||||
|
if (newOptions.arch === undefined) {
|
||||||
|
const executableInfo = getExecutableInfo(
|
||||||
|
executablePath,
|
||||||
|
newOptions.platform,
|
||||||
|
);
|
||||||
|
newOptions.arch = executableInfo.arch;
|
||||||
|
log.debug(`Extracted arch from executable: ${newOptions.arch}`);
|
||||||
|
}
|
||||||
|
if (newOptions.platform === undefined || newOptions.arch == undefined) {
|
||||||
|
throw Error(`Could not determine platform / arch of app in ${appRoot}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newOptions;
|
||||||
|
}
|
39
src/helpers/upgrade/plistInfoXMLHelpers.ts
Normal file
39
src/helpers/upgrade/plistInfoXMLHelpers.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
export function extractBoolean(
|
||||||
|
infoPlistXML: string,
|
||||||
|
plistKey: string,
|
||||||
|
): boolean | undefined {
|
||||||
|
const plistValue = extractRaw(infoPlistXML, plistKey);
|
||||||
|
|
||||||
|
return plistValue === undefined
|
||||||
|
? undefined
|
||||||
|
: plistValue.split('<')[1].split('/>')[0].toLowerCase() === 'true';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function extractString(
|
||||||
|
infoPlistXML: string,
|
||||||
|
plistKey: string,
|
||||||
|
): string | undefined {
|
||||||
|
const plistValue = extractRaw(infoPlistXML, plistKey);
|
||||||
|
|
||||||
|
return plistValue === undefined
|
||||||
|
? undefined
|
||||||
|
: plistValue.split('<string>')[1].split('</string>')[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractRaw(
|
||||||
|
infoPlistXML: string,
|
||||||
|
plistKey: string,
|
||||||
|
): string | undefined {
|
||||||
|
// This would be easier with xml2js, but let's not add a dependency for something this minor.
|
||||||
|
const fullKey = `\n <key>${plistKey}</key>`;
|
||||||
|
|
||||||
|
if (infoPlistXML.indexOf(fullKey) === -1) {
|
||||||
|
// This value wasn't set, so we'll stay agnostic to it
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return infoPlistXML
|
||||||
|
.split(fullKey)[1]
|
||||||
|
.split('\n </dict>')[0] // Get everything between here and the end of the main plist dict
|
||||||
|
.split('\n <key>')[0]; // Get everything before the next key (if it exists)
|
||||||
|
}
|
42
src/helpers/upgrade/rceditGet.ts
Normal file
42
src/helpers/upgrade/rceditGet.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import * as os from 'os';
|
||||||
|
import * as path from 'path';
|
||||||
|
import { spawnSync } from 'child_process';
|
||||||
|
|
||||||
|
// A modification of https://github.com/electron/node-rcedit to support the retrieval
|
||||||
|
// of information.
|
||||||
|
|
||||||
|
export function getVersionString(
|
||||||
|
executablePath: string,
|
||||||
|
versionString: string,
|
||||||
|
): string {
|
||||||
|
let rcedit = path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'..',
|
||||||
|
'..',
|
||||||
|
'..',
|
||||||
|
'node_modules',
|
||||||
|
'rcedit',
|
||||||
|
'bin',
|
||||||
|
process.arch === 'x64' ? 'rcedit-x64.exe' : 'rcedit.exe',
|
||||||
|
);
|
||||||
|
const args = [executablePath, `--get-version-string`, versionString];
|
||||||
|
|
||||||
|
const spawnOptions = {
|
||||||
|
env: { ...process.env },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use Wine on non-Windows platforms except for WSL, which doesn't need it
|
||||||
|
if (process.platform !== 'win32' && !os.release().endsWith('Microsoft')) {
|
||||||
|
args.unshift(rcedit);
|
||||||
|
rcedit = process.arch === 'x64' ? 'wine64' : 'wine';
|
||||||
|
// Suppress "fixme:" stderr log messages
|
||||||
|
spawnOptions.env.WINEDEBUG = '-all';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const child = spawnSync(rcedit, args, spawnOptions);
|
||||||
|
const result = child.output?.toString().split(',wine: ')[0];
|
||||||
|
return result.startsWith(',') ? result.substr(1) : result;
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
199
src/helpers/upgrade/upgrade.ts
Normal file
199
src/helpers/upgrade/upgrade.ts
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
import * as log from 'loglevel';
|
||||||
|
|
||||||
|
import { NativefierOptions } from '../../options/model';
|
||||||
|
import { dirExists, fileExists } from '../fsHelpers';
|
||||||
|
import { extractBoolean, extractString } from './plistInfoXMLHelpers';
|
||||||
|
import { getOptionsFromExecutable } from './executableHelpers';
|
||||||
|
|
||||||
|
export type UpgradeAppInfo = {
|
||||||
|
appResourcesDir: string;
|
||||||
|
options: NativefierOptions;
|
||||||
|
};
|
||||||
|
|
||||||
|
function findUpgradeAppResourcesDir(searchDir: string): string | null {
|
||||||
|
searchDir = dirExists(searchDir) ? searchDir : path.dirname(searchDir);
|
||||||
|
log.debug(`Searching for nativfier.json in ${searchDir}`);
|
||||||
|
const children = fs.readdirSync(searchDir, { withFileTypes: true });
|
||||||
|
if (fileExists(path.join(searchDir, 'nativefier.json'))) {
|
||||||
|
// Found 'nativefier.json', so this must be it!
|
||||||
|
return path.resolve(searchDir);
|
||||||
|
}
|
||||||
|
const childDirectories = children.filter((c) => c.isDirectory());
|
||||||
|
for (const childDir of childDirectories) {
|
||||||
|
// We must go deeper!
|
||||||
|
const result = findUpgradeAppResourcesDir(
|
||||||
|
path.join(searchDir, childDir.name, 'nativefier.json'),
|
||||||
|
);
|
||||||
|
if (result !== null) {
|
||||||
|
return path.resolve(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Didn't find it down here
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getIconPath(appResourcesDir: string): string | undefined {
|
||||||
|
const icnsPath = path.join(appResourcesDir, '..', 'electron.icns');
|
||||||
|
if (fileExists(icnsPath)) {
|
||||||
|
log.debug(`Found icon at: ${icnsPath}`);
|
||||||
|
return path.resolve(icnsPath);
|
||||||
|
}
|
||||||
|
const icoPath = path.join(appResourcesDir, 'icon.ico');
|
||||||
|
if (fileExists(icoPath)) {
|
||||||
|
log.debug(`Found icon at: ${icoPath}`);
|
||||||
|
return path.resolve(icoPath);
|
||||||
|
}
|
||||||
|
const pngPath = path.join(appResourcesDir, 'icon.png');
|
||||||
|
if (fileExists(pngPath)) {
|
||||||
|
log.debug(`Found icon at: ${pngPath}`);
|
||||||
|
return path.resolve(pngPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug('Could not find icon file.');
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getInfoPListOptions(
|
||||||
|
appResourcesDir: string,
|
||||||
|
priorOptions: NativefierOptions,
|
||||||
|
): NativefierOptions {
|
||||||
|
if (!fileExists(path.join(appResourcesDir, '..', '..', 'Info.plist'))) {
|
||||||
|
// Not a darwin/mas app, so this is irrelevant
|
||||||
|
return priorOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newOptions = { ...priorOptions };
|
||||||
|
|
||||||
|
const infoPlistXML: string = fs
|
||||||
|
.readFileSync(path.join(appResourcesDir, '..', '..', 'Info.plist'))
|
||||||
|
.toString();
|
||||||
|
|
||||||
|
if (newOptions.appCopyright === undefined) {
|
||||||
|
// https://github.com/electron/electron-packager/blob/0d3f84374e9ab3741b171610735ebc6be3e5e75f/src/mac.js#L230-L232
|
||||||
|
newOptions.appCopyright = extractString(
|
||||||
|
infoPlistXML,
|
||||||
|
'NSHumanReadableCopyright',
|
||||||
|
);
|
||||||
|
log.debug(
|
||||||
|
`Extracted app copyright from Info.plist: ${newOptions.appCopyright}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newOptions.appVersion === undefined) {
|
||||||
|
// https://github.com/electron/electron-packager/blob/0d3f84374e9ab3741b171610735ebc6be3e5e75f/src/mac.js#L214-L216
|
||||||
|
// This could also be the buildVersion, but since they end up in the same place, that SHOULDN'T matter
|
||||||
|
const bundleVersion = extractString(infoPlistXML, 'CFBundleVersion');
|
||||||
|
newOptions.appVersion =
|
||||||
|
bundleVersion === undefined || bundleVersion === '1.0.0' // If it's 1.0.0, that's just the default
|
||||||
|
? undefined
|
||||||
|
: bundleVersion;
|
||||||
|
(newOptions.darwinDarkModeSupport =
|
||||||
|
newOptions.darwinDarkModeSupport === undefined
|
||||||
|
? undefined
|
||||||
|
: newOptions.darwinDarkModeSupport === false),
|
||||||
|
log.debug(
|
||||||
|
`Extracted app version from Info.plist: ${newOptions.appVersion}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newOptions.darwinDarkModeSupport === undefined) {
|
||||||
|
// https://github.com/electron/electron-packager/blob/0d3f84374e9ab3741b171610735ebc6be3e5e75f/src/mac.js#L234-L236
|
||||||
|
newOptions.darwinDarkModeSupport = extractBoolean(
|
||||||
|
infoPlistXML,
|
||||||
|
'NSRequiresAquaSystemAppearance',
|
||||||
|
);
|
||||||
|
log.debug(
|
||||||
|
`Extracted Darwin dark mode support from Info.plist: ${
|
||||||
|
newOptions.darwinDarkModeSupport ? 'Yes' : 'No'
|
||||||
|
}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getInjectPaths(appResourcesDir: string): string[] | undefined {
|
||||||
|
const injectDir = path.join(appResourcesDir, 'inject');
|
||||||
|
if (!dirExists(injectDir)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const injectPaths = fs
|
||||||
|
.readdirSync(injectDir, { withFileTypes: true })
|
||||||
|
.filter(
|
||||||
|
(fd) =>
|
||||||
|
fd.isFile() &&
|
||||||
|
(fd.name.toLowerCase().endsWith('.css') ||
|
||||||
|
fd.name.toLowerCase().endsWith('.js')),
|
||||||
|
)
|
||||||
|
.map((fd) => path.resolve(path.join(injectDir, fd.name)));
|
||||||
|
log.debug(`CSS/JS Inject paths: ${injectPaths.join(', ')}`);
|
||||||
|
return injectPaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAsar(appResourcesDir: string): boolean {
|
||||||
|
const asar = fileExists(path.join(appResourcesDir, '..', 'electron.asar'));
|
||||||
|
log.debug(`Is this app an ASAR? ${asar ? 'Yes' : 'No'}`);
|
||||||
|
return asar;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function findUpgradeApp(upgradeFrom: string): UpgradeAppInfo | null {
|
||||||
|
const searchDir = dirExists(upgradeFrom)
|
||||||
|
? upgradeFrom
|
||||||
|
: path.dirname(upgradeFrom);
|
||||||
|
log.debug(`Looking for old options file in ${searchDir}`);
|
||||||
|
const appResourcesDir = findUpgradeAppResourcesDir(searchDir);
|
||||||
|
if (appResourcesDir === null) {
|
||||||
|
log.debug(`No nativefier.json file found in ${searchDir}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug(`Loading ${path.join(appResourcesDir, 'nativefier.json')}`);
|
||||||
|
const options: NativefierOptions = JSON.parse(
|
||||||
|
fs.readFileSync(path.join(appResourcesDir, 'nativefier.json'), 'utf8'),
|
||||||
|
);
|
||||||
|
|
||||||
|
options.electronVersion = undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
appResourcesDir,
|
||||||
|
options: {
|
||||||
|
...options,
|
||||||
|
...getOptionsFromExecutable(appResourcesDir, options),
|
||||||
|
...getInfoPListOptions(appResourcesDir, options),
|
||||||
|
asar: options.asar !== undefined ? options.asar : isAsar(appResourcesDir),
|
||||||
|
icon: getIconPath(appResourcesDir),
|
||||||
|
inject: getInjectPaths(appResourcesDir),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useOldAppOptions(
|
||||||
|
rawOptions: NativefierOptions,
|
||||||
|
oldApp: UpgradeAppInfo,
|
||||||
|
): NativefierOptions {
|
||||||
|
if (rawOptions.targetUrl !== undefined && dirExists(rawOptions.targetUrl)) {
|
||||||
|
// You got your ouput dir in my targetUrl!
|
||||||
|
rawOptions.out = rawOptions.targetUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug('rawOptions', rawOptions);
|
||||||
|
log.debug('oldApp', oldApp);
|
||||||
|
|
||||||
|
if (
|
||||||
|
oldApp.options.userAgentOverriden === undefined ||
|
||||||
|
oldApp.options.userAgentOverriden === false
|
||||||
|
) {
|
||||||
|
oldApp.options.userAgent = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const combinedOptions = { ...rawOptions, ...oldApp.options };
|
||||||
|
|
||||||
|
log.debug('Combined options', combinedOptions);
|
||||||
|
|
||||||
|
return combinedOptions;
|
||||||
|
}
|
@ -1,10 +1,24 @@
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
|
import * as os from 'os';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
|
import { DEFAULT_ELECTRON_VERSION } from './constants';
|
||||||
import { getTempDir } from './helpers/helpers';
|
import { getTempDir } from './helpers/helpers';
|
||||||
|
import { inferArch } from './infer/inferOs';
|
||||||
|
import { inferUserAgent } from './infer/inferUserAgent';
|
||||||
import { buildNativefierApp } from './main';
|
import { buildNativefierApp } from './main';
|
||||||
|
|
||||||
function checkApp(appRoot: string, inputOptions: any): void {
|
async function checkApp(appRoot: string, inputOptions: any): Promise<void> {
|
||||||
|
const arch = (inputOptions.arch as string) || inferArch();
|
||||||
|
if (inputOptions.out !== undefined) {
|
||||||
|
expect(
|
||||||
|
path.join(
|
||||||
|
inputOptions.out,
|
||||||
|
`Google-${inputOptions.platform as string}-${arch}`,
|
||||||
|
),
|
||||||
|
).toBe(appRoot);
|
||||||
|
}
|
||||||
|
|
||||||
let relativeAppFolder: string;
|
let relativeAppFolder: string;
|
||||||
|
|
||||||
switch (inputOptions.platform) {
|
switch (inputOptions.platform) {
|
||||||
@ -18,7 +32,9 @@ function checkApp(appRoot: string, inputOptions: any): void {
|
|||||||
relativeAppFolder = 'resources/app';
|
relativeAppFolder = 'resources/app';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error('Unknown app platform');
|
throw new Error(
|
||||||
|
`Unknown app platform: ${new String(inputOptions.platform).toString()}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const appPath = path.join(appRoot, relativeAppFolder);
|
const appPath = path.join(appRoot, relativeAppFolder);
|
||||||
@ -36,6 +52,28 @@ function checkApp(appRoot: string, inputOptions: any): void {
|
|||||||
const iconPath = path.join(appPath, iconFile);
|
const iconPath = path.join(appPath, iconFile);
|
||||||
expect(fs.existsSync(iconPath)).toBe(true);
|
expect(fs.existsSync(iconPath)).toBe(true);
|
||||||
expect(fs.statSync(iconPath).size).toBeGreaterThan(1000);
|
expect(fs.statSync(iconPath).size).toBeGreaterThan(1000);
|
||||||
|
|
||||||
|
// Test arch
|
||||||
|
if (inputOptions.arch !== undefined) {
|
||||||
|
expect(inputOptions.arch).toBe(nativefierConfig.arch);
|
||||||
|
} else {
|
||||||
|
expect(os.arch()).toBe(nativefierConfig.arch);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test electron version
|
||||||
|
expect(nativefierConfig.electronVersionUsed).toBe(
|
||||||
|
inputOptions.electronVersion || DEFAULT_ELECTRON_VERSION,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test user agent
|
||||||
|
expect(nativefierConfig.userAgent).toBe(
|
||||||
|
inputOptions.userAgent !== undefined
|
||||||
|
? inputOptions.userAgent
|
||||||
|
: await inferUserAgent(
|
||||||
|
inputOptions.electronVersion || DEFAULT_ELECTRON_VERSION,
|
||||||
|
inputOptions.platform,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Nativefier', () => {
|
describe('Nativefier', () => {
|
||||||
@ -52,7 +90,54 @@ describe('Nativefier', () => {
|
|||||||
platform,
|
platform,
|
||||||
};
|
};
|
||||||
const appPath = await buildNativefierApp(options);
|
const appPath = await buildNativefierApp(options);
|
||||||
checkApp(appPath, options);
|
await checkApp(appPath, options);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Nativefier upgrade', () => {
|
||||||
|
jest.setTimeout(300000);
|
||||||
|
|
||||||
|
test.each([
|
||||||
|
{ platform: 'darwin', arch: 'x64' },
|
||||||
|
{ platform: 'linux', arch: 'arm64', userAgent: 'FIREFOX' },
|
||||||
|
// Exhaustive integration testing here would be neat, but takes too long.
|
||||||
|
// -> For now, only testing a subset of platforms/archs
|
||||||
|
// { platform: 'win32', arch: 'x64' },
|
||||||
|
// { platform: 'win32', arch: 'ia32' },
|
||||||
|
// { platform: 'darwin', arch: 'arm64' },
|
||||||
|
// { platform: 'linux', arch: 'x64' },
|
||||||
|
// { platform: 'linux', arch: 'armv7l' },
|
||||||
|
// { platform: 'linux', arch: 'ia32' },
|
||||||
|
])(
|
||||||
|
'can upgrade a Nativefier app for platform/arch: %s',
|
||||||
|
async (baseAppOptions) => {
|
||||||
|
const tempDirectory = getTempDir('integtestUpgrade1');
|
||||||
|
const options = {
|
||||||
|
targetUrl: 'https://google.com/',
|
||||||
|
out: tempDirectory,
|
||||||
|
overwrite: true,
|
||||||
|
electronVersion: '11.2.3',
|
||||||
|
...baseAppOptions,
|
||||||
|
};
|
||||||
|
const appPath = await buildNativefierApp(options);
|
||||||
|
await checkApp(appPath, options);
|
||||||
|
|
||||||
|
const upgradeOptions = {
|
||||||
|
upgrade: appPath,
|
||||||
|
overwrite: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const upgradeAppPath = await buildNativefierApp(upgradeOptions);
|
||||||
|
options.electronVersion = DEFAULT_ELECTRON_VERSION;
|
||||||
|
options.userAgent =
|
||||||
|
baseAppOptions.userAgent !== undefined
|
||||||
|
? baseAppOptions.userAgent
|
||||||
|
: await inferUserAgent(
|
||||||
|
DEFAULT_ELECTRON_VERSION,
|
||||||
|
baseAppOptions.platform,
|
||||||
|
);
|
||||||
|
await checkApp(upgradeAppPath, options);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -3,6 +3,8 @@ import * as electronPackager from 'electron-packager';
|
|||||||
export interface ElectronPackagerOptions extends electronPackager.Options {
|
export interface ElectronPackagerOptions extends electronPackager.Options {
|
||||||
targetUrl: string;
|
targetUrl: string;
|
||||||
platform: string;
|
platform: string;
|
||||||
|
upgrade: boolean;
|
||||||
|
upgradeFrom?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AppOptions {
|
export interface AppOptions {
|
||||||
@ -24,6 +26,7 @@ export interface AppOptions {
|
|||||||
disableGpu: boolean;
|
disableGpu: boolean;
|
||||||
disableOldBuildWarning: boolean;
|
disableOldBuildWarning: boolean;
|
||||||
diskCacheSize: number;
|
diskCacheSize: number;
|
||||||
|
electronVersionUsed?: string;
|
||||||
enableEs3Apis: boolean;
|
enableEs3Apis: boolean;
|
||||||
fastQuit: boolean;
|
fastQuit: boolean;
|
||||||
fileDownloadOptions: any;
|
fileDownloadOptions: any;
|
||||||
@ -46,6 +49,7 @@ export interface AppOptions {
|
|||||||
titleBarStyle: string;
|
titleBarStyle: string;
|
||||||
tray: string | boolean;
|
tray: string | boolean;
|
||||||
userAgent: string;
|
userAgent: string;
|
||||||
|
userAgentOverriden: boolean;
|
||||||
verbose: boolean;
|
verbose: boolean;
|
||||||
versionString: string;
|
versionString: string;
|
||||||
width: number;
|
width: number;
|
||||||
|
@ -28,7 +28,7 @@ export async function getOptions(rawOptions: any): Promise<AppOptions> {
|
|||||||
appCopyright: rawOptions.appCopyright,
|
appCopyright: rawOptions.appCopyright,
|
||||||
appVersion: rawOptions.appVersion,
|
appVersion: rawOptions.appVersion,
|
||||||
arch: rawOptions.arch || inferArch(),
|
arch: rawOptions.arch || inferArch(),
|
||||||
asar: rawOptions.conceal || false,
|
asar: rawOptions.asar || rawOptions.conceal || false,
|
||||||
buildVersion: rawOptions.buildVersion,
|
buildVersion: rawOptions.buildVersion,
|
||||||
darwinDarkModeSupport: rawOptions.darwinDarkModeSupport || false,
|
darwinDarkModeSupport: rawOptions.darwinDarkModeSupport || false,
|
||||||
dir: PLACEHOLDER_APP_DIR,
|
dir: PLACEHOLDER_APP_DIR,
|
||||||
@ -38,8 +38,13 @@ export async function getOptions(rawOptions: any): Promise<AppOptions> {
|
|||||||
out: rawOptions.out || process.cwd(),
|
out: rawOptions.out || process.cwd(),
|
||||||
overwrite: rawOptions.overwrite,
|
overwrite: rawOptions.overwrite,
|
||||||
platform: rawOptions.platform || inferPlatform(),
|
platform: rawOptions.platform || inferPlatform(),
|
||||||
targetUrl: normalizeUrl(rawOptions.targetUrl),
|
targetUrl:
|
||||||
|
rawOptions.targetUrl === undefined
|
||||||
|
? '' // We'll plug this in later via upgrade
|
||||||
|
: normalizeUrl(rawOptions.targetUrl),
|
||||||
tmpdir: false, // workaround for electron-packager#375
|
tmpdir: false, // workaround for electron-packager#375
|
||||||
|
upgrade: rawOptions.upgrade !== undefined ? true : false,
|
||||||
|
upgradeFrom: rawOptions.upgrade,
|
||||||
win32metadata: rawOptions.win32metadata || {
|
win32metadata: rawOptions.win32metadata || {
|
||||||
ProductName: rawOptions.name,
|
ProductName: rawOptions.name,
|
||||||
InternalName: rawOptions.name,
|
InternalName: rawOptions.name,
|
||||||
@ -86,6 +91,8 @@ export async function getOptions(rawOptions: any): Promise<AppOptions> {
|
|||||||
titleBarStyle: rawOptions.titleBarStyle || null,
|
titleBarStyle: rawOptions.titleBarStyle || null,
|
||||||
tray: rawOptions.tray || false,
|
tray: rawOptions.tray || false,
|
||||||
userAgent: rawOptions.userAgent,
|
userAgent: rawOptions.userAgent,
|
||||||
|
userAgentOverriden:
|
||||||
|
rawOptions.userAgent !== undefined && rawOptions.userAgent !== null,
|
||||||
verbose: rawOptions.verbose,
|
verbose: rawOptions.verbose,
|
||||||
versionString: rawOptions.versionString,
|
versionString: rawOptions.versionString,
|
||||||
width: rawOptions.width || 1280,
|
width: rawOptions.width || 1280,
|
||||||
|
Loading…
Reference in New Issue
Block a user