diff --git a/.idea/dictionaries/develar.xml b/.idea/dictionaries/develar.xml index 83574acb2ac..caa8feae166 100644 --- a/.idea/dictionaries/develar.xml +++ b/.idea/dictionaries/develar.xml @@ -29,6 +29,7 @@ passin pkcs postinstall + productbuild promisify repos rimraf diff --git a/.idea/runConfigurations/CodeSignTest.xml b/.idea/runConfigurations/CodeSignTest.xml new file mode 100644 index 00000000000..f1065afd29f --- /dev/null +++ b/.idea/runConfigurations/CodeSignTest.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 14543e7c188..754411cea37 100755 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ npm install electron-builder --save-dev * [Native application dependencies](http://electron.atom.io/docs/latest/tutorial/using-native-node-modules/) compilation (only if [two-package.json project structure](#two-packagejson-structure) used). * [Auto Update](#auto-update) ready application packaging. -* [Code Signing](#code-signing) on a CI server or development machine. +* [Code Signing](https://github.com/electron-userland/electron-builder/wiki/Code-Signing) on a CI server or development machine. * [Build version management](#build-version-management). * [Publishing artifacts to GitHub Releases](https://github.com/electron-userland/electron-builder/wiki/Publishing-Artifacts). @@ -86,25 +86,6 @@ Please note — packaged into an asar archive [by default](https://github.com/el You need to deploy somewhere releases/downloads server. Consider to use [Nuts](https://github.com/GitbookIO/nuts) (GitHub as a backend to store assets) or [Electron Release Server](https://github.com/ArekSredzki/electron-release-server). -# Code Signing -OS X and Windows code singing is supported. -On a development machine set environment variable `CSC_NAME` to your identity (recommended). Or pass `--sign` parameter. -``` -export CSC_NAME="Developer ID Application: Your Name (code)" -``` - -## Travis, AppVeyor and other CI servers -To sign app on build server: - -1. [Export](https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/AppDistributionGuide/MaintainingCertificates/MaintainingCertificates.html#//apple_ref/doc/uid/TP40012582-CH31-SW7) certificate. - [Strong password](http://security.stackexchange.com/a/54773) must be used. Consider to not use special characters (for bash) because “*values are not escaped when your builds are executed*”. -2. Upload `*.p12` file (e.g. on [Google Drive](http://www.syncwithtech.org/p/direct-download-link-generator.html)). -3. Set ([Travis](https://docs.travis-ci.com/user/environment-variables/#Encrypted-Variables) or [AppVeyor](https://ci.appveyor.com/tools/encrypt)) `CSC_LINK` and `CSC_KEY_PASSWORD` environment variables: -``` -travis encrypt "CSC_LINK='https://drive.google.com/uc?export=download&id=***'" --add -travis encrypt 'CSC_KEY_PASSWORD=beAwareAboutBashEscaping!!!' --add -``` - # Build Version Management `CFBundleVersion` (OS X) and `FileVersion` (Windows) will be set automatically to `version`.`build_number` on CI server (Travis, AppVeyor and CircleCI supported). diff --git a/docs/Code Signing.md b/docs/Code Signing.md new file mode 100644 index 00000000000..e63339534a4 --- /dev/null +++ b/docs/Code Signing.md @@ -0,0 +1,28 @@ +OS X and Windows code singing is supported. + +On a development machine set environment variable `CSC_NAME` (and `CSC_INSTALLER_NAME` if you build for Mac App Store) to your identity. + +| Env name | Description +| -------------- | ----------- +| `CSC_LINK` | The HTTPS link to certificate (`*.p12` file). +| `CSC_KEY_PASSWORD` | The password to decrypt the certificate given in `CSC_LINK`. +| `CSC_INSTALLER_LINK` | *osx-only* The HTTPS link to certificate to sign Mac App Store build (`*.p12` file). +| `CSC_INSTALLER_KEY_PASSWORD` | *osx-only* The password to decrypt the certificate given in `CSC_INSTALLER_LINK`. +| `CSC_NAME` | *osx-only* Name of certificate (to retrieve from login.keychain). Useful on a development machine (not on CI). +| `CSC_INSTALLER_NAME` | *osx-only* Name of installer certificate (to retrieve from login.keychain). Useful on a development machine (not on CI). + +``` +export CSC_NAME="Developer ID Application: Your Name (code)" +``` + +## Travis, AppVeyor and other CI servers +To sign app on build server you need to set `CSC_LINK`, `CSC_KEY_PASSWORD` (and `CSC_INSTALLER_LINK`, `CSC_INSTALLER_KEY_PASSWORD` if you build for Mac App Store): + +1. [Export](https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/AppDistributionGuide/MaintainingCertificates/MaintainingCertificates.html#//apple_ref/doc/uid/TP40012582-CH31-SW7) certificate. + [Strong password](http://security.stackexchange.com/a/54773) must be used. Consider to not use special characters (for bash) because “*values are not escaped when your builds are executed*”. +2. Upload `*.p12` file (e.g. on [Google Drive](http://www.syncwithtech.org/p/direct-download-link-generator.html)). +3. Set ([Travis](https://docs.travis-ci.com/user/environment-variables/#Encrypted-Variables) or [AppVeyor](https://ci.appveyor.com/tools/encrypt)) `CSC_LINK` and `CSC_KEY_PASSWORD` environment variables: +``` +travis encrypt "CSC_LINK='https://drive.google.com/uc?export=download&id=***'" --add +travis encrypt 'CSC_KEY_PASSWORD=beAwareAboutBashEscaping!!!' --add +``` \ No newline at end of file diff --git a/docs/Options.md b/docs/Options.md index 3d0d9381b73..a4b11949bb5 100644 --- a/docs/Options.md +++ b/docs/Options.md @@ -53,7 +53,7 @@ Here documented only `electron-builder` specific options: | app-bundle-id | *OS X-only.* The bundle identifier to use in the application's plist. | app-category-type |

*OS X-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/).

