From 9134f61eaab707f8ad651928eee92d944a92317c Mon Sep 17 00:00:00 2001 From: develar Date: Wed, 20 Apr 2016 07:42:11 +0200 Subject: [PATCH] feat: Windows code signing from OS X Closes #314 --- docs/Multi Platform Build.md | 2 +- package.json | 3 +- src/macPackager.ts | 3 +- src/winPackager.ts | 46 ++++++++++++++++++------- test/fixtures/test-app-one/package.json | 2 +- test/fixtures/test-app/package.json | 2 +- test/src/BuildTest.ts | 2 +- test/src/helpers/runTests.ts | 2 +- test/tsconfig.json | 1 + tsconfig.json | 1 + typings/signcode.d.ts | 13 +++++++ 11 files changed, 56 insertions(+), 21 deletions(-) create mode 100644 typings/signcode.d.ts diff --git a/docs/Multi Platform Build.md b/docs/Multi Platform Build.md index 7b6ec9d544c..62084a5144c 100755 --- a/docs/Multi Platform Build.md +++ b/docs/Multi Platform Build.md @@ -4,7 +4,7 @@ Don't expect that you can build app for all platforms on one platform. [prebuild](https://www.npmjs.com/package/prebuild) is a solution, but most node modules [don't provide](https://github.com/atom/node-keytar/issues/27) prebuilt binaries. * OS Code Signing works only OS X. [Cannot be fixed](http://stackoverflow.com/a/12156576). -* Windows Code Signing works only on Windows. We are going [to fix it](https://developer.mozilla.org/en/docs/Signing_an_executable_with_Authenticode) soon. +* Windows Code Signing doesn't work on Linux. We are going to fix it soon. Don't think that mentioned issues are major, you should use build servers — e.g. [AppVeyor](http://www.appveyor.com/) to build Windows app and [Travis](https://travis-ci.org) to build OS X/Linux apps. diff --git a/package.json b/package.json index 3d2725d0fb1..eec60689911 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "command-line-args": "^2.1.6", "deep-assign": "^2.0.0", "electron-packager": "^7.0.0", - "electron-winstaller-fixed": "~2.3.0-beta.1", + "electron-winstaller-fixed": "~2.3.0-beta.4", "fs-extra": "^0.28.0", "fs-extra-p": "^0.2.0", "globby": "^4.0.0", @@ -70,6 +70,7 @@ "progress": "^1.1.8", "progress-stream": "^1.2.0", "read-package-json": "^2.0.3", + "signcode": "^0.4.0", "source-map-support": "^0.4.0", "tmp": "0.0.28" }, diff --git a/src/macPackager.ts b/src/macPackager.ts index c6fb456591e..2291e8749fe 100644 --- a/src/macPackager.ts +++ b/src/macPackager.ts @@ -33,8 +33,7 @@ export default class MacPackager extends PlatformPackager { async pack(outDir: string, appOutDir: string, arch: string): Promise { await super.pack(outDir, appOutDir, arch) - const codeSigningInfo = await this.codeSigningInfo - await this.signMac(path.join(appOutDir, this.appName + ".app"), codeSigningInfo) + await this.signMac(path.join(appOutDir, this.appName + ".app"), await this.codeSigningInfo) } private signMac(distPath: string, codeSigningInfo: CodeSigningInfo): Promise { diff --git a/src/winPackager.ts b/src/winPackager.ts index 4a4a695041f..8e75e21907b 100644 --- a/src/winPackager.ts +++ b/src/winPackager.ts @@ -5,6 +5,7 @@ import { Platform, WinBuildOptions } from "./metadata" import * as path from "path" import { log, statOrNull } from "./util" import { readFile, deleteFile, stat, rename, copy, emptyDir, writeFile, open, close, read } from "fs-extra-p" +import { sign } from "signcode" //noinspection JSUnusedLocalSymbols const __awaiter = require("./awaiter") @@ -21,9 +22,7 @@ export class WinPackager extends PlatformPackager { constructor(info: BuildInfo, cleanupTasks: Array<() => Promise>) { super(info) - // https://developer.mozilla.org/en-US/docs/Signing_an_executable_with_Authenticode - // https://github.com/Squirrel/Squirrel.Windows/pull/505 - if (this.options.cscLink != null && this.options.cscKeyPassword != null && process.platform !== "darwin") { + if (this.options.cscLink != null && this.options.cscKeyPassword != null) { this.certFilePromise = downloadCertificate(this.options.cscLink) .then(path => { cleanupTasks.push(() => deleteFile(path, true)) @@ -58,16 +57,15 @@ export class WinPackager extends PlatformPackager { await this.iconPath if (!this.options.dist) { - return super.pack(outDir, appOutDir, arch) + return await super.pack(outDir, appOutDir, arch) } const installerOut = computeDistOut(outDir, arch) log("Removing %s", installerOut) - await - BluebirdPromise.all([ - this.doPack(outDir, appOutDir, arch), - emptyDir(installerOut) - ]) + await BluebirdPromise.all([ + this.doPack(outDir, appOutDir, arch), + emptyDir(installerOut) + ]) const extraResources = await this.copyExtraResources(appOutDir, arch) if (extraResources.length > 0) { @@ -82,6 +80,24 @@ export class WinPackager extends PlatformPackager { } } + protected async doPack(outDir: string, appOutDir: string, arch: string) { + await super.doPack(outDir, appOutDir, arch) + + if (process.platform === "darwin" && this.options.cscLink != null && this.options.cscKeyPassword != null) { + const filename = this.appName + ".exe" + log(`Signing ${filename}`) + await BluebirdPromise.promisify(sign)({ + path: path.join(appOutDir, filename), + cert: await this.certFilePromise, + password: this.options.cscKeyPassword, + name: this.appName, + site: await this.computePackageUrl(), + hash: ["sha256"], + overwrite: true, + }) + } + } + protected async computeEffectiveDistOptions(appOutDir: string, installerOutDir: string): Promise { let iconUrl = this.devMetadata.build.iconUrl if (!iconUrl) { @@ -98,11 +114,9 @@ export class WinPackager extends PlatformPackager { } } - const certificateFile = await this.certFilePromise - const projectUrl = await this.computePackageUrl() - use(this.customBuildOptions, checkConflictingOptions) + const projectUrl = await this.computePackageUrl() const options: any = Object.assign({ name: this.metadata.name, productName: this.appName, @@ -115,7 +129,7 @@ export class WinPackager extends PlatformPackager { authors: this.metadata.author.name, iconUrl: iconUrl, setupIcon: await this.iconPath, - certificateFile: certificateFile, + certificateFile: await this.certFilePromise, certificatePassword: this.options.cscKeyPassword, fixUpPaths: false, skipUpdateIcon: true, @@ -123,6 +137,12 @@ export class WinPackager extends PlatformPackager { noMsi: true, extraFileSpecs: this.extraNuGetFileSources == null ? null : ("\n" + (await this.extraNuGetFileSources).join("\n")), extraMetadataSpecs: projectUrl == null ? null : `\n${projectUrl}`, + sign: { + name: this.appName, + site: projectUrl, + hash: ["sha256"], + overwrite: true, + } }, this.customBuildOptions) if (this.loadingGifStat != null) { diff --git a/test/fixtures/test-app-one/package.json b/test/fixtures/test-app-one/package.json index 581fc0d9985..13d4461e79d 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.5" + "electron-prebuilt": "^0.37.6" }, "build": { "app-bundle-id": "your.id", diff --git a/test/fixtures/test-app/package.json b/test/fixtures/test-app/package.json index adbf45ec20e..3d33310dde1 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.5" + "electron-prebuilt": "^0.37.6" }, "build": { "app-bundle-id": "your.id", diff --git a/test/src/BuildTest.ts b/test/src/BuildTest.ts index 88fcea92d72..c77c8dbce90 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.5" + version: "0.37.6" }), modifyPackageJson(projectDir, data => { data.devDependencies = {} diff --git a/test/src/helpers/runTests.ts b/test/src/helpers/runTests.ts index 52cb7cd19ee..66ee98ce451 100755 --- a/test/src/helpers/runTests.ts +++ b/test/src/helpers/runTests.ts @@ -14,7 +14,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.5" +const electronVersion = "0.37.6" BluebirdPromise.all([ deleteOldElectronVersion(), diff --git a/test/tsconfig.json b/test/tsconfig.json index 23c823b053d..de59eee8bd7 100755 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -41,6 +41,7 @@ "../typings/node.d.ts", "../typings/progress-stream.d.ts", "../typings/read-package-json.d.ts", + "../typings/signcode.d.ts", "typings/ava.d.ts", "typings/decompress-zip.d.ts", "typings/json-parse-helpfulerror.d.ts", diff --git a/tsconfig.json b/tsconfig.json index e78fd7135a3..518f5dcec89 100755 --- a/tsconfig.json +++ b/tsconfig.json @@ -45,6 +45,7 @@ "typings/node.d.ts", "typings/progress-stream.d.ts", "typings/read-package-json.d.ts", + "typings/signcode.d.ts", "node_modules/typescript/lib/lib.es7.d.ts", "node_modules/fs-extra-p/index.d.ts", "node_modules/7zip-bin/index.d.ts", diff --git a/typings/signcode.d.ts b/typings/signcode.d.ts new file mode 100644 index 00000000000..6e1ed17bfc5 --- /dev/null +++ b/typings/signcode.d.ts @@ -0,0 +1,13 @@ +declare module "signcode" { + export interface SignOptions { + path: string + cert: string + name?: string + password: string + site?: string + hash?: Array + overwrite?: boolean + } + + export function sign(options: SignOptions, callback: (error: Error) => void): void +} \ No newline at end of file