Skip to content

Commit

Permalink
fix(mac): MacOS Sierra Command failed: codesign; The specified item c…
Browse files Browse the repository at this point in the history
…ould not be found in the keychain

Close electron-userland#1457
  • Loading branch information
develar committed Jun 18, 2017
1 parent e5817bc commit 239d16d
Show file tree
Hide file tree
Showing 19 changed files with 133 additions and 71 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ osx_image: xcode8.3
matrix:
include:
- os: osx
env: TEST_FILES=BuildTest,extraMetadataTest,globTest,filesTest,ignoreTest,linux.* NODE_VERSION=6 PUBLISH_TO_NPM=true
env: TEST_FILES=BuildTest,extraMetadataTest,globTest,filesTest,ignoreTest,linux.*,windows.* NODE_VERSION=6 PUBLISH_TO_NPM=true

- os: osx
env: TEST_FILES=windows.*,mac.* NODE_VERSION=8
env: TEST_FILES=mac.* NODE_VERSION=8

- os: osx
env: TEST_FILES=mac.* NODE_VERSION=6
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"scripts": {
"compile": "ts-babel packages/asar-integrity packages/electron-builder-http packages/electron-builder-util packages/electron-publish packages/electron-builder packages/electron-builder-squirrel-windows packages/electron-updater packages/electron-publisher-s3 test && node ./test/vendor/yarn.js schema",
"lint": "node test/out/helpers/lint.js",
"pretest": "node ./test/vendor/yarn.js compile && node ./test/vendor/yarn.js lint && node ./test/vendor/yarn.js lint-deps",
"lint-deps": "node ./test/out/helpers/checkDeps.js",
"pretest": "node ./test/vendor/yarn.js compile && concurrently \"node test/out/helpers/lint.js\" \"node ./test/out/helpers/checkDeps.js\"",
"///": "Please see https://github.com/electron-userland/electron-builder/blob/master/CONTRIBUTING.md#run-test-using-cli how to run particular test instead full (and very slow) run",
"test": "node ./test/out/helpers/runTests.js skipArtifactPublisher ALL_TESTS=isCi",
"test-all": "node ./test/vendor/yarn.js pretest && node ./test/out/helpers/runTests.js",
Expand All @@ -14,7 +14,6 @@
"update-wiki": "(git branch -D wiki || true) && git subtree split -b wiki --prefix docs/ && git push -f wiki wiki:master",
"whitespace": "whitespace 'src/**/*.ts'",
"docker-images": "docker/build.sh",
"test-deps-mac": "brew install rpm dpkg mono lzip gnu-tar graphicsmagick xz && brew install wine --without-x11",
"update-deps": "node ./packages/update-deps.js",
"set-versions": "node test/out/helpers/setVersions.js",
"npm-publish": "yarn set-versions && yarn compile && ./packages/npm-publish.sh && conventional-changelog -p angular -i CHANGELOG.md -s",
Expand Down Expand Up @@ -78,6 +77,7 @@
"babel-plugin-transform-es2015-parameters": "^6.24.1",
"babel-plugin-transform-es2015-spread": "^6.22.0",
"babel-plugin-transform-inline-imports-commonjs": "^1.2.0",
"concurrently": "^3.4.0",
"convert-source-map": "^1.5.0",
"decompress-zip": "^0.3.0",
"depcheck": "^0.6.7",
Expand Down
2 changes: 2 additions & 0 deletions packages/electron-builder-util/src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import "source-map-support/register"
import { statOrNull } from "./fs"
import { log, warn } from "./log"

export { TmpDir } from "./tmp"

export const debug = _debug("electron-builder")
export const debug7z = _debug("electron-builder:7z")

Expand Down
11 changes: 1 addition & 10 deletions packages/electron-builder/certs/create.sh
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
#!/usr/bin/env bash

> /tmp/bundle.crt
curl https://www.startssl.com/certs/sca.code2.crt >> /tmp/bundle.crt
curl https://www.startssl.com/certs/sca.code3.crt >> /tmp/bundle.crt

curl https://repository.certum.pl/cscasha2.pem >> /tmp/bundle.crt


rm -rf $PWD/root_certs.keychain
security create-keychain -p pass $PWD/root_certs.keychain
security set-keychain-settings -t 86400 -u $PWD/root_certs.keychain

security import /tmp/bundle.crt -k $PWD/root_certs.keychain -T /usr/bin/codesign -T /usr/bin/productbuild
security set-keychain-settings $PWD/root_certs.keychain

curl https://developer.apple.com/certificationauthority/AppleWWDRCA.cer > /tmp/AppleWWDRCA.cer
security import /tmp/AppleWWDRCA.cer -k $PWD/root_certs.keychain -T /usr/bin/codesign -T /usr/bin/productbuild
4 changes: 2 additions & 2 deletions packages/electron-builder/certs/root_certs.keychain
Git LFS file not shown
3 changes: 1 addition & 2 deletions packages/electron-builder/src/cli/create-self-signed-cert.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { bold } from "chalk"
import { exec, spawn } from "electron-builder-util"
import { exec, spawn, TmpDir } from "electron-builder-util"
import { unlinkIfExists } from "electron-builder-util/out/fs"
import { log } from "electron-builder-util/out/log"
import { TmpDir } from "electron-builder-util/out/tmp"
import { ensureDir } from "fs-extra-p"
import * as path from "path"
import sanitizeFileName from "sanitize-filename"
Expand Down
70 changes: 40 additions & 30 deletions packages/electron-builder/src/codeSign.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import BluebirdPromise from "bluebird-lst"
import { randomBytes } from "crypto"
import { exec, getCacheDirectory, getTempName, isEmptyOrSpaces } from "electron-builder-util"
import { exec, getCacheDirectory, getTempName, isEmptyOrSpaces, Lazy, TmpDir } from "electron-builder-util"
import { copyFile, statOrNull } from "electron-builder-util/out/fs"
import { httpExecutor } from "electron-builder-util/out/nodeHttpExecutor"
import { all, executeFinally } from "electron-builder-util/out/promise"
import { TmpDir } from "electron-builder-util/out/tmp"
import { deleteFile, outputFile, rename } from "fs-extra-p"
import { outputFile, rename } from "fs-extra-p"
import isCi from "is-ci"
import { homedir } from "os"
import * as path from "path"
Expand Down Expand Up @@ -62,7 +60,7 @@ export async function downloadCertificate(urlOrBase64: string, tmpDir: TmpDir, c
}
}