-| iconUrl |

*windows-only.* A URL to an ICO file to use as the application icon (displayed in Control Panel > Programs and Features). Defaults to the Electron icon.

Please note — [local icon file url is not accepted](https://github.com/atom/grunt-electron-installer/issues/73), must be https/http.

+| iconUrl |

*windows-only.* A URL to an ICO file to use as the application icon (displayed in Control Panel > Programs and Features). Defaults to the Electron icon.

Please note — [local icon file url is not accepted](https://github.com/atom/grunt-electron-installer/issues/73), must be https/http.

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

A [glob expression](https://www.npmjs.com/package/glob#glob-primer), when specified, copy the file or directory with matching names directly into the app’s directory (Contents/Resources for OS X).

You can use ${os} (expanded to osx, 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 <project_dir>/foo directory.

May be specified in the platform options (i.e. in the build.osx).

| osx | See [.build.osx](#OsXBuildOptions). @@ -70,7 +70,7 @@ See all [appdmg options](https://www.npmjs.com/package/appdmg#json-specification | --- | --- | icon | The path to icon, which will be shown when mounted (default: `build/icon.icns`). | background | The path to background (default: `build/background.png`). -| target | Target package type: list of `default`, `dmg`, `zip`. +| target | Target package type: list of `default`, `dmg`, `zip`, `mas`. ### `.build.win` diff --git a/package.json b/package.json index 7efd17e2eb8..6898d9aa0cc 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,8 @@ "chalk": "^1.1.3", "command-line-args": "^2.1.6", "deep-assign": "^2.0.0", - "electron-packager": "^7.0.1", + "electron-osx-sign-tf": "^0.4.0-beta.0", + "electron-packager-tf": "^7.0.2-beta.0", "electron-winstaller-fixed": "~2.3.0-beta.4", "fs-extra-p": "^0.3.0", "globby": "^4.0.0", diff --git a/src/codeSign.ts b/src/codeSign.ts index f639e32ea4a..b3baf54e4cb 100644 --- a/src/codeSign.ts +++ b/src/codeSign.ts @@ -11,8 +11,10 @@ import { randomBytes } from "crypto" const __awaiter = require("./awaiter") export interface CodeSigningInfo { - cscName: string - cscKeychainName?: string + name: string + keychainName?: string + + installerName?: string } function randomString(): string { @@ -23,29 +25,30 @@ export function generateKeychainName(): string { return "csc-" + randomString() + ".keychain" } -export function createKeychain(keychainName: string, cscLink: string, cscKeyPassword: string, csaLink?: string): Promise { - const authorityCerts = [csaLink || "https://developer.apple.com/certificationauthority/AppleWWDRCA.cer"] +export function createKeychain(keychainName: string, cscLink: string, cscKeyPassword: string, cscILink?: string, cscIKeyPassword?: string, csaLink?: string): Promise { + const certLinks = [csaLink || "https://developer.apple.com/certificationauthority/AppleWWDRCA.cer"] if (csaLink == null) { - authorityCerts.push("https://startssl.com/certs/sca.code2.crt", "https://startssl.com/certs/sca.code3.crt") + certLinks.push("https://startssl.com/certs/sca.code2.crt", "https://startssl.com/certs/sca.code3.crt") } - const authorityCertPaths = authorityCerts.map(() => path.join(tmpdir(), randomString() + ".cer")) - const developerCertPath = path.join(tmpdir(), randomString() + ".p12") + certLinks.push(cscLink) + if (cscILink != null) { + certLinks.push(cscILink) + } + const certPaths = certLinks.map(it => path.join(tmpdir(), randomString() + (it.endsWith(".cer") ? ".cer" : ".p12"))) const keychainPassword = randomString() return executeFinally(BluebirdPromise.all([ - BluebirdPromise.map(authorityCertPaths, (p, i) => download(authorityCerts[i], p)), - download(cscLink, developerCertPath), + BluebirdPromise.map(certPaths, (p, i) => download(certLinks[i], p)), BluebirdPromise.mapSeries([ ["create-keychain", "-p", keychainPassword, keychainName], ["unlock-keychain", "-p", keychainPassword, keychainName], ["set-keychain-settings", "-t", "3600", "-u", keychainName] ], it => exec("security", it)) ]) - .then(() => importCerts(keychainName, authorityCertPaths, developerCertPath, cscKeyPassword)), + .then(() => importCerts(keychainName, certPaths, [cscKeyPassword, cscIKeyPassword].filter(it => it != null))), errorOccurred => { - const tasks = authorityCertPaths.map(it => deleteFile(it, true)) - tasks.push(deleteFile(developerCertPath, true)) + const tasks = certPaths.map(it => deleteFile(it, true)) if (errorOccurred) { tasks.push(deleteKeychain(keychainName)) } @@ -53,15 +56,25 @@ export function createKeychain(keychainName: string, cscLink: string, cscKeyPass }) } -async function importCerts(keychainName: string, authorityCertPaths: Array, developerCertPath: string, cscKeyPassword: string): Promise { - for (let p of authorityCertPaths) { - await exec("security", ["import", p, "-k", keychainName, "-T", "/usr/bin/codesign"]) +async function importCerts(keychainName: string, paths: Array, keyPasswords: Array): Promise { + for (let f of paths.slice(0, -keyPasswords.length)) { + await exec("security", ["import", f, "-k", keychainName, "-T", "/usr/bin/codesign"]) + } + + const namePromises: Array> = [] + for (let i = paths.length - keyPasswords.length, j = 0; i < paths.length; i++, j++) { + const password = keyPasswords[j] + const certPath = paths[i] + await exec("security", ["import", certPath, "-k", keychainName, "-T", "/usr/bin/codesign", "-T", "/usr/bin/productbuild", "-P", password]) + + namePromises.push(extractCommonName(password, certPath)) } - await exec("security", ["import", developerCertPath, "-k", keychainName, "-T", "/usr/bin/codesign", "-P", cscKeyPassword]) - let cscName = await extractCommonName(cscKeyPassword, developerCertPath) + + const names = await BluebirdPromise.all(namePromises) return { - cscName: cscName, - cscKeychainName: keychainName + name: names[0], + installerName: names.length > 1 ? names[1] : null, + keychainName: keychainName, } } @@ -79,9 +92,9 @@ function extractCommonName(password: string, certPath: string): BluebirdPromise< } export function sign(path: string, options: CodeSigningInfo): BluebirdPromise { - const args = ["--deep", "--force", "--sign", options.cscName, path] - if (options.cscKeychainName != null) { - args.push("--keychain", options.cscKeychainName) + const args = ["--deep", "--force", "--sign", options.name, path] + if (options.keychainName != null) { + args.push("--keychain", options.keychainName) } return exec("codesign", args) } diff --git a/src/macPackager.ts b/src/macPackager.ts index 4325f9b6b03..d5f191594cd 100644 --- a/src/macPackager.ts +++ b/src/macPackager.ts @@ -3,9 +3,10 @@ import { Platform, OsXBuildOptions } from "./metadata" import * as path from "path" import { Promise as BluebirdPromise } from "bluebird" import { log, spawn, statOrNull } from "./util" -import { createKeychain, deleteKeychain, CodeSigningInfo, generateKeychainName, sign } from "./codeSign" +import { createKeychain, deleteKeychain, CodeSigningInfo, generateKeychainName } from "./codeSign" import { path7za } from "7zip-bin" import deepAssign = require("deep-assign") +import { sign, flat, BaseSignOptions } from "electron-osx-sign-tf" //noinspection JSUnusedLocalSymbols const __awaiter = require("./awaiter") @@ -21,7 +22,7 @@ export default class OsXPackager extends PlatformPackager { if (this.options.cscLink != null && this.options.cscKeyPassword != null) { const keychainName = generateKeychainName() cleanupTasks.push(() => deleteKeychain(keychainName)) - this.codeSigningInfo = createKeychain(keychainName, this.options.cscLink, this.options.cscKeyPassword, this.options.csaLink) + this.codeSigningInfo = createKeychain(keychainName, this.options.cscLink, this.options.cscKeyPassword, this.options.cscInstallerLink, this.options.cscInstallerKeyPassword, this.options.csaLink) } else { this.codeSigningInfo = BluebirdPromise.resolve(null) @@ -32,7 +33,7 @@ export default class OsXPackager extends PlatformPackager { target = Array.isArray(target) ? target : [target] target = target.map(it => it.toLowerCase().trim()) for (let t of target) { - if (t !== "default" && t !== "dmg" && t !== "zip") { + if (t !== "default" && t !== "dmg" && t !== "zip" && t !== "mas") { throw new Error("Unknown target: " + t) } } @@ -44,23 +45,63 @@ export default class OsXPackager extends PlatformPackager { return Platform.OSX } - async pack(outDir: string, appOutDir: string, arch: string): Promise { - await super.pack(outDir, appOutDir, arch) - await this.signMac(path.join(appOutDir, this.appName + ".app"), await this.codeSigningInfo) + protected computeAppOutDir(outDir: string, arch: string): string { + return this.target.includes("mas") ? path.join(outDir, `${this.appName}-mas-${arch}`) : super.computeAppOutDir(outDir, arch) } - private signMac(distPath: string, codeSigningInfo: CodeSigningInfo): Promise { + async doPack(outDir: string, appOutDir: string, arch: string): Promise { + await super.doPack(outDir, appOutDir, arch) + await this.sign(appOutDir, await this.codeSigningInfo) + } + + protected beforePack(options: any): void { + if (this.target.includes("mas")) { + options.platform = "mas" + } + // disable warning + options["osx-sign"] = false + } + + private async sign(appOutDir: string, codeSigningInfo: CodeSigningInfo): Promise { if (codeSigningInfo == null) { - codeSigningInfo = {cscName: this.options.sign || process.env.CSC_NAME} + codeSigningInfo = { + name: this.options.sign || process.env.CSC_NAME, + installerName: this.options.sign || process.env.CSC_INSTALLER_NAME, + } } - if (codeSigningInfo.cscName == null) { + if (codeSigningInfo.name == null) { log("App is not signed: CSC_LINK or CSC_NAME are not specified") - return BluebirdPromise.resolve() + return } - else { - log("Signing app") - return sign(distPath, codeSigningInfo) + + log("Signing app") + + const isMas = this.target.includes("mas") + const baseSignOptions: BaseSignOptions = { + app: path.join(appOutDir, this.appName + ".app"), + platform: isMas ? "mas" : "darwin" + } + if (codeSigningInfo.keychainName != null) { + baseSignOptions.keychain = codeSigningInfo.keychainName + } + + await BluebirdPromise.promisify(sign)(Object.assign({ + identity: codeSigningInfo.name, + }, (this.devMetadata.build)["osx-sign"], baseSignOptions)) + + if (isMas) { + const installerIdentity = codeSigningInfo.installerName + if (installerIdentity == null) { + throw new Error("Signing is required for mas builds but CSC_INSTALLER_LINK or CSC_INSTALLER_NAME are not specified") + } + + const pkg = path.join(appOutDir, `${this.appName}-${this.metadata.version}.pkg`) + await BluebirdPromise.promisify(flat)(Object.assign({ + pkg: pkg, + identity: installerIdentity, + }, baseSignOptions)) + this.dispatchArtifactCreated(pkg, `${this.metadata.name}-${this.metadata.version}.pkg`) } } diff --git a/src/metadata.ts b/src/metadata.ts index b81e2fd8da9..5f90454249a 100755 --- a/src/metadata.ts +++ b/src/metadata.ts @@ -158,7 +158,7 @@ export interface OsXBuildOptions extends PlatformSpecificBuildOptions { readonly background?: string /* - Target package type: list of `default`, `dmg`, `zip`. + Target package type: list of `default`, `dmg`, `zip`, `mas`. */ readonly target?: Array } diff --git a/src/packager.ts b/src/packager.ts index 9942ee7a44e..700033ce398 100644 --- a/src/packager.ts +++ b/src/packager.ts @@ -35,9 +35,6 @@ export class Packager implements BuildInfo { //noinspection JSUnusedGlobalSymbols constructor(public options: PackagerOptions, public repositoryInfo: InfoRetriever = null) { this.projectDir = options.projectDir == null ? process.cwd() : path.resolve(options.projectDir) - if (this.appDir === this.projectDir) { - this.isTwoPackageJsonProjectLayoutUsed = false - } } artifactCreated(handler: (event: ArtifactCreated) => void): Packager { @@ -55,6 +52,9 @@ export class Packager implements BuildInfo { this.devMetadata = deepAssign(await readPackageJson(devPackageFile), this.options.devMetadata) this.appDir = await computeDefaultAppDirectory(this.projectDir, use(this.devMetadata.directories, it => it.app) || this.options.appDir) + + this.isTwoPackageJsonProjectLayoutUsed = this.appDir !== this.projectDir + const appPackageFile = this.projectDir === this.appDir ? devPackageFile : path.join(this.appDir, "package.json") this.metadata = appPackageFile === devPackageFile ? this.devMetadata : await readPackageJson(appPackageFile) this.checkMetadata(appPackageFile, devPackageFile, platforms) @@ -74,8 +74,7 @@ export class Packager implements BuildInfo { for (let arch of normalizeArchs(platform, this.options.arch)) { await this.installAppDependencies(platform, arch) // electron-packager uses productName in the directory name - const appOutDir = path.join(outDir, `${helper.appName}-${platform.nodeName}-${arch}`) - await helper.pack(outDir, appOutDir, arch) + const appOutDir = await helper.pack(outDir, arch) if (this.options.dist) { distTasks.push(helper.packageInDistributableFormat(outDir, appOutDir, arch)) } diff --git a/src/platformPackager.ts b/src/platformPackager.ts index 24eba5b9f48..5dfebfa0143 100644 --- a/src/platformPackager.ts +++ b/src/platformPackager.ts @@ -3,7 +3,7 @@ import { AppMetadata, DevMetadata, Platform, PlatformSpecificBuildOptions, getPr import EventEmitter = NodeJS.EventEmitter import { Promise as BluebirdPromise } from "bluebird" import * as path from "path" -import packager = require("electron-packager") +import packager = require("electron-packager-tf") import globby = require("globby") import { copy } from "fs-extra-p" import { statOrNull, use } from "./util" @@ -34,6 +34,9 @@ export interface PackagerOptions { csaLink?: string cscKeyPassword?: string + cscInstallerLink?: string + cscInstallerKeyPassword?: string + platformPackagerFactory?: (packager: Packager, platform: Platform, cleanupTasks: Array<() => Promise>) => PlatformPackager /** @@ -90,6 +93,10 @@ export abstract class PlatformPackager return use(this.devMetadata.directories, it => it.buildResources) || "build" } + protected computeAppOutDir(outDir: string, arch: string): string { + return path.join(outDir, `${this.appName}-${this.platform.nodeName}-${arch}`) + } + protected dispatchArtifactCreated(file: string, artifactName?: string) { this.info.eventEmitter.emit("artifactCreated", { file: file, @@ -98,12 +105,18 @@ export abstract class PlatformPackager }) } - async pack(outDir: string, appOutDir: string, arch: string): Promise { + async pack(outDir: string, arch: string): Promise { + const appOutDir = this.computeAppOutDir(outDir, arch) await this.doPack(outDir, appOutDir, arch) await this.copyExtraResources(appOutDir, arch) + return appOutDir + } + + protected beforePack(options: any): void { + // to override } - protected async doPack(outDir: string, appOutDir: string, arch: string) { + protected async doPack(outDir: string, appOutDir: string, arch: string): Promise { const version = this.metadata.version let buildVersion = version const buildNumber = this.computeBuildNumber() @@ -140,6 +153,7 @@ export abstract class PlatformPackager // this option only for windows-installer delete options.iconUrl + this.beforePack(options) await pack(options) const outStat = await statOrNull(appOutDir) diff --git a/src/winPackager.ts b/src/winPackager.ts index 2d9a150c8dc..597bf14ad50 100644 --- a/src/winPackager.ts +++ b/src/winPackager.ts @@ -52,14 +52,15 @@ export class WinPackager extends PlatformPackager { return iconPath } - async pack(outDir: string, appOutDir: string, arch: string): Promise { + async pack(outDir: string, arch: string): Promise { // we must check icon before pack because electron-packager uses icon and it leads to cryptic error message "spawn wine ENOENT" await this.iconPath if (!this.options.dist) { - return await super.pack(outDir, appOutDir, arch) + return await super.pack(outDir, arch) } + const appOutDir = this.computeAppOutDir(outDir, arch) const installerOut = computeDistOut(outDir, arch) log("Removing %s", installerOut) await BluebirdPromise.all([ @@ -78,6 +79,8 @@ export class WinPackager extends PlatformPackager { }) }) } + + return appOutDir } protected async doPack(outDir: string, appOutDir: string, arch: string) { diff --git a/test/README.md b/test/README.md index e288ed823cd..4c951d545d0 100755 --- a/test/README.md +++ b/test/README.md @@ -2,4 +2,7 @@ In addition to [required system packages](./multi-platform-build.md), on OS X `d # Inspect output if test uses temporary directory Set environment variable `TEST_APP_TMP_DIR` (e.g. `/tmp/electron-builder-test`). -Specified directory will be used instead of random temporary directory and *cleared* on each run. \ No newline at end of file +Specified directory will be used instead of random temporary directory and *cleared* on each run. + +# Test Code Signing Ceritificates +If test installer certificate is expired: http://security.stackexchange.com/questions/17909/how-to-create-an-apple-installer-package-signing-certificate \ No newline at end of file diff --git a/test/fixtures/test-app-one/package.json b/test/fixtures/test-app-one/package.json index 13d4461e79d..1bf44396fa9 100755 --- a/test/fixtures/test-app-one/package.json +++ b/test/fixtures/test-app-one/package.json @@ -10,7 +10,7 @@ "author": "Foo Bar ", "license": "MIT", "devDependencies": { - "electron-prebuilt": "^0.37.6" + "electron-prebuilt": "^0.37.7" }, "build": { "app-bundle-id": "your.id", diff --git a/test/fixtures/test-app/package.json b/test/fixtures/test-app/package.json index 3d33310dde1..a114c1d836c 100755 --- a/test/fixtures/test-app/package.json +++ b/test/fixtures/test-app/package.json @@ -4,7 +4,7 @@ "start": "electron ." }, "devDependencies": { - "electron-prebuilt": "^0.37.6" + "electron-prebuilt": "^0.37.7" }, "build": { "app-bundle-id": "your.id", diff --git a/test/src/BuildTest.ts b/test/src/BuildTest.ts index 651078647d6..84153cf946d 100755 --- a/test/src/BuildTest.ts +++ b/test/src/BuildTest.ts @@ -52,7 +52,7 @@ test("version from electron-prebuilt dependency", () => assertPack("test-app-one tempDirCreated: projectDir => { return BluebirdPromise.all([ outputJson(path.join(projectDir, "node_modules", "electron-prebuilt", "package.json"), { - version: "0.37.6" + version: "0.37.7" }), modifyPackageJson(projectDir, data => { data.devDependencies = {} diff --git a/test/src/CodeSignTest.ts b/test/src/CodeSignTest.ts index 619fe64c18c..bae8e7c62af 100644 --- a/test/src/CodeSignTest.ts +++ b/test/src/CodeSignTest.ts @@ -1,17 +1,31 @@ import { createKeychain, deleteKeychain, generateKeychainName } from "out/codeSign" import * as assertThat from "should/as-function" import test from "./helpers/avaEx" -import { CSC_NAME, CSC_LINK, CSC_KEY_PASSWORD } from "./helpers/codeSignData" +import { + CSC_NAME, CSC_LINK, + CSC_KEY_PASSWORD, + CSC_INSTALLER_KEY_PASSWORD, + CSC_INSTALLER_LINK +} from "./helpers/codeSignData" import { executeFinally, all } from "out/promise" //noinspection JSUnusedLocalSymbols const __awaiter = require("out/awaiter") -test.ifOsx("create keychain", async (t) => { +test.ifOsx("create keychain", async () => { const keychainName = generateKeychainName() await executeFinally(createKeychain(keychainName, CSC_LINK, CSC_KEY_PASSWORD) .then(result => { - assertThat(result.cscKeychainName).not.empty() - assertThat(result.cscName).equal(CSC_NAME) + assertThat(result.keychainName).not.empty() + assertThat(result.name).equal(CSC_NAME) + }), () => all([deleteKeychain(keychainName)])) +}) + +test.ifOsx("create keychain with installers", async () => { + const keychainName = generateKeychainName() + await executeFinally(createKeychain(keychainName, CSC_LINK, CSC_KEY_PASSWORD, CSC_INSTALLER_LINK, CSC_INSTALLER_KEY_PASSWORD) + .then(result => { + assertThat(result.keychainName).not.empty() + assertThat(result.name).equal(CSC_NAME) }), () => all([deleteKeychain(keychainName)])) }) \ No newline at end of file diff --git a/test/src/helpers/codeSignData.ts b/test/src/helpers/codeSignData.ts index f41aed0c1bd..c655353961e 100644 --- a/test/src/helpers/codeSignData.ts +++ b/test/src/helpers/codeSignData.ts @@ -1,3 +1,7 @@ export const CSC_LINK = "https://www.dropbox.com/s/86zaffzbao198xe/test.p12?dl=1" export const CSC_KEY_PASSWORD = "password" + +export const CSC_INSTALLER_LINK = "https://www.dropbox.com/s/2drwf5owgoqxkr3/test-installer.p12?dl=1" +export const CSC_INSTALLER_KEY_PASSWORD = "password" + export const CSC_NAME = "Test Test" \ No newline at end of file diff --git a/test/src/helpers/packTester.ts b/test/src/helpers/packTester.ts index cab98045812..cf7a058fabb 100755 --- a/test/src/helpers/packTester.ts +++ b/test/src/helpers/packTester.ts @@ -2,7 +2,7 @@ import { copy, emptyDir, remove, writeJson, readJson, readFile } from "fs-extra- import * as assertThat from "should/as-function" import * as path from "path" import { parse as parsePlist } from "plist" -import { CSC_LINK, CSC_KEY_PASSWORD } from "./codeSignData" +import { CSC_LINK, CSC_KEY_PASSWORD, CSC_INSTALLER_LINK, CSC_INSTALLER_KEY_PASSWORD } from "./codeSignData" import { expectedLinuxContents, expectedWinContents } from "./expectedContents" import { Packager, PackagerOptions, Platform, getProductName, ArtifactCreated } from "out" import { exec } from "out/util" @@ -62,6 +62,8 @@ export async function assertPack(fixtureName: string, packagerOptions: PackagerO projectDir: projectDir, cscLink: CSC_LINK, cscKeyPassword: CSC_KEY_PASSWORD, + cscInstallerLink: CSC_INSTALLER_LINK, + cscInstallerKeyPassword: CSC_INSTALLER_KEY_PASSWORD, dist: true, }, packagerOptions), checkOptions) diff --git a/test/src/helpers/runTests.ts b/test/src/helpers/runTests.ts index 66ee98ce451..e4da7448a25 100755 --- a/test/src/helpers/runTests.ts +++ b/test/src/helpers/runTests.ts @@ -2,6 +2,7 @@ import { spawn } from "child_process" import * as path from "path" import { Promise as BluebirdPromise } from "bluebird" import * as fs from "fs-extra-p" +import { Platform } from "out/metadata"; // we set NODE_PATH in this file, so, we cannot use 'out/awaiter' path here //noinspection JSUnusedLocalSymbols @@ -14,7 +15,7 @@ const rootDir = path.join(__dirname, "..", "..", "..") const testPackageDir = path.join(require("os").tmpdir(), "electron_builder_published") const testNodeModules = path.join(testPackageDir, "node_modules") -const electronVersion = "0.37.6" +const electronVersion = "0.37.7" BluebirdPromise.all([ deleteOldElectronVersion(), @@ -65,12 +66,18 @@ function deleteOldElectronVersion(): Promise { function downloadAllRequiredElectronVersions(): Promise { const downloadPromises: Array> = [] - for (let platform of packager.normalizePlatforms(["all"])) { - for (let arch of packager.normalizeArchs(platform)) { + + const platforms = packager.normalizePlatforms(["all"]).map((it: Platform) => it.nodeName) + if (process.platform === "darwin") { + platforms.push("mas") + } + + for (let platform of platforms) { + for (let arch of (platform === "mas" || platform === "darwin" ? ["x64"] : ["ia32", "x64"])) { downloadPromises.push(downloadElectron({ version: electronVersion, arch: arch, - platform: platform.nodeName, + platform: platform, })) } } diff --git a/test/src/osxPackagerTest.ts b/test/src/osxPackagerTest.ts index b9428f1e7eb..ae2a57b1cb9 100644 --- a/test/src/osxPackagerTest.ts +++ b/test/src/osxPackagerTest.ts @@ -11,12 +11,12 @@ import * as assertThat from "should/as-function" //noinspection JSUnusedLocalSymbols const __awaiter = require("out/awaiter") -test.ifOsx("two-package.json", () => assertPack("test-app", { +test.ifOsx("two-package", () => assertPack("test-app", { platform: [Platform.OSX], arch: "all", })) -test.ifOsx("one-package.json", () => assertPack("test-app-one", platform(Platform.OSX))) +test.ifOsx("one-package", () => assertPack("test-app-one", platform(Platform.OSX))) function createTargetTest(target: string, expectedContents: Array) { return () => assertPack("test-app-one", { @@ -38,6 +38,8 @@ test.ifOsx("only dmg", createTargetTest("dmg", ["TestApp-1.1.0.dmg"])) test.ifOsx("only zip", createTargetTest("zip", ["TestApp-1.1.0-mac.zip"])) test.ifOsx("invalid target", (t: any) => t.throws(createTargetTest("ttt", [])(), "Unknown target: ttt")) +test.ifOsx("mas", createTargetTest("mas", ["TestApp-1.1.0.pkg"])) + // test.ifOsx("no background", (t: any) => assertPack("test-app-one", platform(Platform.OSX), { // tempDirCreated: projectDir => deleteFile(path.join(projectDir, "build", "background.png")) // })) @@ -73,8 +75,9 @@ class CheckingOsXPackager extends OsXPackager { super(info, cleanupTasks) } - async pack(outDir: string, appOutDir: string, arch: string): Promise { + async pack(outDir: string, arch: string): Promise { // skip pack + return this.computeAppOutDir(outDir, arch) } async packageInDistributableFormat(outDir: string, appOutDir: string, arch: string): Promise { diff --git a/test/src/winPackagerTest.ts b/test/src/winPackagerTest.ts index a568cdc7c02..b264777bbbb 100755 --- a/test/src/winPackagerTest.ts +++ b/test/src/winPackagerTest.ts @@ -78,8 +78,9 @@ class CheckingWinPackager extends WinPackager { super(info, cleanupTasks) } - async pack(outDir: string, appOutDir: string, arch: string): Promise { + async pack(outDir: string, arch: string): Promise { // skip pack + return this.computeAppOutDir(outDir, arch) } async packageInDistributableFormat(outDir: string, appOutDir: string, arch: string): Promise { diff --git a/tsconfig.json b/tsconfig.json index a0444327be6..8a3da7e9bdb 100755 --- a/tsconfig.json +++ b/tsconfig.json @@ -28,7 +28,8 @@ "node_modules/typescript/lib/lib.es7.d.ts", "node_modules/fs-extra-p/index.d.ts", "node_modules/7zip-bin/index.d.ts", - "node_modules/fs-extra-p/bluebird.d.ts" + "node_modules/fs-extra-p/bluebird.d.ts", + "node_modules/electron-osx-sign-tf/index.d.ts" ], "files": [ "typings/appdmg.d.ts", @@ -51,6 +52,7 @@ "node_modules/fs-extra-p/index.d.ts", "node_modules/7zip-bin/index.d.ts", "node_modules/fs-extra-p/bluebird.d.ts", + "node_modules/electron-osx-sign-tf/index.d.ts", "src/awaiter.ts", "src/build-cli.ts", "src/builder.ts", diff --git a/typings/electron-packager.d.ts b/typings/electron-packager.d.ts index a25e89fc8a7..b6a167332b9 100644 --- a/typings/electron-packager.d.ts +++ b/typings/electron-packager.d.ts @@ -105,7 +105,7 @@ declare namespace ElectronPackager { } } -declare module "electron-packager" { +declare module "electron-packager-tf" { const packager: ElectronPackager.Packager; export = packager; }