diff --git a/.idea/dictionaries/develar.xml b/.idea/dictionaries/develar.xml
index 4aa9c9a9966..72dd05273a1 100644
--- a/.idea/dictionaries/develar.xml
+++ b/.idea/dictionaries/develar.xml
@@ -73,6 +73,7 @@
nuget
nupkg
nuspec
+ osslsigncode
packagejson
pacman
passin
@@ -83,9 +84,11 @@
progexe
promisify
psmdcp
+ readpass
repos
rimraf
semver
+ signtool
templating
testapp
tsconfig
diff --git a/docker/winSign.sh b/docker/winSign.sh
new file mode 100755
index 00000000000..9a48e8bc86a
--- /dev/null
+++ b/docker/winSign.sh
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+dir=${PWD##*/}
+rm -rf ../${dir}.7z
+7za a -m0=lzma2 -mx=9 -mfb=64 -md=64m -ms=on ../winCodeSign.7z .
diff --git a/package.json b/package.json
index 325a3bab425..85b804bc446 100644
--- a/package.json
+++ b/package.json
@@ -17,7 +17,7 @@
"compile": "npm run compile-production && npm run compile-test",
"compile-production": "ts-babel",
"compile-test": "ts-babel test",
- "lint": "tslint src/**/*.ts test/src/**/*.ts",
+ "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 && npm publish && semantic-release post",
@@ -87,8 +87,8 @@
"rcedit": "^0.5.1",
"sanitize-filename": "^1.6.0",
"semver": "^5.3.0",
- "signcode-tf": "~0.7.5",
"source-map-support": "^0.4.2",
+ "tslint": "^3.14.0-dev.1",
"typescript": "2.0.0-dev.20160705",
"update-notifier": "^1.0.2",
"uuid-1345": "^0.99.6",
diff --git a/src/asarUtil.ts b/src/asarUtil.ts
index 32489c233e9..c8d0a273907 100644
--- a/src/asarUtil.ts
+++ b/src/asarUtil.ts
@@ -6,7 +6,6 @@ import {
} from "fs-extra-p"
import { Promise as BluebirdPromise } from "bluebird"
import * as path from "path"
-import pathSorter = require("path-sort")
import { log } from "./util/log"
import { Minimatch } from "minimatch"
import { deepAssign } from "./util/deepAssign"
@@ -85,16 +84,16 @@ export async function createAsarArchive(src: string, resourcesPath: string, opti
}
function isUnpackDir(path: string, pattern: Minimatch, rawPattern: string): boolean {
- return path.indexOf(rawPattern) === 0 || pattern.match(path)
+ return path.startsWith(rawPattern) || pattern.match(path)
}
async function order(src: string, filenames: Array, options: any) {
- const orderingFiles = (await readFile(options.ordering, "utf8")).split("\n").map(function (line) {
- if (line.indexOf(':') !== -1) {
- line = line.split(':').pop()!
+ const orderingFiles = (await readFile(options.ordering, "utf8")).split("\n").map(line => {
+ if (line.indexOf(":") !== -1) {
+ line = line.split(":").pop()!
}
line = line.trim()
- if (line[0] === '/') {
+ if (line[0] === "/") {
line = line.slice(1)
}
return line
@@ -121,7 +120,7 @@ async function order(src: string, filenames: Array, options: any) {
for (let file of filenames) {
if (!filenamesSorted.includes(file)) {
filenamesSorted.push(file)
- missing += 1;
+ missing += 1
}
}
log(`Ordering file has ${((total - missing) / total * 100)}% coverage.`)
@@ -149,7 +148,7 @@ async function detectUnpackedDirs(src: string, files: Array, metadata: M
const nodeModuleDir = file.substring(0, nextSlashIndex)
- if (file.length == (nodeModuleDir.length + 1 + packageJsonStringLength) && file.endsWith("package.json")) {
+ if (file.length === (nodeModuleDir.length + 1 + packageJsonStringLength) && file.endsWith("package.json")) {
const promise = readJson(file)
if (readPackageJsonPromises.length > MAX_FILE_REQUESTS) {
@@ -162,7 +161,7 @@ async function detectUnpackedDirs(src: string, files: Array, metadata: M
if (autoUnpackDirs.has(nodeModuleDir)) {
const fileParent = path.dirname(file)
- if (fileParent != nodeModuleDir && !autoUnpackDirs.has(fileParent)) {
+ if (fileParent !== nodeModuleDir && !autoUnpackDirs.has(fileParent)) {
autoUnpackDirs.add(fileParent)
if (createDirPromises.length > MAX_FILE_REQUESTS) {
@@ -190,7 +189,7 @@ async function detectUnpackedDirs(src: string, files: Array, metadata: M
log(`${path.relative(src, nodeModuleDir)} is not packed into asar archive - contains executable code`)
autoUnpackDirs.add(nodeModuleDir)
const fileParent = path.dirname(file)
- if (fileParent != nodeModuleDir) {
+ if (fileParent !== nodeModuleDir) {
autoUnpackDirs.add(fileParent)
// create parent dir to be able to copy file later without directory existence check
createDirPromises.push(ensureDir(path.join(unpackedDest, path.relative(src, fileParent))))
diff --git a/src/builder.ts b/src/builder.ts
index cc0d45ffd32..a130536176b 100644
--- a/src/builder.ts
+++ b/src/builder.ts
@@ -168,7 +168,7 @@ export function normalizeOptions(args: CliOptions): BuildOptions {
delete r.windows
delete r.osx
delete r.macos
- delete r["$0"]
+ delete r.$0
delete r._
delete r.version
delete r.help
diff --git a/src/cliOptions.ts b/src/cliOptions.ts
index 9b57b0743b4..9ae31c4f1df 100644
--- a/src/cliOptions.ts
+++ b/src/cliOptions.ts
@@ -74,7 +74,7 @@ export function createYargs(): any {
choices: ["ia32", "x64", "all"],
})
.option("extraMetadata", {
- alias: ["em",],
+ alias: ["em"],
group: buildGroup,
describe: "Inject properties to application package.json (asar only)",
})
diff --git a/src/macPackager.ts b/src/macPackager.ts
index 1473b7dc295..11710908fb0 100644
--- a/src/macPackager.ts
+++ b/src/macPackager.ts
@@ -86,7 +86,7 @@ export default class MacPackager extends PlatformPackager {
if (hasMas) {
// osx-sign - disable warning
const appOutDir = path.join(outDir, "mas")
- const masBuildOptions = deepAssign({}, this.platformSpecificBuildOptions, (this.devMetadata.build)["mas"])
+ const masBuildOptions = deepAssign({}, this.platformSpecificBuildOptions, (this.devMetadata.build).mas)
//noinspection JSUnusedGlobalSymbols
await this.doPack(packOptions, outDir, appOutDir, "mas", arch, masBuildOptions)
await this.sign(appOutDir, masBuildOptions)
diff --git a/src/metadata.ts b/src/metadata.ts
index a6b3c93c556..2ce230676a9 100755
--- a/src/metadata.ts
+++ b/src/metadata.ts
@@ -559,9 +559,10 @@ export class Platform {
case Platform.LINUX.nodeName:
return Platform.LINUX
- }
- throw new Error(`Unknown platform: ${name}`)
+ default:
+ throw new Error(`Unknown platform: ${name}`)
+ }
}
}
diff --git a/src/winPackager.ts b/src/winPackager.ts
index 2320c64eedf..abd4ebce838 100644
--- a/src/winPackager.ts
+++ b/src/winPackager.ts
@@ -5,7 +5,7 @@ import { Platform, WinBuildOptions, Arch } from "./metadata"
import * as path from "path"
import { log, task } from "./util/log"
import { deleteFile, open, close, read } from "fs-extra-p"
-import { sign, SignOptions } from "signcode-tf"
+import { sign, SignOptions } from "./windowsCodeSign"
import SquirrelWindowsTarget from "./targets/squirrelWindows"
import NsisTarget from "./targets/nsis"
import { DEFAULT_TARGET, createCommonTarget, DIR_TARGET } from "./targets/targetFactory"
@@ -123,7 +123,6 @@ export class WinPackager extends PlatformPackager {
password: cscInfo.password!,
name: this.appInfo.productName,
site: await this.appInfo.computePackageUrl(),
- overwrite: true,
hash: this.platformSpecificBuildOptions.signingHashAlgorithms,
})
}
@@ -131,7 +130,7 @@ export class WinPackager extends PlatformPackager {
//noinspection JSMethodCanBeStatic
protected async doSign(opts: SignOptions): Promise {
- return BluebirdPromise.promisify(sign)(opts)
+ return sign(opts)
}
protected packageInDistributableFormat(outDir: string, appOutDir: string, arch: Arch, targets: Array, promises: Array>): void {
diff --git a/src/windowsCodeSign.ts b/src/windowsCodeSign.ts
new file mode 100644
index 00000000000..9b33c4744f2
--- /dev/null
+++ b/src/windowsCodeSign.ts
@@ -0,0 +1,142 @@
+import { spawn } from "./util/util"
+import { rename } from "fs-extra-p"
+import * as path from "path"
+import { release } from "os"
+import { getBin } from "./util/binDownload"
+//noinspection JSUnusedLocalSymbols
+const __awaiter = require("./util/awaiter")
+
+const TOOLS_VERSION = "winCodeSign-1.0.0"
+
+export interface SignOptions {
+ path: string
+ cert: string
+ name?: string | null
+ password: string
+ site?: string | null
+ hash?: Array | null
+}
+
+export async function sign(options: SignOptions) {
+ let hashes = options.hash
+ if (hashes == null) {
+ hashes = ["sha1", "sha256"]
+ }
+ else {
+ hashes = Array.isArray(hashes) ? hashes.slice() : [hashes]
+ }
+
+ const isWin = process.platform === "win32"
+ let nest = false
+ //noinspection JSUnusedAssignment
+ let outputPath = ""
+ for (let hash of hashes) {
+ outputPath = isWin ? options.path : getOutputPath(options.path, hash)
+ await spawnSign(options, options.path, outputPath, hash, nest)
+ nest = true
+ if (!isWin) {
+ await rename(outputPath, options.path)
+ }
+ }
+}
+
+// on windows be aware of http://stackoverflow.com/a/32640183/1910191
+async function spawnSign(options: any, inputPath: string, outputPath: string, hash: string, nest: boolean) {
+ const timestampingServiceUrl = "http://timestamp.verisign.com/scripts/timstamp.dll"
+ const isWin = process.platform === "win32"
+ const args = isWin ? [
+ "sign",
+ nest || hash === "sha256" ? "/tr" : "/t", nest || hash === "sha256" ? "http://timestamp.comodoca.com/rfc3161" : timestampingServiceUrl
+ ] : [
+ "-in", inputPath,
+ "-out", outputPath,
+ "-t", timestampingServiceUrl
+ ]
+
+ const certExtension = path.extname(options.cert)
+ if (certExtension === ".p12" || certExtension === ".pfx") {
+ args.push(isWin ? "/f" : "-pkcs12", options.cert)
+ }
+ else {
+ args.push(isWin ? "/f" : "-certs", options.cert)
+ // todo win maybe incorrect
+ args.push(isWin ? "/csp" : "-key", options.key)
+ }
+
+ if (!isWin || hash !== "sha1") {
+ args.push(isWin ? "/fd" : "-h", hash)
+ if (isWin) {
+ args.push("/td", "sha256")
+ }
+ }
+
+ if (options.name) {
+ args.push(isWin ? "/d" : "-n", options.name)
+ }
+
+ if (options.site) {
+ args.push(isWin ? "/du" : "-i", options.site)
+ }
+
+ if (nest) {
+ args.push(isWin ? "/as" : "-nest")
+ }
+
+ if (options.password) {
+ args.push(isWin ? "/p" : "-pass", options.password)
+ }
+
+ if (options.passwordPath) {
+ if (isWin) {
+ throw new Error("-readpass is not supported on Windows")
+ }
+ args.push("-readpass", options.passwordPath)
+ }
+
+ if (isWin) {
+ // must be last argument
+ args.push(inputPath)
+ }
+
+ return await spawn(await getToolPath(options), args, {
+ stdio: ["ignore", "ignore", "inherit"],
+ })
+}
+
+// async function verify(options: any) {
+// const out = await exec(await getToolPath(options), [
+// "verify",
+// "-in", options.path,
+// "-require-leaf-hash", options.hash
+// ])
+// if (out.includes("No signature found.")) {
+// throw new Error("No signature found")
+// }
+// else if (out.includes("Leaf hash match: failed")) {
+// throw new Error("Leaf hash match failed")
+// }
+// }
+
+function getOutputPath(inputPath: string, hash: string) {
+ const extension = path.extname(inputPath)
+ return path.join(path.dirname(inputPath), `${path.basename(inputPath, extension)}-signed-${hash}${extension}`)
+}
+
+async function getToolPath(options: any) {
+ let result = options.signcodePath || process.env.SIGNTOOL_PATH
+ if (result) {
+ return result
+ }
+
+ if (process.env.USE_SYSTEM_SIGNCODE || process.platform === "linux") {
+ return "osslsigncode"
+ }
+
+ const vendorPath = await getBin("winCodeSign", TOOLS_VERSION, `https://dl.bintray.com/electron-userland/bin/${TOOLS_VERSION}.7z`, "0e524943dd6f03a9cee4cdaaa235e27d4945a1c9dc80ccee8ff1219e7b73ad88")
+ if (process.platform === "win32") {
+ return path.join(vendorPath, `windows-${(release().startsWith("6.") ? "6" : "10")}`, "signtool.exe")
+ }
+ else {
+ return path.join(vendorPath, process.platform, "osslsigncode")
+ }
+}
diff --git a/test/src/winPackagerTest.ts b/test/src/winPackagerTest.ts
index 3d510269a7f..8c4c6fd4942 100755
--- a/test/src/winPackagerTest.ts
+++ b/test/src/winPackagerTest.ts
@@ -1,4 +1,4 @@
-import { Platform, Arch, BuildInfo, PackagerOptions } from "out"
+import { Platform, Arch, BuildInfo } from "out"
import test from "./helpers/avaEx"
import { assertPack, platform, modifyPackageJson, signed, getTestAsset } from "./helpers/packTester"
import { outputFile, rename, copy } from "fs-extra-p"
@@ -6,7 +6,7 @@ import * as path from "path"
import { WinPackager } from "out/winPackager"
import { Promise as BluebirdPromise } from "bluebird"
import { assertThat } from "./helpers/fileAssert"
-import { SignOptions } from "signcode-tf"
+import { SignOptions } from "out/windowsCodeSign"
import SquirrelWindowsTarget from "out/targets/squirrelWindows"
import { Target } from "out/platformPackager"
import { ElectronPackagerOptions } from "out/packager/dirPackager"
@@ -14,23 +14,15 @@ import { ElectronPackagerOptions } from "out/packager/dirPackager"
//noinspection JSUnusedLocalSymbols
const __awaiter = require("out/util/awaiter")
-function _signed(packagerOptions: PackagerOptions): PackagerOptions {
- if (process.platform !== "win32") {
- // todo Linux Signing failed with SIGBUS
- return packagerOptions
- }
- return signed(packagerOptions)
-}
-
-test.ifNotCiOsx("win", () => assertPack("test-app-one", _signed({
+test.ifNotCiOsx("win", () => assertPack("test-app-one", signed({
targets: Platform.WINDOWS.createTarget(["default", "zip"]),
})
))
-test("nsis", () => assertPack("test-app-one", _signed({
+test("nsis", () => assertPack("test-app-one", signed({
targets: Platform.WINDOWS.createTarget(["nsis"], Arch.ia32, Arch.x64),
}), {
- useTempDir: true,
+ useTempDir: true,
}
))
@@ -52,7 +44,7 @@ test.ifDevOrLinuxCi("nsis 32 perMachine, no run after finish", () => assertPack(
}
}))
-test.ifNotCiOsx("nsis boring", () => assertPack("test-app-one", _signed({
+test.ifNotCiOsx("nsis boring", () => assertPack("test-app-one", signed({
targets: Platform.WINDOWS.createTarget(["nsis"]),
devMetadata: {
build: {
@@ -62,7 +54,6 @@ test.ifNotCiOsx("nsis boring", () => assertPack("test-app-one", _signed({
}
}
})))
-
test.ifNotCiOsx("nsis, installerHeaderIcon", () => {
let headerIconPath: string | null = null
return assertPack("test-app-one", {
diff --git a/tslint.json b/tslint.json
index ca7b1797ad5..cca20347622 100644
--- a/tslint.json
+++ b/tslint.json
@@ -56,6 +56,8 @@
"check-operator",
"check-separator",
"check-type"
- ]
+ ],
+ "no-bitwise": false,
+ "jsdoc-format": false
}
}
\ No newline at end of file
diff --git a/typings/signcode.d.ts b/typings/signcode.d.ts
deleted file mode 100644
index 930b847ea2b..00000000000
--- a/typings/signcode.d.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-declare module "signcode-tf" {
- export interface SignOptions {
- path: string
- cert: string
- name?: string | null
- password: string
- site?: string | null
- hash?: Array | null
- overwrite?: boolean
- }
-
- export function sign(options: SignOptions, callback: (error: Error | null) => void): void
-}
\ No newline at end of file