let bundledCertKeychainAdded: Promise<any> | null = null
const bundledCertKeychainAdded = new Lazy<void>(createCustomCertKeychain)

// "Note that filename will not be searched to resolve the signing identity's certificate chain unless it is also on the user's keychain search list."
// but "security list-keychains" doesn't support add - we should 1) get current list 2) set new list - it is very bad http://stackoverflow.com/questions/10538942/add-a-keychain-to-search-list
Expand All @@ -72,24 +70,28 @@ async function createCustomCertKeychain() {
// copy to temp and then atomic rename to final path
const tmpKeychainPath = path.join(getCacheDirectory(), getTempName("electron-builder-root-certs"))
const keychainPath = path.join(getCacheDirectory(), "electron-builder-root-certs.keychain")
const results = await BluebirdPromise.all<string>([
exec("security", ["list-keychains"]),
const results = await BluebirdPromise.all<any>([
listUserKeychains(),
copyFile(path.join(__dirname, "..", "certs", "root_certs.keychain"), tmpKeychainPath)
.then(() => rename(tmpKeychainPath, keychainPath)),
])
const list = results[0]
.split("\n")
.map(it => {
const r = it.trim()
return r.substring(1, r.length - 1)
})
.filter(it => it.length > 0)

if (!list.includes(keychainPath)) {
await exec("security", ["list-keychains", "-d", "user", "-s", keychainPath].concat(list))
}
}

function listUserKeychains(): Promise<Array<string>> {
return exec("security", ["list-keychains", "-d", "user"])
.then(it => it
.split("\n")
.map(it => {
const r = it.trim()
return r.substring(1, r.length - 1)
})
.filter(it => it.length > 0))
}

export interface CreateKeychainOptions {
tmpDir: TmpDir
cscLink: string
Expand All @@ -99,32 +101,40 @@ export interface CreateKeychainOptions {
currentDir: string
}

/** @private */
export async function createKeychain({tmpDir, cscLink, cscKeyPassword, cscILink, cscIKeyPassword, currentDir}: CreateKeychainOptions): Promise<CodeSigningInfo> {
if (bundledCertKeychainAdded == null) {
bundledCertKeychainAdded = createCustomCertKeychain()
// travis has correct AppleWWDRCA cert
if (process.env.TRAVIS !== "true") {
await bundledCertKeychainAdded.value
}
await bundledCertKeychainAdded

const keychainName = await tmpDir.getTempFile(".keychain")
const keychainFile = await tmpDir.getTempFile(".keychain")

const certLinks = [cscLink]
if (cscILink != null) {
certLinks.push(cscILink)
}

const certPaths = new Array(certLinks.length)
const keychainPassword = randomBytes(8).toString("hex")
return await executeFinally(BluebirdPromise.all([
BluebirdPromise.map(certLinks, (link, i) => downloadCertificate(link, tmpDir, currentDir).then(it => certPaths[i] = it)),
BluebirdPromise.mapSeries([
["create-keychain", "-p", keychainPassword, keychainName],
["unlock-keychain", "-p", keychainPassword, keychainName],
["set-keychain-settings", "-t", "3600", "-u", keychainName]
], it => exec("security", it))
])
.then<CodeSigningInfo>(() => importCerts(keychainName, certPaths, <Array<string>>[cscKeyPassword, cscIKeyPassword].filter(it => it != null))),
() => all(certPaths.map((it, index) => certLinks[index].startsWith("https://") ? deleteFile(it, true) : BluebirdPromise.resolve())))
const keychainPassword = randomBytes(8).toString("base64")
await BluebirdPromise.all([
// we do not clear downloaded files - will be removed on tmpDir cleanup automatically. not a security issue since in any case data is available as env variables and protected by password.
BluebirdPromise.map(certLinks, (link, i) => downloadCertificate(link, tmpDir, currentDir).then(it => certPaths[i] = it)),
BluebirdPromise.mapSeries([
["create-keychain", "-p", keychainPassword, keychainFile],
["unlock-keychain", "-p", keychainPassword, keychainFile],
["set-keychain-settings", keychainFile]
], it => exec("security", it))
])

// https://stackoverflow.com/questions/42484678/codesign-keychain-gets-ignored
// https://github.com/electron-userland/electron-builder/issues/1457
if (isCi) {
const list = await listUserKeychains()
if (!list.includes(keychainFile)) {
await exec("security", ["list-keychains", "-d", "user", "-s", keychainFile].concat(list))
}
}
return await importCerts(keychainFile, certPaths, <Array<string>>[cscKeyPassword, cscIKeyPassword].filter(it => it != null))
}

async function importCerts(keychainName: string, paths: Array<string>, keyPasswords: Array<string>): Promise<CodeSigningInfo> {
Expand Down
3 changes: 1 addition & 2 deletions packages/electron-builder/src/packager.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import BluebirdPromise from "bluebird-lst"
import { CancellationToken } from "electron-builder-http"
import { computeDefaultAppDirectory, debug, exec, Lazy, safeStringifyJson, use } from "electron-builder-util"
import { computeDefaultAppDirectory, debug, exec, Lazy, safeStringifyJson, TmpDir, use } from "electron-builder-util"
import { deepAssign } from "electron-builder-util/out/deepAssign"
import { log } from "electron-builder-util/out/log"
import { all, executeFinally, orNullIfFileNotExist } from "electron-builder-util/out/promise"
import { TmpDir } from "electron-builder-util/out/tmp"
import { EventEmitter } from "events"
import { ensureDir } from "fs-extra-p"
import * as path from "path"
Expand Down
2 changes: 1 addition & 1 deletion packages/electron-builder/src/packagerApi.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { CancellationToken } from "electron-builder-http"
import { PublishConfiguration } from "electron-builder-http/out/publishOptions"
import { TmpDir } from "electron-builder-util/out/tmp"
import { TmpDir } from "electron-builder-util"
import { AppInfo } from "./appInfo"
import { Arch, Platform, SourceRepositoryInfo, Target } from "./core"
import { AfterPackContext, Config, Metadata } from "./metadata"
Expand Down
3 changes: 1 addition & 2 deletions packages/electron-builder/src/targets/fpm.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import BluebirdPromise from "bluebird-lst"
import { exec, smarten, use } from "electron-builder-util"
import { exec, smarten, TmpDir, use } from "electron-builder-util"
import { getBin } from "electron-builder-util/out/binDownload"
import { unlinkIfExists } from "electron-builder-util/out/fs"
import { log, warn } from "electron-builder-util/out/log"
import { TmpDir } from "electron-builder-util/out/tmp"
import { ensureDir, outputFile, readFile } from "fs-extra-p"
import * as path from "path"
import { Arch, Target, toLinuxArchString } from "../core"
Expand Down
2 changes: 1 addition & 1 deletion packages/lint.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ for (const projectDir of require("./process").getPackages()) {
continue
}

console.log(`Linting ${path.basename(projectDir)}`)
// console.log(`Linting ${path.basename(projectDir)}`)
const program = Linter.createProgram("tsconfig.json", projectDir)
for (const file of Linter.getFileNames(program)) {
const fileContents = program.getSourceFile(file).getFullText()
Expand Down
2 changes: 1 addition & 1 deletion test/src/ArtifactPublisherTest.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { CancellationToken, HttpError } from "electron-builder-http"
import { S3Options } from "electron-builder-http/out/publishOptions"
import { TmpDir } from "electron-builder-util"
import { copyFile } from "electron-builder-util/out/fs"
import { TmpDir } from "electron-builder-util/out/tmp"
import { createPublisher } from "electron-builder/out/publish/PublishManager"
import { PublishContext } from "electron-publish"
import { BintrayPublisher } from "electron-publish/out/BintrayPublisher"
Expand Down
2 changes: 1 addition & 1 deletion test/src/filesTest.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import BluebirdPromise from "bluebird-lst"
import { DIR_TARGET, Platform } from "electron-builder"
import { TmpDir } from "electron-builder-util"
import { copyDir } from "electron-builder-util/out/fs"
import { TmpDir } from "electron-builder-util/out/tmp"
import { outputFile, readFile, stat, symlink } from "fs-extra-p"
import * as path from "path"
import Mode, { Permissions } from "stat-mode"
Expand Down
2 changes: 1 addition & 1 deletion test/src/helpers/checkDeps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const packageDir = path.join(rootDir, "packages")

async function check(projectDir: string, devPackageData: any): Promise<boolean> {
const packageName = path.basename(projectDir)
console.log(`Checking ${packageName}`)
// console.log(`Checking ${packageName}`)

const result = await new BluebirdPromise<DepCheckResult>(function (resolve) {
depCheck(projectDir, {
Expand Down
3 changes: 1 addition & 2 deletions test/src/mac/CodeSignTest.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { removePassword } from "electron-builder-util"
import { TmpDir } from "electron-builder-util/out/tmp"
import { removePassword, TmpDir } from "electron-builder-util"
import { createKeychain } from "electron-builder/out/codeSign"
import { CSC_LINK } from "../helpers/codeSignData"

Expand Down
2 changes: 1 addition & 1 deletion test/src/nsisUpdaterTest.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import BluebirdPromise from "bluebird-lst"
import { BintrayOptions, GenericServerOptions, GithubOptions } from "electron-builder-http/out/publishOptions"
import { TmpDir } from "electron-builder-util"
import { httpExecutor } from "electron-builder-util/out/nodeHttpExecutor"
import { TmpDir } from "electron-builder-util/out/tmp"
import { NoOpLogger } from "electron-updater/out/AppUpdater"
import { NsisUpdater } from "electron-updater/out/NsisUpdater"
import { outputFile } from "fs-extra-p"
Expand Down
2 changes: 1 addition & 1 deletion test/src/windows/installerTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ test.ifNotCiMac("boring, only perMachine", app({
}
}))

test.ifAll("allowToChangeInstallationDirectory", app({
test.ifAll.ifNotCiMac("allowToChangeInstallationDirectory", app({
targets: nsisTarget,
config: {
extraMetadata: {
Expand Down
2 changes: 1 addition & 1 deletion test/src/windows/winPackagerTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ describe.ifAll("sign", () => {
}
}))

test("forceCodeSigning", appThrows({
test.ifNotCiMac("forceCodeSigning", appThrows({
targets: windowsDirTarget,
config: {
forceCodeSigning: true,
Expand Down
Loading

0 comments on commit 239d16d

Please sign in to comment.