From 6a906ac97ac1c2dbf66ff2cac5842500790c2712 Mon Sep 17 00:00:00 2001 From: develar Date: Tue, 9 Aug 2016 18:49:28 +0200 Subject: [PATCH] feat: remove OriginalFilename, add LegalTrademarks, add ProductVersion for NSIS Closes #655 --- .idea/dictionaries/develar.xml | 1 + README.md | 2 +- docs/Multi Platform Build.md | 3 +- docs/Options.md | 8 ++-- package.json | 7 ++-- src/appInfo.ts | 27 +++++++------ src/errorMessages.ts | 2 +- src/linuxPackager.ts | 5 +++ src/metadata.ts | 31 +++++++++------ src/packager/dirPackager.ts | 20 ++-------- src/packager/mac.ts | 28 ++++--------- src/packager/win32.ts | 23 ----------- src/platformPackager.ts | 9 +++-- src/targets/nsis.ts | 22 ++++++----- src/targets/squirrelWindows.ts | 9 +---- src/util/util.ts | 29 ++++++++++---- src/winPackager.ts | 39 ++++++++++++++++++- src/windowsCodeSign.ts | 10 +++-- .../fixtures/app-executable-deps/package.json | 2 +- test/fixtures/test-app-one/package.json | 2 +- test/fixtures/test-app/package.json | 2 +- test/src/globTest.ts | 2 +- test/src/winPackagerTest.ts | 7 +++- 23 files changed, 162 insertions(+), 128 deletions(-) delete mode 100644 src/packager/win32.ts diff --git a/.idea/dictionaries/develar.xml b/.idea/dictionaries/develar.xml index e9853439a1f..35ae13010f8 100644 --- a/.idea/dictionaries/develar.xml +++ b/.idea/dictionaries/develar.xml @@ -91,6 +91,7 @@ repos rimraf semver + signcode signtool templating testapp diff --git a/README.md b/README.md index 41781ad05e7..5c502c2c878 100755 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ For a production app you need to sign your application, see [Where to buy code s ```json "build": { "appId": "your.id", - "app-category-type": "your.app.category.type", + "category": "your.app.category.type", "win": { "iconUrl": "(windows-only) https link to icon" } diff --git a/docs/Multi Platform Build.md b/docs/Multi Platform Build.md index 09abb52575f..4b137d99abc 100755 --- a/docs/Multi Platform Build.md +++ b/docs/Multi Platform Build.md @@ -26,7 +26,8 @@ Use [brew](http://brew.sh) to install required packages. ### To build app for Windows on MacOS: ``` -brew install Caskroom/cask/xquartz wine mono +brew install wine --with-win64 --without-x11 --devel +brew install mono ``` ### To build app for Linux on MacOS: diff --git a/docs/Options.md b/docs/Options.md index c7e756efa5d..871ac1defe1 100644 --- a/docs/Options.md +++ b/docs/Options.md @@ -51,12 +51,14 @@ Here documented only `electron-builder` specific options: | Name | Description | --- | --- | appId |

