From 1778a8de11afe2c53f83706c7c195ccc9fa333fa Mon Sep 17 00:00:00 2001 From: develar Date: Thu, 4 Aug 2016 20:51:30 +0200 Subject: [PATCH] feat: prevent error "Unable to find a valid app" Closes #633 --- docs/Options.md | 18 +++++++++++++++--- src/asarUtil.ts | 14 +++++++++----- src/metadata.ts | 16 +++++----------- src/packager.ts | 15 +++++++++++++++ src/platformPackager.ts | 16 ++++++++-------- src/util/util.ts | 5 ++++- 6 files changed, 56 insertions(+), 28 deletions(-) diff --git a/docs/Options.md b/docs/Options.md index 60e5fb567b7..d0c930bcd92 100644 --- a/docs/Options.md +++ b/docs/Options.md @@ -54,8 +54,8 @@ Here documented only `electron-builder` specific options: | app-category-type |

*macOS-only.* The application category type, as shown in the Finder via *View -> Arrange by Application Category* when viewing the Applications directory.

For example, app-category-type=public.app-category.developer-tools will set the application category to *Developer Tools*.

Valid values are listed in [Apple’s documentation](https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/LaunchServicesKeys.html#//apple_ref/doc/uid/TP40009250-SW8).

| asar |

Whether to package the application’s source code into an archive, using [Electron’s archive format](https://github.com/electron/asar). Defaults to true. Reasons why you may want to disable this feature are described in [an application packaging tutorial in Electron’s documentation](http://electron.atom.io/docs/latest/tutorial/application-packaging/#limitations-on-node-api/).

Or you can pass object of any asar options.

electron-builder detects node modules that must be unpacked automatically, you don’t need to explicitly set asar.unpackDir - please file issue if this doesn’t work.

| productName | See [AppMetadata.productName](#AppMetadata-productName). -| files |

A [glob patterns](https://www.npmjs.com/package/glob#glob-primer) relative to the [app directory](#MetadataDirectories-app), which specifies which files to include when copying files to create the package. Defaults to **\/\* (i.e. [hidden files are ignored by default](https://www.npmjs.com/package/glob#dots)).

Development dependencies are never copied in any case. You don’t need to ignore it explicitly.

[Multiple patterns](#multiple-glob-patterns) are supported. You can use ${os} (expanded to mac, linux or win according to current platform) and ${arch} in the pattern. If directory matched, all contents are copied. So, you can just specify foo to copy foo directory.

Remember that default pattern \*\*\/\* is not added to your custom, so, you have to add it explicitly — e.g. ["\*\*\/\*", "!ignoreMe${/\*}"].

May be specified in the platform options (e.g. in the build.mac).

-| extraResources |

A [glob patterns](https://www.npmjs.com/package/glob#glob-primer) relative to the project directory, when specified, copy the file or directory with matching names directly into the app’s resources directory (Contents/Resources for MacOS, resources for Linux/Windows).

Glob rules the same as for [files](#BuildMetadata-files).

+| files |

A [glob patterns](https://www.npmjs.com/package/glob#glob-primer) relative to the [app directory](#MetadataDirectories-app), which specifies which files to include when copying files to create the package. See [File Patterns](#multiple-glob-patterns).

+| extraResources |

A [glob patterns](https://www.npmjs.com/package/glob#glob-primer) relative to the project directory, when specified, copy the file or directory with matching names directly into the app’s resources directory (Contents/Resources for MacOS, resources for Linux/Windows).

Glob rules the same as for [files](#multiple-glob-patterns).

| extraFiles | The same as [extraResources](#BuildMetadata-extraResources) but copy into the app's content directory (`Contents` for MacOS, root directory for Linux/Windows). | mac | See [.build.mac](#MacOptions). | dmg | See [.build.dmg](#DmgOptions). @@ -174,8 +174,20 @@ NSIS only, [in progress](https://github.com/electron-userland/electron-builder/i +# File Patterns -# Multiple Glob Patterns +[build.files](#BuildMetadata-files) defaults to `**/*` (i.e. [hidden files are ignored by default](https://www.npmjs.com/package/glob#dots)). + +Development dependencies are never copied in any case. You don't need to ignore it explicitly. + +[Multiple patterns](#multiple-glob-patterns) are supported. You can use `${os}` (expanded to mac, linux or win according to current platform) and `${arch}` in the pattern. +If directory matched, all contents are copied. So, you can just specify `foo` to copy `foo` directory. + +Remember that default pattern `**/*` is not added to your custom, so, you have to add it explicitly — e.g. `["**/*", "!ignoreMe${/*}"]`. + +May be specified in the platform options (e.g. in the `build.mac`). + +## Multiple Glob Patterns ```js [ // match all files diff --git a/src/asarUtil.ts b/src/asarUtil.ts index c8d0a273907..18214b2cdf5 100644 --- a/src/asarUtil.ts +++ b/src/asarUtil.ts @@ -384,7 +384,11 @@ function writeAsarFile(filesystem: any, dest: string, toPack: Array, cha }) } -export async function checkFileInPackage(asarFile: string, relativeFile: string) { +export async function checkFileInArchive(asarFile: string, relativeFile: string, messagePrefix: string) { + function error(text: string) { + return new Error(`${messagePrefix} "${relativeFile}" in the "${asarFile}" ${text}`) + } + let stat: AsarFileInfo | null try { stat = statFile(asarFile, relativeFile) @@ -392,14 +396,14 @@ export async function checkFileInPackage(asarFile: string, relativeFile: string) catch (e) { const fileStat = await statOrNull(asarFile) if (fileStat == null) { - throw new Error(`File "${asarFile}" does not exist. Seems like a wrong configuration.`) + throw error(`does not exist. Seems like a wrong configuration.`) } try { listPackage(asarFile) } catch (e) { - throw new Error(`File "${asarFile}" is corrupted: ${e}`) + throw error(`is corrupted: ${e}`) } // asar throws error on access to undefined object (info.link) @@ -407,10 +411,10 @@ export async function checkFileInPackage(asarFile: string, relativeFile: string) } if (stat == null) { - throw new Error(`Application entry file "${relativeFile}" in the "${asarFile}" does not exist. Seems like a wrong configuration.`) + throw error(`does not exist. Seems like a wrong configuration.`) } if (stat.size === 0) { - throw new Error(`Application entry file "${relativeFile}" in the "${asarFile}" is corrupted: size 0`) + throw error(`is corrupted: size 0`) } } diff --git a/src/metadata.ts b/src/metadata.ts index 2ce230676a9..35b2cd6de55 100755 --- a/src/metadata.ts +++ b/src/metadata.ts @@ -3,6 +3,8 @@ import { ElectronPackagerOptions } from "./packager/dirPackager" export interface Metadata { readonly repository?: string | RepositoryInfo | null + + dependencies?: { [key: string]: string } } /* @@ -122,23 +124,15 @@ export interface BuildMetadata { readonly productName?: string | null /** - A [glob patterns](https://www.npmjs.com/package/glob#glob-primer) relative to the [app directory](#MetadataDirectories-app), which specifies which files to include when copying files to create the package. Defaults to `**\/\*` (i.e. [hidden files are ignored by default](https://www.npmjs.com/package/glob#dots)). - - Development dependencies are never copied in any case. You don't need to ignore it explicitly. - - [Multiple patterns](#multiple-glob-patterns) are supported. You can use `${os}` (expanded to mac, linux or win according to current platform) and `${arch}` in the pattern. - If directory matched, all contents are copied. So, you can just specify `foo` to copy `foo` directory. - - Remember that default pattern `\*\*\/\*` is not added to your custom, so, you have to add it explicitly — e.g. `["\*\*\/\*", "!ignoreMe${/\*}"]`. - - May be specified in the platform options (e.g. in the `build.mac`). + A [glob patterns](https://www.npmjs.com/package/glob#glob-primer) relative to the [app directory](#MetadataDirectories-app), which specifies which files to include when copying files to create the package. + See [File Patterns](#multiple-glob-patterns). */ readonly files?: Array | string | null /** A [glob patterns](https://www.npmjs.com/package/glob#glob-primer) relative to the project directory, when specified, copy the file or directory with matching names directly into the app's resources directory (`Contents/Resources` for MacOS, `resources` for Linux/Windows). - Glob rules the same as for [files](#BuildMetadata-files). + Glob rules the same as for [files](#multiple-glob-patterns). */ readonly extraResources?: Array | string | null diff --git a/src/packager.ts b/src/packager.ts index 45a224c98da..f4005761d1f 100644 --- a/src/packager.ts +++ b/src/packager.ts @@ -168,7 +168,10 @@ export class Packager implements BuildInfo { checkNotEmpty("description", appMetadata.description) checkNotEmpty("version", appMetadata.version) + checkDependencies(this.devMetadata.dependencies) if ((appMetadata) !== this.devMetadata) { + checkDependencies(appMetadata.dependencies) + if ((appMetadata).build != null) { throw new Error(util.format(errorMessages.buildInAppSpecified, appPackageFile, devAppPackageFile)) } @@ -282,4 +285,16 @@ async function checkWineVersion(checkPromise: Promise) { if (semver.lt(wineVersion, "1.8.0")) { throw new Error(wineError(`wine 1.8+ is required, but your version is ${wineVersion}`)) } +} + +function checkDependencies(dependencies?: { [key: string]: string }) { + if (dependencies == null) { + return + } + + for (let name of ["electron", "electron-prebuilt", "electron-builder"]) { + if (name in dependencies) { + throw new Error(`${name} must be in the devDependencies`) + } + } } \ No newline at end of file diff --git a/src/platformPackager.ts b/src/platformPackager.ts index aa28d245da5..27aedd651ea 100644 --- a/src/platformPackager.ts +++ b/src/platformPackager.ts @@ -8,7 +8,7 @@ import { Packager } from "./packager" import { AsarOptions } from "asar-electron-builder" import { archiveApp } from "./targets/archive" import { Minimatch } from "minimatch" -import { checkFileInPackage, createAsarArchive } from "./asarUtil" +import { checkFileInArchive, createAsarArchive } from "./asarUtil" import { deepAssign } from "./util/deepAssign" import { warn, log, task } from "./util/log" import { AppInfo } from "./appInfo" @@ -362,25 +362,24 @@ export abstract class PlatformPackager return path.join(appOutDir, `${this.appInfo.productFilename}.app`, "Contents", "Resources") } - private async checkFileInPackage(resourcesDir: string, file: string, isAsar: boolean) { + private async checkFileInPackage(resourcesDir: string, file: string, messagePrefix: string, isAsar: boolean) { const relativeFile = path.relative(this.info.appDir, path.resolve(this.info.appDir, file)) if (isAsar) { - await checkFileInPackage(path.join(resourcesDir, "app.asar"), relativeFile) + await checkFileInArchive(path.join(resourcesDir, "app.asar"), relativeFile, messagePrefix) } else { const outStat = await statOrNull(path.join(resourcesDir, "app", relativeFile)) if (outStat == null) { - throw new Error(`Application entry file "${relativeFile}" does not exist. Seems like a wrong configuration.`) + throw new Error(`${messagePrefix} "${relativeFile}" does not exist. Seems like a wrong configuration.`) } else if (!outStat.isFile()) { - throw new Error(`Application entry file "${relativeFile}" is not a file. Seems like a wrong configuration.`) + throw new Error(`${messagePrefix} "${relativeFile}" is not a file. Seems like a wrong configuration.`) } } } private async sanityCheckPackage(appOutDir: string, isAsar: boolean): Promise { const outStat = await statOrNull(appOutDir) - if (outStat == null) { throw new Error(`Output directory "${appOutDir}" does not exist. Seems like a wrong configuration.`) } @@ -388,8 +387,9 @@ export abstract class PlatformPackager throw new Error(`Output directory "${appOutDir}" is not a directory. Seems like a wrong configuration.`) } - const mainFile = this.appInfo.metadata.main || "index.js" - await this.checkFileInPackage(this.getResourcesDir(appOutDir), mainFile, isAsar) + const resourcesDir = this.getResourcesDir(appOutDir) + await this.checkFileInPackage(resourcesDir, this.appInfo.metadata.main || "index.js", "Application entry file", isAsar) + await this.checkFileInPackage(resourcesDir, "package.json", "Application", isAsar) } protected async archiveApp(format: string, appOutDir: string, outFile: string): Promise { diff --git a/src/util/util.ts b/src/util/util.ts index a558ebce76e..ca1f6b8299b 100644 --- a/src/util/util.ts +++ b/src/util/util.ts @@ -204,6 +204,9 @@ export async function computeDefaultAppDirectory(projectDir: string, userAppDir: else if (!stat.isDirectory()) { throw new Error(`Application directory ${userAppDir} is not a directory`) } + else if (projectDir === absolutePath) { + warn(`Specified application directory "${userAppDir}" equals to project dir — superfluous or wrong configuration`) + } return absolutePath } @@ -238,7 +241,7 @@ let tmpDirCounter = 0 const tempDirPrefix = `${process.pid.toString(36)}-${Date.now().toString(36)}` export function getTempName(prefix?: string | n): string { - return `${prefix == null ? "" : prefix + "-"}${tempDirPrefix}-${(tmpDirCounter++).toString(36)}` + return `${prefix == null ? "" : `${prefix}-`}${tempDirPrefix}-${(tmpDirCounter++).toString(36)}` } export function isEmptyOrSpaces(s: string | n) {