Skip to content

Commit

Permalink
fix: Failing to sign the Windows build on Linux
Browse files Browse the repository at this point in the history
Closes #578
  • Loading branch information
develar committed Aug 1, 2016
1 parent 6a5c4bb commit a6a0cd6
Show file tree
Hide file tree
Showing 13 changed files with 178 additions and 49 deletions.
3 changes: 3 additions & 0 deletions .idea/dictionaries/develar.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions docker/winSign.sh
Original file line number Diff line number Diff line change
@@ -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 .
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
19 changes: 9 additions & 10 deletions src/asarUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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<string>, 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
Expand All @@ -121,7 +120,7 @@ async function order(src: string, filenames: Array<string>, 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.`)
Expand Down Expand Up @@ -149,7 +148,7 @@ async function detectUnpackedDirs(src: string, files: Array<string>, 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) {
Expand All @@ -162,7 +161,7 @@ async function detectUnpackedDirs(src: string, files: Array<string>, 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) {
Expand Down Expand Up @@ -190,7 +189,7 @@ async function detectUnpackedDirs(src: string, files: Array<string>, 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))))
Expand Down
2 changes: 1 addition & 1 deletion src/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/cliOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)",
})
Expand Down
2 changes: 1 addition & 1 deletion src/macPackager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export default class MacPackager extends PlatformPackager<MacOptions> {
if (hasMas) {
// osx-sign - disable warning
const appOutDir = path.join(outDir, "mas")
const masBuildOptions = deepAssign({}, this.platformSpecificBuildOptions, (<any>this.devMetadata.build)["mas"])
const masBuildOptions = deepAssign({}, this.platformSpecificBuildOptions, (<any>this.devMetadata.build).mas)
//noinspection JSUnusedGlobalSymbols
await this.doPack(packOptions, outDir, appOutDir, "mas", arch, masBuildOptions)
await this.sign(appOutDir, masBuildOptions)
Expand Down
5 changes: 3 additions & 2 deletions src/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`)
}
}
}

Expand Down
5 changes: 2 additions & 3 deletions src/winPackager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -123,15 +123,14 @@ export class WinPackager extends PlatformPackager<WinBuildOptions> {
password: cscInfo.password!,
name: this.appInfo.productName,
site: await this.appInfo.computePackageUrl(),
overwrite: true,
hash: this.platformSpecificBuildOptions.signingHashAlgorithms,
})
}
}

//noinspection JSMethodCanBeStatic
protected async doSign(opts: SignOptions): Promise<any> {
return BluebirdPromise.promisify(sign)(opts)
return sign(opts)
}

protected packageInDistributableFormat(outDir: string, appOutDir: string, arch: Arch, targets: Array<Target>, promises: Array<Promise<any>>): void {
Expand Down
142 changes: 142 additions & 0 deletions src/windowsCodeSign.ts
Original file line number Diff line number Diff line change
@@ -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<string> | 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")
}
}
21 changes: 6 additions & 15 deletions test/src/winPackagerTest.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,28 @@
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"
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"

//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,
}
))

Expand All @@ -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: {
Expand All @@ -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", {
Expand Down
4 changes: 3 additions & 1 deletion tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
"check-operator",
"check-separator",
"check-type"
]
],
"no-bitwise": false,
"jsdoc-format": false
}
}
Loading

0 comments on commit a6a0cd6

Please sign in to comment.