From 239d16d5884d519ac2468ec108ba58f0b30a032e Mon Sep 17 00:00:00 2001
From: develar <develar@gmail.com>
Date: Sun, 18 Jun 2017 23:03:48 +0200
Subject: [PATCH] fix(mac): MacOS Sierra Command failed: codesign; The
 specified item could not be found in the keychain

Close #1457
---
 .travis.yml                                   |  4 +-
 package.json                                  |  4 +-
 packages/electron-builder-util/src/util.ts    |  2 +
 packages/electron-builder/certs/create.sh     | 11 +--
 .../certs/root_certs.keychain                 |  4 +-
 .../src/cli/create-self-signed-cert.ts        |  3 +-
 packages/electron-builder/src/codeSign.ts     | 70 +++++++++-------
 packages/electron-builder/src/packager.ts     |  3 +-
 packages/electron-builder/src/packagerApi.ts  |  2 +-
 packages/electron-builder/src/targets/fpm.ts  |  3 +-
 packages/lint.js                              |  2 +-
 test/src/ArtifactPublisherTest.ts             |  2 +-
 test/src/filesTest.ts                         |  2 +-
 test/src/helpers/checkDeps.ts                 |  2 +-
 test/src/mac/CodeSignTest.ts                  |  3 +-
 test/src/nsisUpdaterTest.ts                   |  2 +-
 test/src/windows/installerTest.ts             |  2 +-
 test/src/windows/winPackagerTest.ts           |  2 +-
 yarn.lock                                     | 81 ++++++++++++++++---
 19 files changed, 133 insertions(+), 71 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 3fbfdb4d344..98d2bc80ad4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -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
diff --git a/package.json b/package.json
index 744b2c02454..e19c8a52761 100644
--- a/package.json
+++ b/package.json
@@ -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",
@@ -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",
@@ -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",
diff --git a/packages/electron-builder-util/src/util.ts b/packages/electron-builder-util/src/util.ts
index ba0a90941c4..608fcfcb896 100644
--- a/packages/electron-builder-util/src/util.ts
+++ b/packages/electron-builder-util/src/util.ts
@@ -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")
 
diff --git a/packages/electron-builder/certs/create.sh b/packages/electron-builder/certs/create.sh
index 4067a89b2e7..242ef964c38 100755
--- a/packages/electron-builder/certs/create.sh
+++ b/packages/electron-builder/certs/create.sh
@@ -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
\ No newline at end of file
diff --git a/packages/electron-builder/certs/root_certs.keychain b/packages/electron-builder/certs/root_certs.keychain
index aec2067511e..bff875c9a20 100644
--- a/packages/electron-builder/certs/root_certs.keychain
+++ b/packages/electron-builder/certs/root_certs.keychain
@@ -1,3 +1,3 @@
 version https://git-lfs.github.com/spec/v1