The application id. Used as [CFBundleIdentifier](https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/20001431-102070) for MacOS and as [Application User Model ID](https://msdn.microsoft.com/en-us/library/windows/desktop/dd378459(v=vs.85).aspx) for Windows.

For windows only NSIS target supports it. Squirrel.Windows is not fixed yet.

Defaults to com.electron.${name}. It is strongly recommended that an explicit ID be set.

-| 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.

+| category |

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

For example, "category": "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).

+| copyright | The human-readable copyright line for the app. Defaults to `Copyright © year author`. +| 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.

Node modules, that must be unpacked, will be detected 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.

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). +| fileAssociations | File associations. See [.build.fileAssociations](#FileAssociation). | mac | See [.build.mac](#MacOptions). | dmg | See [.build.dmg](#DmgOptions). | mas | See [.build.mas](#MasBuildOptions). @@ -66,7 +68,6 @@ Here documented only `electron-builder` specific options: | compression | The compression level, one of `store`, `normal`, `maximum` (default: `normal`). If you want to rapidly test build, `store` can reduce build time significantly. | afterPack | *programmatic API only* The function to be run after pack (but before pack into distributable format and sign). Promise must be returned. | npmRebuild | Whether to [rebuild](https://docs.npmjs.com/cli/rebuild) native dependencies (`npm rebuild`) before starting to package the app. Defaults to `true`. -| fileAssociations | File associations. (NSIS only for now). ### `.build.mac` @@ -119,6 +120,7 @@ Windows specific build options. | remoteToken | *Squirrel.Windows-only.* Authentication token for remote updates | signingHashAlgorithms | Array of signing algorithms used. Defaults to `['sha1', 'sha256']` | icon | The path to application icon. Defaults to `build/icon.ico` (consider using this convention instead of complicating your configuration). +| legalTrademarks | The trademarks and registered trademarks. ### `.build.nsis` diff --git a/package.json b/package.json index 36f61b14602..f7cefb334ba 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "lint": "tslint 'src/**/*.ts' 'test/src/**/*.ts'", "pretest": "npm run compile && npm run lint", "test": "node ./test/out/helpers/runTests.js", - "semantic-release": "semantic-release pre && cd nsis-auto-updater && npm install --production && cd .. && npm publish && semantic-release post", + "semantic-release": "semantic-release pre && cd nsis-auto-updater && npm install --production && npm prune && cd .. && npm publish && semantic-release post", "//": "Update wiki if docs changed. Update only if functionalily are generally available (latest release, not next)", "update-wiki": "git subtree split -b wiki --prefix docs/ && git push -f wiki wiki:master", "whitespace": "whitespace 'src/**/*.ts'", @@ -69,7 +69,7 @@ "debug": "^2.2.0", "electron-download": "^2.1.2", "electron-osx-sign": "^0.4.0-beta4", - "electron-winstaller-fixed": "~3.1.0", + "electron-winstaller-fixed": "~4.0.0", "extract-zip": "^1.5.0", "fs-extra-p": "^1.0.6", "hosted-git-info": "^2.1.5", @@ -84,7 +84,6 @@ "pretty-ms": "^2.1.0", "progress": "^1.1.8", "progress-stream": "^1.2.0", - "rcedit": "^0.5.1", "sanitize-filename": "^1.6.0", "semver": "^5.3.0", "source-map-support": "^0.4.2", @@ -110,7 +109,7 @@ "@types/progress": "^1.1.27", "@types/semver": "^5.3.28", "@types/source-map-support": "^0.2.27", - "ava-tf": "^0.15.4", + "ava-tf": "^0.16.0", "babel-plugin-array-includes": "^2.0.3", "babel-plugin-transform-es2015-destructuring": "^6.9.0", "babel-plugin-transform-es2015-parameters": "^6.11.4", diff --git a/src/appInfo.ts b/src/appInfo.ts index fe8059b7be6..75b0da7903a 100644 --- a/src/appInfo.ts +++ b/src/appInfo.ts @@ -10,16 +10,6 @@ const __awaiter = require("./util/awaiter") export class AppInfo { readonly description = smarten(this.metadata.description!) - - // windows-only - versionString = { - CompanyName: this.companyName, - FileDescription: this.description, - ProductName: this.productName, - InternalName: this.productName, - LegalCopyright: this.copyright, - } - readonly version: string readonly buildNumber: string readonly buildVersion: string @@ -69,12 +59,27 @@ export class AppInfo { return this.metadata.name } + get category() { + const metadata = this.devMetadata.build + const old = (metadata)["app-category-type"] + if (old != null) { + warn('"app-category-type" is deprecated — please use "category" instead') + } + return metadata.category || old + } + get productName(): string { return getProductName(this.metadata, this.devMetadata) } get copyright(): string { - const copyright = (this.devMetadata.build)["app-copyright"] + const metadata = this.devMetadata.build + const old = (metadata)["app-copyright"] + if (old != null) { + warn('"app-copyright" is deprecated — please use "copyright" instead') + } + + const copyright = metadata.copyright || old if (copyright != null) { return copyright } diff --git a/src/errorMessages.ts b/src/errorMessages.ts index a0af52e282c..98099318dbc 100644 --- a/src/errorMessages.ts +++ b/src/errorMessages.ts @@ -2,7 +2,7 @@ export const buildIsMissed = `Please specify 'build' configuration in the develo build: { "appId": "your.id", - "app-category-type": "your.app.category.type" + "category": "your.app.category.type" } } diff --git a/src/linuxPackager.ts b/src/linuxPackager.ts index 12137371457..1d8bc4ec6d5 100755 --- a/src/linuxPackager.ts +++ b/src/linuxPackager.ts @@ -6,6 +6,7 @@ import FpmTarget from "./targets/fpm" import { createCommonTarget, DEFAULT_TARGET } from "./targets/targetFactory" import { LinuxTargetHelper } from "./targets/LinuxTargetHelper" import AppImageTarget from "./targets/appImage" +import { rename } from "fs-extra-p" //noinspection JSUnusedLocalSymbols const __awaiter = require("./util/awaiter") @@ -65,6 +66,10 @@ export class LinuxPackager extends PlatformPackager { postAsyncTasks.push(this.packageInDistributableFormat(outDir, appOutDir, arch, targets)) } + protected postInitApp(appOutDir: string): Promise { + return rename(path.join(appOutDir, "electron"), path.join(appOutDir, this.appInfo.productFilename)) + } + protected async packageInDistributableFormat(outDir: string, appOutDir: string, arch: Arch, targets: Array): Promise { // todo fix fpm - if run in parallel, get strange tar errors for (let t of targets) { diff --git a/src/metadata.ts b/src/metadata.ts index e6788e2c9c4..08ac327770b 100755 --- a/src/metadata.ts +++ b/src/metadata.ts @@ -93,17 +93,19 @@ export interface BuildMetadata { */ readonly appId?: string | null - // deprecated - readonly "app-bundle-id"?: string | null - /* *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*. + For example, `"category": "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). */ - readonly "app-category-type"?: string | null + readonly category?: string | null + + /* + The human-readable copyright line for the app. Defaults to `Copyright © year author`. + */ + readonly copyright?: string | null /* Whether to package the application's source code into an archive, using [Electron's archive format](https://github.com/electron/asar). Defaults to `true`. @@ -111,7 +113,7 @@ export interface BuildMetadata { 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. + Node modules, that must be unpacked, will be detected automatically, you don't need to explicitly set `asar.unpackDir` - please file issue if this doesn't work. */ readonly asar?: AsarOptions | boolean | null @@ -142,6 +144,11 @@ export interface BuildMetadata { */ readonly extraFiles?: Array | string | null + /* + File associations. See [.build.fileAssociations](#FileAssociation). + */ + readonly fileAssociations?: Array | FileAssociation + /* See [.build.mac](#MacOptions). */ @@ -194,10 +201,8 @@ export interface BuildMetadata { readonly icon?: string | null - /* - File associations. (NSIS only for now). - */ - readonly fileAssociations?: Array | FileAssociation + // deprecated + readonly "app-bundle-id"?: string | null } export interface AfterPackContext { @@ -335,7 +340,6 @@ export interface WinBuildOptions extends PlatformSpecificBuildOptions { Array of signing algorithms used. Defaults to `['sha1', 'sha256']` */ readonly signingHashAlgorithms?: Array | null - readonly signcodePath?: string | null /* The path to application icon. Defaults to `build/icon.ico` (consider using this convention instead of complicating your configuration). @@ -343,6 +347,11 @@ export interface WinBuildOptions extends PlatformSpecificBuildOptions { readonly icon?: string | null readonly fileAssociations?: Array | FileAssociation + + /* + The trademarks and registered trademarks. + */ + readonly legalTrademarks?: string | null } /* diff --git a/src/packager/dirPackager.ts b/src/packager/dirPackager.ts index d46b051e725..8aa62ed53a7 100644 --- a/src/packager/dirPackager.ts +++ b/src/packager/dirPackager.ts @@ -1,8 +1,7 @@ import { Promise as BluebirdPromise } from "bluebird" -import { emptyDir, rename } from "fs-extra-p" +import { emptyDir } from "fs-extra-p" import { warn } from "../util/log" import { AppInfo } from "../appInfo" -import * as path from "path" const downloadElectron: (options: any) => Promise = BluebirdPromise.promisify(require("electron-download")) const extract: any = BluebirdPromise.promisify(require("extract-zip")) @@ -12,7 +11,6 @@ const __awaiter = require("../util/awaiter") export interface ElectronPackagerOptions { "extend-info"?: string - "app-category-type"?: string protocols?: any @@ -25,13 +23,6 @@ export interface ElectronPackagerOptions { ignore?: any } -const supportedPlatforms: any = { - // Maps to module ID for each platform (lazy-required if used) - darwin: "./mac", - mas: "./mac", // map to darwin - win32: "./win32" -} - function createDownloadOpts(opts: any, platform: string, arch: string, electronVersion: string) { const downloadOpts = Object.assign({ cache: opts.cache, @@ -58,13 +49,10 @@ export async function pack(opts: ElectronPackagerOptions, out: string, platform: ]))[0] await extract(zipPath, {dir: out}) - if (platform === "linux") { - await BluebirdPromise.all([ - initializeApp(), - rename(path.join(out, "electron"), path.join(out, opts.appInfo.productFilename)) - ]) + if (platform === "darwin" || platform === "mas") { + await(require("./mac")).createApp(opts, out, initializeApp) } else { - await (require(supportedPlatforms[platform])).createApp(opts, out, initializeApp) + await initializeApp() } } \ No newline at end of file diff --git a/src/packager/mac.ts b/src/packager/mac.ts index b4b9908d5b7..5423c5a6581 100644 --- a/src/packager/mac.ts +++ b/src/packager/mac.ts @@ -3,6 +3,7 @@ import { rename, readFile, writeFile, copy } from "fs-extra-p" import * as path from "path" import { parse as parsePlist, build as buildPlist } from "plist" import { Promise as BluebirdPromise } from "bluebird" +import { use } from "../util/util" //noinspection JSUnusedLocalSymbols const __awaiter = require("../util/awaiter") @@ -52,15 +53,9 @@ export async function createApp(opts: ElectronPackagerOptions, appOutDir: string Object.assign(appPlist, parsePlist(fileContents[4])) } - // Now set fields based on explicit options - const appBundleIdentifier = filterCFBundleIdentifier(appInfo.id) const helperBundleIdentifier = filterCFBundleIdentifier(opts["helper-bundle-id"] || `${appBundleIdentifier}.helper`) - const buildVersion = appInfo.buildVersion - const appCategoryType = opts["app-category-type"] - const humanReadableCopyright = appInfo.copyright - appPlist.CFBundleDisplayName = appInfo.productName appPlist.CFBundleIdentifier = appBundleIdentifier appPlist.CFBundleName = appInfo.productName @@ -78,13 +73,11 @@ export async function createApp(opts: ElectronPackagerOptions, appOutDir: string helperNPPlist.CFBundleName = `${appInfo.productName} Helper NP` helperNPPlist.CFBundleExecutable = `${appFilename} Helper NP` - if (appInfo.version != null) { - appPlist.CFBundleShortVersionString = appPlist.CFBundleVersion = appInfo.version - } - - if (buildVersion != null) { - appPlist.CFBundleVersion = buildVersion - } + use(appInfo.version, it => { + appPlist.CFBundleShortVersionString = it + appPlist.CFBundleVersion = it + }) + use(appInfo.buildVersion, it => appPlist.CFBundleVersion = it) if (opts.protocols && opts.protocols.length) { appPlist.CFBundleURLTypes = opts.protocols.map(function (protocol: any) { @@ -95,13 +88,8 @@ export async function createApp(opts: ElectronPackagerOptions, appOutDir: string }) } - if (appCategoryType) { - appPlist.LSApplicationCategoryType = appCategoryType - } - - if (humanReadableCopyright) { - appPlist.NSHumanReadableCopyright = humanReadableCopyright - } + use(appInfo.category, it => appPlist.LSApplicationCategoryType = it) + use(appInfo.copyright, it => appPlist.NSHumanReadableCopyright = it) const promises: Array> = [ writeFile(appPlistFilename, buildPlist(appPlist)), diff --git a/src/packager/win32.ts b/src/packager/win32.ts deleted file mode 100644 index 4a4aab6d77a..00000000000 --- a/src/packager/win32.ts +++ /dev/null @@ -1,23 +0,0 @@ -import * as path from "path" -import { Promise as BluebirdPromise } from "bluebird" -import { rename } from "fs-extra-p" -import { ElectronPackagerOptions } from "./dirPackager" - -const rcedit: any = BluebirdPromise.promisify(require("rcedit")) - -export function createApp(opts: ElectronPackagerOptions, appOutDir: string, initializeApp: () => Promise) { - const newExePath = path.join(appOutDir, `${opts.appInfo.productFilename}.exe`) - return BluebirdPromise.all([ - initializeApp(), - rename(path.join(appOutDir, "electron.exe"), newExePath) - .then(() => { - const appInfo = opts.appInfo - return rcedit(newExePath, { - "version-string": appInfo.versionString, - "file-version": appInfo.buildVersion, - "product-version": appInfo.version, - icon: opts.icon - }) - }) - ]) -} \ No newline at end of file diff --git a/src/platformPackager.ts b/src/platformPackager.ts index 4e82db584d0..4389fc72eea 100644 --- a/src/platformPackager.ts +++ b/src/platformPackager.ts @@ -235,6 +235,7 @@ export abstract class PlatformPackager promises.push(remove(path.join(resourcesPath, "default_app"))) } + promises.push(this.postInitApp(appOutDir)) await BluebirdPromise.all(promises) }) await task(`Packaging for platform ${platformName} ${Arch[arch]} using electron ${this.info.electronVersion} to ${path.relative(this.projectDir, appOutDir)}`, p) @@ -253,6 +254,10 @@ export abstract class PlatformPackager await this.sanityCheckPackage(appOutDir, asarOptions != null) } + protected postInitApp(executableFile: string): Promise { + return BluebirdPromise.resolve(null) + } + protected async computePackOptions(): Promise { //noinspection JSUnusedGlobalSymbols const appInfo = this.appInfo @@ -261,10 +266,6 @@ export abstract class PlatformPackager appInfo: appInfo, }, this.devMetadata.build) - if (this.platform === Platform.WINDOWS) { - options["version-string"] = appInfo.versionString - } - delete options.osx delete options.win delete options.linux diff --git a/src/targets/nsis.ts b/src/targets/nsis.ts index bf48d85d213..4d471b7ad4c 100644 --- a/src/targets/nsis.ts +++ b/src/targets/nsis.ts @@ -1,6 +1,6 @@ import { WinPackager } from "../winPackager" import { Arch, NsisOptions } from "../metadata" -import { debug, doSpawn, handleProcess } from "../util/util" +import { debug, doSpawn, handleProcess, use } from "../util/util" import * as path from "path" import { Promise as BluebirdPromise } from "bluebird" import { getBin } from "../util/binDownload" @@ -110,18 +110,22 @@ export default class NsisTarget extends Target { // Error: invalid VIProductVersion format, should be X.X.X.X // so, we must strip beta const parsedVersion = new semver.SemVer(appInfo.version) + const versionKey = [ + `ProductName "${appInfo.productName}"`, + `ProductVersion "${appInfo.version}"`, + `CompanyName "${appInfo.companyName}"`, + `LegalCopyright "${appInfo.copyright}"`, + `FileDescription "${appInfo.description}"`, + `FileVersion "${appInfo.buildVersion}"`, + ] + use(this.packager.platformSpecificBuildOptions.legalTrademarks, it => versionKey.push(`LegalTrademarks "${it}"`)) + const commands: any = { OutFile: `"${installerPath}"`, // LoadLanguageFile: '"${NSISDIR}/Contrib/Language files/English.nlf"', VIProductVersion: `${parsedVersion.major}.${parsedVersion.minor}.${parsedVersion.patch}.${appInfo.buildNumber || "0"}`, // VIFileVersion: packager.appInfo.buildVersion, - VIAddVersionKey: [ - `ProductName "${appInfo.productName}"`, - `CompanyName "${appInfo.versionString.CompanyName}"`, - `LegalCopyright "${appInfo.versionString.LegalCopyright}"`, - `FileDescription "${appInfo.description}"`, - `FileVersion "${appInfo.buildVersion}"`, - ], + VIAddVersionKey: versionKey, } if (packager.devMetadata.build.compression === "store") { @@ -233,7 +237,7 @@ export default class NsisTarget extends Target { // we use NSIS_CONFIG_CONST_DATA_PATH=no to build makensis on Linux, but in any case it doesn't use stubs as MacOS/Windows version, so, we explicitly set NSISDIR env: Object.assign({}, process.env, {NSISDIR: nsisPath}), cwd: nsisTemplatesDir, - }, true) + }) handleProcess("close", childProcess, command, resolve, reject) childProcess.stdin.end(script) diff --git a/src/targets/squirrelWindows.ts b/src/targets/squirrelWindows.ts index 6826a1e01e5..c1797e07d1c 100644 --- a/src/targets/squirrelWindows.ts +++ b/src/targets/squirrelWindows.ts @@ -57,12 +57,6 @@ export default class SquirrelWindowsTarget extends Target { const appInfo = packager.appInfo const projectUrl = await appInfo.computePackageUrl() - const rceditOptions = { - "version-string": appInfo.versionString, - "file-version": appInfo.buildVersion, - "product-version": appInfo.version, - } - const cscInfo = await packager.cscInfo const options: any = Object.assign({ name: appInfo.name, @@ -86,8 +80,7 @@ export default class SquirrelWindowsTarget extends Target { extraMetadataSpecs: projectUrl == null ? null : `\n ${projectUrl}`, copyright: appInfo.copyright, packageCompressionLevel: packager.devMetadata.build.compression === "store" ? 0 : 9, - sign: this.packager.sign.bind(this.packager), - rcedit: rceditOptions, + sign: this.packager.signAndEditResources.bind(this.packager), }, packager.platformSpecificBuildOptions) if (!("loadingGif" in options)) { diff --git a/src/util/util.ts b/src/util/util.ts index 7c3942c3709..c604147d11e 100644 --- a/src/util/util.ts +++ b/src/util/util.ts @@ -78,7 +78,6 @@ export function exec(file: string, args?: Array | null, options?: ExecOp if (stderr.length !== 0) { log(stderr) } - // log(stdout) } resolve(stdout) } @@ -97,12 +96,12 @@ export function exec(file: string, args?: Array | null, options?: ExecOp }) } -export function doSpawn(command: string, args: Array, options?: SpawnOptions, pipeInput?: Boolean): ChildProcess { +export function doSpawn(command: string, args: Array, options?: SpawnOptions): ChildProcess { if (options == null) { options = {} } if (options.stdio == null) { - options.stdio = [pipeInput ? "pipe" : "ignore", debug.enabled ? "inherit" : "pipe", "inherit"] + options.stdio = ["pipe", debug.enabled ? "inherit" : "pipe", "pipe"] } if (debug.enabled) { @@ -111,9 +110,9 @@ export function doSpawn(command: string, args: Array, options?: SpawnOpt return _spawn(command, args, options) } -export function spawn(command: string, args?: Array | null, options?: SpawnOptions, pipeInput?: Boolean): BluebirdPromise { +export function spawn(command: string, args?: Array | null, options?: SpawnOptions): BluebirdPromise { return new BluebirdPromise((resolve, reject) => { - handleProcess("close", doSpawn(command, args || [], options, pipeInput), command, resolve, reject) + handleProcess("close", doSpawn(command, args || [], options), command, resolve, reject) }) } @@ -127,13 +126,29 @@ export function handleProcess(event: string, childProcess: ChildProcess, command }) } - childProcess.on(event, (code: number) => { + let errorOut = "" + if (childProcess.stderr != null) { + childProcess.stderr.on("data", (data: string) => { + errorOut += data + }) + } + + childProcess.once(event, (code: number) => { if (code === 0 && debug.enabled) { debug(`${command} (${childProcess.pid}) exited with code ${code}`) } if (code !== 0) { - reject(new Error(`${command} exited with code ${code}${out.length === 0 ? "" : `\n${out}`}`)) + function formatOut(text: string, title: string) { + if (text.length === 0) { + return "" + } + else { + return `\n${title}:\n${text}` + } + } + + reject(new Error(`${command} exited with code ${code}${formatOut(out, "Output")}${formatOut(errorOut, "Error output")}`)) } else if (resolve != null) { resolve() diff --git a/src/winPackager.ts b/src/winPackager.ts index abd4ebce838..0bcdf57ca44 100644 --- a/src/winPackager.ts +++ b/src/winPackager.ts @@ -4,11 +4,13 @@ import { PlatformPackager, BuildInfo, getArchSuffix, Target } from "./platformPa import { Platform, WinBuildOptions, Arch } from "./metadata" import * as path from "path" import { log, task } from "./util/log" +import { exec, use } from "./util/util" import { deleteFile, open, close, read } from "fs-extra-p" -import { sign, SignOptions } from "./windowsCodeSign" +import { sign, SignOptions, getSignVendorPath } from "./windowsCodeSign" import SquirrelWindowsTarget from "./targets/squirrelWindows" import NsisTarget from "./targets/nsis" import { DEFAULT_TARGET, createCommonTarget, DIR_TARGET } from "./targets/targetFactory" +import { rename } from "fs-extra-p" //noinspection JSUnusedLocalSymbols const __awaiter = require("./util/awaiter") @@ -133,6 +135,41 @@ export class WinPackager extends PlatformPackager { return sign(opts) } + async signAndEditResources(file: string) { + const appInfo = this.appInfo + + const args = [ + file, + "--set-version-string", "CompanyName", appInfo.companyName, + "--set-version-string", "FileDescription", appInfo.description, + "--set-version-string", "ProductName", appInfo.productName, + "--set-version-string", "InternalName", appInfo.productName, + "--set-version-string", "LegalCopyright", appInfo.copyright, + // cannot remove OriginalFilename, must be set to some value + "--set-version-string", "OriginalFilename", "", + "--set-file-version", appInfo.buildVersion, + "--set-product-version", appInfo.version, + "--set-icon", await this.getIconPath(), + ] + + use(this.platformSpecificBuildOptions.legalTrademarks, it => args.push("--set-version-string", "LegalTrademarks", it!)) + + const rceditExecutable = path.join(await getSignVendorPath(), "rcedit.exe") + const isWin = process.platform === "win32" + if (!isWin) { + args.unshift(rceditExecutable) + } + await exec(isWin ? rceditExecutable : "wine", args) + + await this.sign(file) + } + + protected async postInitApp(appOutDir: string) { + const executable = path.join(appOutDir, `${this.appInfo.productFilename}.exe`) + await rename(path.join(appOutDir, "electron.exe"), executable) + await this.signAndEditResources(executable) + } + protected packageInDistributableFormat(outDir: string, appOutDir: string, arch: Arch, targets: Array, promises: Array>): void { for (let target of targets) { if (target instanceof SquirrelWindowsTarget) { diff --git a/src/windowsCodeSign.ts b/src/windowsCodeSign.ts index eacffafd07f..b0fd87b98b2 100644 --- a/src/windowsCodeSign.ts +++ b/src/windowsCodeSign.ts @@ -6,7 +6,11 @@ import { getBin } from "./util/binDownload" //noinspection JSUnusedLocalSymbols const __awaiter = require("./util/awaiter") -const TOOLS_VERSION = "winCodeSign-1.0.0" +const TOOLS_VERSION = "winCodeSign-1.1.0" + +export function getSignVendorPath() { + return getBin("winCodeSign", TOOLS_VERSION, `https://dl.bintray.com/electron-userland/bin/${TOOLS_VERSION}.7z`, "f9cd51c00f673c49290e2a1c610ba1106b84e68fc23f7075b98e4e10403d6e02") +} export interface SignOptions { path: string @@ -121,7 +125,7 @@ function getOutputPath(inputPath: string, hash: string) { } async function getToolPath(options: any) { - let result = options.signcodePath || process.env.SIGNTOOL_PATH + let result = process.env.SIGNTOOL_PATH if (result) { return result } @@ -130,7 +134,7 @@ async function getToolPath(options: any) { return "osslsigncode" } - const vendorPath = await getBin("winCodeSign", TOOLS_VERSION, `https://dl.bintray.com/electron-userland/bin/${TOOLS_VERSION}.7z`, "0e524943dd6f03a9cee4cdaaa235e27d4945a1c9dc80ccee8ff1219e7b73ad88") + const vendorPath = await getSignVendorPath() if (process.platform === "win32") { return path.join(vendorPath, `windows-${(release().startsWith("6.") ? "6" : "10")}`, "signtool.exe") } diff --git a/test/fixtures/app-executable-deps/package.json b/test/fixtures/app-executable-deps/package.json index 010bc106e20..ae837904c92 100644 --- a/test/fixtures/app-executable-deps/package.json +++ b/test/fixtures/app-executable-deps/package.json @@ -4,6 +4,6 @@ "electron": "^1.3.2" }, "build": { - "app-category-type": "public.app-category.business" + "category": "public.app-category.business" } } diff --git a/test/fixtures/test-app-one/package.json b/test/fixtures/test-app-one/package.json index 648e82f0db1..9560eb14084 100755 --- a/test/fixtures/test-app-one/package.json +++ b/test/fixtures/test-app-one/package.json @@ -10,7 +10,7 @@ "build": { "electronVersion": "1.3.2", "appId": "org.electron-builder.testApp", - "app-category-type": "your.app.category.type", + "category": "your.app.category.type", "iconUrl": "https://raw.githubusercontent.com/szwacz/electron-boilerplate/master/resources/windows/icon.ico", "compression": "store" } diff --git a/test/fixtures/test-app/package.json b/test/fixtures/test-app/package.json index ba7f3ecec59..5909c6b6324 100755 --- a/test/fixtures/test-app/package.json +++ b/test/fixtures/test-app/package.json @@ -3,7 +3,7 @@ "build": { "electronVersion": "1.3.2", "appId": "org.electron-builder.testApp", - "app-category-type": "your.app.category.type", + "category": "your.app.category.type", "iconUrl": "https://raw.githubusercontent.com/szwacz/electron-boilerplate/master/resources/windows/icon.ico", "compression": "store" } diff --git a/test/src/globTest.ts b/test/src/globTest.ts index 76f965775b2..f8e6a5de5e4 100644 --- a/test/src/globTest.ts +++ b/test/src/globTest.ts @@ -204,7 +204,7 @@ test("extraResources", async () => { }) test("extraResources - one-package", async () => { - for (let platform of [Platform.LINUX]) { + for (let platform of [process.platform === "win32" ? Platform.WINDOWS : Platform.LINUX]) { const osName = platform.buildConfigurationKey const winDirPrefix = "lib/net45/resources/" diff --git a/test/src/winPackagerTest.ts b/test/src/winPackagerTest.ts index fd9ed5d35cf..427114a3a1b 100755 --- a/test/src/winPackagerTest.ts +++ b/test/src/winPackagerTest.ts @@ -35,7 +35,12 @@ test.skip("delta and msi", () => assertPack("test-app-one", { test.ifDevOrWinCi("beta version", () => { const metadata: any = { - version: "3.0.0-beta.2" + version: "3.0.0-beta.2", + build: { + win: { + legalTrademarks: "My Trademark" + }, + }, } return assertPack("test-app-one", { targets: Platform.WINDOWS.createTarget(["squirrel", "nsis"]),