-oid sha256:d63fbb2e912ca7b375a6edefcdf1bff92b25f311fdb67f226c03bd91e744ab54
-size 31944
+oid sha256:4dacee80dd413ba54ba834a677028866a21c07d3b18486e908c89f0f3bcb86db
+size 24440
diff --git a/packages/electron-builder/src/cli/create-self-signed-cert.ts b/packages/electron-builder/src/cli/create-self-signed-cert.ts
index 659e8f6ac6e..fea01767d0d 100644
--- a/packages/electron-builder/src/cli/create-self-signed-cert.ts
+++ b/packages/electron-builder/src/cli/create-self-signed-cert.ts
@@ -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"
diff --git a/packages/electron-builder/src/codeSign.ts b/packages/electron-builder/src/codeSign.ts
index dce575afd3e..c69ed100360 100644
--- a/packages/electron-builder/src/codeSign.ts
+++ b/packages/electron-builder/src/codeSign.ts
@@ -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"
@@ -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
@@ -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
@@ -99,14 +101,13 @@ 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) {
@@ -114,17 +115,26 @@ export async function createKeychain({tmpDir, cscLink, cscKeyPassword, 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> {
diff --git a/packages/electron-builder/src/packager.ts b/packages/electron-builder/src/packager.ts
index e4fb9ae8ff8..fdd01bc3699 100644
--- a/packages/electron-builder/src/packager.ts
+++ b/packages/electron-builder/src/packager.ts
@@ -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"
diff --git a/packages/electron-builder/src/packagerApi.ts b/packages/electron-builder/src/packagerApi.ts
index a514ed303e0..edf5fd9257f 100644
--- a/packages/electron-builder/src/packagerApi.ts
+++ b/packages/electron-builder/src/packagerApi.ts
@@ -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"
diff --git a/packages/electron-builder/src/targets/fpm.ts b/packages/electron-builder/src/targets/fpm.ts
index 8eec14592fa..ff89b5e1955 100644
--- a/packages/electron-builder/src/targets/fpm.ts
+++ b/packages/electron-builder/src/targets/fpm.ts
@@ -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"
diff --git a/packages/lint.js b/packages/lint.js
index 9fe5ccd4fc2..b49ca59d81d 100644
--- a/packages/lint.js
+++ b/packages/lint.js
@@ -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()
diff --git a/test/src/ArtifactPublisherTest.ts b/test/src/ArtifactPublisherTest.ts
index 02fc6be3643..57c9f4e0ecd 100644
--- a/test/src/ArtifactPublisherTest.ts
+++ b/test/src/ArtifactPublisherTest.ts
@@ -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"
diff --git a/test/src/filesTest.ts b/test/src/filesTest.ts
index f4ae0594099..838326d04b1 100644
--- a/test/src/filesTest.ts
+++ b/test/src/filesTest.ts
@@ -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"
diff --git a/test/src/helpers/checkDeps.ts b/test/src/helpers/checkDeps.ts
index 75526b9f0f0..d5bed8bbf28 100644
--- a/test/src/helpers/checkDeps.ts
+++ b/test/src/helpers/checkDeps.ts
@@ -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, {
diff --git a/test/src/mac/CodeSignTest.ts b/test/src/mac/CodeSignTest.ts
index f25541ae5ed..b53d8701f62 100644
--- a/test/src/mac/CodeSignTest.ts
+++ b/test/src/mac/CodeSignTest.ts
@@ -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"
 
diff --git a/test/src/nsisUpdaterTest.ts b/test/src/nsisUpdaterTest.ts
index 52d19e8d7be..d79e440432f 100644
--- a/test/src/nsisUpdaterTest.ts
+++ b/test/src/nsisUpdaterTest.ts
@@ -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"
diff --git a/test/src/windows/installerTest.ts b/test/src/windows/installerTest.ts
index 41b1707109d..e79e3b3e5f5 100644
--- a/test/src/windows/installerTest.ts
+++ b/test/src/windows/installerTest.ts
@@ -88,7 +88,7 @@ test.ifNotCiMac("boring, only perMachine", app({
   }
 }))
 
-test.ifAll("allowToChangeInstallationDirectory", app({
+test.ifAll.ifNotCiMac("allowToChangeInstallationDirectory", app({
   targets: nsisTarget,
   config: {
     extraMetadata: {
diff --git a/test/src/windows/winPackagerTest.ts b/test/src/windows/winPackagerTest.ts
index 7663cf7649c..420934e8ed3 100644
--- a/test/src/windows/winPackagerTest.ts
+++ b/test/src/windows/winPackagerTest.ts
@@ -69,7 +69,7 @@ describe.ifAll("sign", () => {
     }
   }))
 
-  test("forceCodeSigning", appThrows({
+  test.ifNotCiMac("forceCodeSigning", appThrows({
     targets: windowsDirTarget,
     config: {
       forceCodeSigning: true,
diff --git a/yarn.lock b/yarn.lock
index 5a3c6bf469d..4fbab384c38 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -128,10 +128,18 @@ ansi-escapes@^1.4.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e"
 
+ansi-regex@^0.2.0, ansi-regex@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-0.2.1.tgz#0d8e946967a3d8143f93e24e298525fc1b2235f9"
+
 ansi-regex@^2.0.0, ansi-regex@^2.1.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
 
+ansi-styles@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.1.0.tgz#eaecbf66cd706882760b2f4691582b8f55d7a7de"
+
 ansi-styles@^2.2.1:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
@@ -523,8 +531,8 @@ babel-types@^6.18.0, babel-types@^6.24.1, babel-types@^6.25.0:
     to-fast-properties "^1.0.1"
 
 babylon@^6.1.21, babylon@^6.13.0, babylon@^6.17.2:
-  version "6.17.3"
-  resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.3.tgz#1327d709950b558f204e5352587fd0290f8d8e48"
+  version "6.17.4"
+  resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.4.tgz#3e8b7402b88d22c3423e137a1577883b15ff869a"
 
 balanced-match@^1.0.0:
   version "1.0.0"
@@ -699,6 +707,16 @@ chainsaw@~0.1.0:
   dependencies:
     traverse ">=0.3.0 <0.4"
 
+chalk@0.5.1:
+  version "0.5.1"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.5.1.tgz#663b3a648b68b55d04690d49167aa837858f2174"
+  dependencies:
+    ansi-styles "^1.1.0"
+    escape-string-regexp "^1.0.0"
+    has-ansi "^0.1.0"
+    strip-ansi "^0.3.0"
+    supports-color "^0.2.0"
+
 chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
@@ -803,6 +821,10 @@ command-line-usage@^4.0.0:
     table-layout "^0.4.0"
     typical "^2.6.0"
 
+commander@2.6.0:
+  version "2.6.0"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-2.6.0.tgz#9df7e52fb2a0cb0fb89058ee80c3104225f37e1d"
+
 commander@^2.9.0:
   version "2.9.0"
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4"
@@ -830,6 +852,19 @@ concat-map@0.0.1:
   version "0.0.1"
   resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
 
+concurrently@^3.4.0:
+  version "3.4.0"
+  resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-3.4.0.tgz#60662b3defde07375bae19aac0ab780ec748ba79"
+  dependencies:
+    chalk "0.5.1"
+    commander "2.6.0"
+    date-fns "^1.23.0"
+    lodash "^4.5.1"
+    rx "2.3.24"
+    spawn-command "^0.0.2-1"
+    supports-color "^3.2.3"
+    tree-kill "^1.1.0"
+
 config-master@^3.0.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/config-master/-/config-master-3.1.0.tgz#667663590505a283bf26a484d68489d74c5485da"
@@ -934,6 +969,10 @@ dashdash@^1.12.0:
   dependencies:
     assert-plus "^1.0.0"
 
+date-fns@^1.23.0:
+  version "1.28.5"
+  resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.28.5.tgz#257cfc45d322df45ef5658665967ee841cd73faf"
+
 debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@^2.6.1, debug@^2.6.3, debug@^2.6.6, debug@^2.6.8:
   version "2.6.8"
   resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc"
@@ -1120,7 +1159,7 @@ error-ex@^1.2.0:
   dependencies:
     is-arrayish "^0.2.1"
 
-escape-string-regexp@^1.0.2, escape-string-regexp@~1.0.5:
+escape-string-regexp@^1.0.0, escape-string-regexp@^1.0.2, escape-string-regexp@~1.0.5:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
 
@@ -1457,6 +1496,12 @@ har-validator@~4.2.1:
     ajv "^4.9.1"
     har-schema "^1.0.5"
 
+has-ansi@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-0.1.0.tgz#84f265aae8c0e6a88a12d7022894b7568894c62e"
+  dependencies:
+    ansi-regex "^0.2.0"
+
 has-ansi@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
@@ -2929,6 +2974,10 @@ rimraf@^2.6.1:
   dependencies:
     glob "^7.0.5"
 
+rx@2.3.24:
+  version "2.3.24"
+  resolved "https://registry.yarnpkg.com/rx/-/rx-2.3.24.tgz#14f950a4217d7e35daa71bbcbe58eff68ea4b2b7"
+
 safe-buffer@^5.0.1:
   version "5.1.0"
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.0.tgz#fe4c8460397f9eaaaa58e73be46273408a45e223"
@@ -3037,6 +3086,10 @@ source-map@~0.2.0:
   dependencies:
     amdefine ">=0.0.4"
 
+spawn-command@^0.0.2-1:
+  version "0.0.2"
+  resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2.tgz#9544e1a43ca045f8531aac1a48cb29bdae62338e"
+
 spdx-correct@~1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40"
@@ -3132,6 +3185,12 @@ stringstream@~0.0.4:
   version "0.0.5"
   resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878"
 
+strip-ansi@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.3.0.tgz#25f48ea22ca79187f3174a4db8759347bb126220"
+  dependencies:
+    ansi-regex "^0.2.1"
+
 strip-ansi@^3.0.0, strip-ansi@^3.0.1:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
@@ -3168,11 +3227,15 @@ sumchecker@^2.0.2:
   dependencies:
     debug "^2.2.0"
 
+supports-color@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-0.2.0.tgz#d92de2694eb3f67323973d7ae3d8b55b4c22190a"
+
 supports-color@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
 
-supports-color@^3.1.2:
+supports-color@^3.1.2, supports-color@^3.2.3:
   version "3.2.3"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
   dependencies:
@@ -3304,6 +3367,10 @@ tr46@~0.0.3:
   version "0.3.9"
   resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9"
 
+tree-kill@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.1.0.tgz#c963dcf03722892ec59cba569e940b71954d1729"
+
 trim-newlines@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
@@ -3585,14 +3652,10 @@ window-size@0.1.0:
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"
 
-wordwrap@0.0.2:
+wordwrap@0.0.2, wordwrap@~0.0.2:
   version "0.0.2"
   resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"
 
-wordwrap@~0.0.2:
-  version "0.0.3"
-  resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
-
 wordwrap@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"