diff --git a/.travis.yml b/.travis.yml index ab2e0abc4e5..9631f3815b5 100755 --- a/.travis.yml +++ b/.travis.yml @@ -36,7 +36,7 @@ install: - if [[ "$TRAVIS_BRANCH" == "master" && "$TRAVIS_PULL_REQUEST" == "false" && "$AUTO_PUBLISH" != "false" && "$TRAVIS_TAG" == "" && "$PUBLISH_TO_NPM" == "true" ]]; then yarn add @develar/semantic-release@next --dev ; fi script: -- npm run test +- yarn run test after_success: - if [[ "$TRAVIS_BRANCH" == "master" && "$TRAVIS_PULL_REQUEST" == "false" && "$AUTO_PUBLISH" != "false" && "$TRAVIS_TAG" == "" && "$PUBLISH_TO_NPM" == "true" ]]; then npm run semantic-release ; fi diff --git a/package.json b/package.json index e88499d82b8..195e8bbbeaa 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "archiver": "^1.2.0", "archiver-utils": "^1.3.0", "asar-electron-builder": "^0.13.5", - "bluebird-lst-c": "^1.0.3", + "bluebird-lst-c": "^1.0.4", "chalk": "^1.1.3", "chromium-pickle-js": "^0.2.0", "cli-cursor": "^1.0.2", @@ -70,7 +70,7 @@ "debug": "^2.2.0", "electron-download": "2.1.2", "electron-osx-sign-tf": "~1.1.0", - "fs-extra-p": "^2.0.5", + "fs-extra-p": "^2.0.6", "hosted-git-info": "^2.1.5", "ini": "^1.3.4", "isbinaryfile": "^3.0.1", diff --git a/src/codeSign.ts b/src/codeSign.ts index 803467d4bb6..08c01ceda62 100644 --- a/src/codeSign.ts +++ b/src/codeSign.ts @@ -186,7 +186,7 @@ async function _findIdentity(namePrefix: CertType, qualifier?: string | null, ke export async function findIdentity(certType: CertType, qualifier?: string | null, keychain?: string | null): Promise { let identity = process.env.CSC_NAME || qualifier if (isEmptyOrSpaces(identity)) { - if (keychain == null && !isCi() && process.env.CSC_IDENTITY_AUTO_DISCOVERY === "false") { + if (keychain == null && !isCi() && (process.env.CSC_IDENTITY_AUTO_DISCOVERY === "false")) { return null } else { diff --git a/src/install-app-deps.ts b/src/install-app-deps.ts index 5c0b74f55a1..78926dc0e01 100644 --- a/src/install-app-deps.ts +++ b/src/install-app-deps.ts @@ -1,12 +1,13 @@ #! /usr/bin/env node -import { computeDefaultAppDirectory, installDependencies, getElectronVersion, use } from "./util/util" +import { computeDefaultAppDirectory, getElectronVersion, use } from "./util/util" import { printErrorAndExit } from "./util/promise" import * as path from "path" import BluebirdPromise from "bluebird-lst-c" import { DevMetadata } from "./metadata" import yargs from "yargs" import { readPackageJson } from "./util/readPackageJson" +import { installDependencies } from "./yarn" const args: any = yargs .option("arch", { diff --git a/src/node-gyp-rebuild.ts b/src/node-gyp-rebuild.ts index 6a91dedb7be..623fb1b990b 100644 --- a/src/node-gyp-rebuild.ts +++ b/src/node-gyp-rebuild.ts @@ -1,11 +1,12 @@ #! /usr/bin/env node -import { getElectronVersion, exec, getGypEnv } from "./util/util" +import { getElectronVersion, exec } from "./util/util" import { printErrorAndExit } from "./util/promise" import * as path from "path" import yargs from "yargs" import { readPackageJson } from "./util/readPackageJson" import { log } from "./util/log" +import { getGypEnv } from "./yarn" const args: any = yargs .option("arch", { diff --git a/src/packager.ts b/src/packager.ts index 105304b6c54..b80a2d2c47a 100644 --- a/src/packager.ts +++ b/src/packager.ts @@ -1,7 +1,7 @@ import * as path from "path" import { - computeDefaultAppDirectory, installDependencies, getElectronVersion, use, - exec, isEmptyOrSpaces, statOrNull, getGypEnv + computeDefaultAppDirectory, getElectronVersion, use, exec, isEmptyOrSpaces, exists, + asArray } from "./util/util" import { all, executeFinally } from "./util/promise" import { EventEmitter } from "events" @@ -20,6 +20,7 @@ import { createTargets } from "./targets/targetFactory" import { readPackageJson } from "./util/readPackageJson" import { TmpDir } from "./util/tmp" import { BuildOptions } from "./builder" +import { getGypEnv, installDependencies, rebuild } from "./yarn" function addHandler(emitter: EventEmitter, event: string, handler: Function) { emitter.on(event, handler) @@ -229,7 +230,7 @@ export class Packager implements BuildInfo { private async installAppDependencies(platform: Platform, arch: Arch): Promise { if (this.devMetadata.build.nodeGypRebuild === true) { - log(`Execute node-gyp rebuild for arch ${Arch[arch]}`) + log(`Executing node-gyp rebuild for arch ${Arch[arch]}`) await exec(process.platform === "win32" ? "node-gyp.cmd" : "node-gyp", ["rebuild"], { env: getGypEnv(this.electronVersion, Arch[arch]), }) @@ -241,11 +242,20 @@ export class Packager implements BuildInfo { } else { const forceBuildFromSource = this.devMetadata.build.npmSkipBuildFromSource !== true - if (platform.nodeName !== process.platform && forceBuildFromSource) { + if (forceBuildFromSource && platform.nodeName !== process.platform) { log("Skip app dependencies rebuild because platform is different") } else { - await installDependencies(this.appDir, this.electronVersion, Arch[arch], forceBuildFromSource, (await statOrNull(path.join(this.appDir, "node_modules"))) == null ? "install" : "rebuild", this.devMetadata.build.npmArgs) + if (await exists(path.join(this.appDir, "node_modules"))) { + const args = asArray(this.devMetadata.build.npmArgs) + if (forceBuildFromSource) { + args.push("--build-from-source") + } + await rebuild(this.appDir, this.electronVersion, Arch[arch], args) + } + else { + await installDependencies(this.appDir, this.electronVersion, Arch[arch], forceBuildFromSource, this.devMetadata.build.npmArgs) + } } } } diff --git a/src/platformPackager.ts b/src/platformPackager.ts index d3a5386e462..fbd96cdad8f 100644 --- a/src/platformPackager.ts +++ b/src/platformPackager.ts @@ -3,7 +3,7 @@ import EventEmitter = NodeJS.EventEmitter import BluebirdPromise from "bluebird-lst-c" import * as path from "path" import { readdir, remove } from "fs-extra-p" -import { statOrNull, use, unlinkIfExists, isEmptyOrSpaces, asArray, dependencies, debug } from "./util/util" +import { statOrNull, use, unlinkIfExists, isEmptyOrSpaces, asArray, debug } from "./util/util" import { Packager } from "./packager" import { AsarOptions } from "asar-electron-builder" import { archiveApp } from "./targets/archive" @@ -18,6 +18,7 @@ import { FileMatchOptions, FileMatcher, FilePattern, deprecatedUserIgnoreFilter import { BuildOptions } from "./builder" import { PublishConfiguration, GithubOptions, BintrayOptions, GenericServerOptions } from "./options/publishOptions" import { getRepositoryInfo } from "./repositoryInfo" +import { dependencies } from "./yarn" export interface PackagerOptions { targets?: Map> diff --git a/src/util/util.ts b/src/util/util.ts index 293148a899d..72e3728b6e8 100644 --- a/src/util/util.ts +++ b/src/util/util.ts @@ -2,10 +2,10 @@ import { execFile, spawn as _spawn, ChildProcess, SpawnOptions } from "child_pro import BluebirdPromise from "bluebird-lst-c" import { homedir } from "os" import * as path from "path" -import { readJson, stat, Stats, unlink } from "fs-extra-p" +import { readJson, stat, Stats, unlink, access } from "fs-extra-p" import { yellow, red } from "chalk" import _debug from "debug" -import { warn, task, log } from "./log" +import { warn, log } from "./log" import { createHash } from "crypto" import Debugger = debug.Debugger @@ -16,59 +16,6 @@ export const debug7z: Debugger = _debug("electron-builder:7z") const DEFAULT_APP_DIR_NAMES = ["app", "www"] -export function installDependencies(appDir: string, electronVersion: string, arch: string = process.arch, forceBuildFromSource: boolean, command: string = "install", additionalArgs?: any): Promise { - return task(`${(command === "install" ? "Installing" : "Rebuilding")} app dependencies for arch ${arch} to ${appDir}`, spawnNpmProduction(command, appDir, forceBuildFromSource, getGypEnv(electronVersion, arch), additionalArgs)) -} - -export function getGypEnv(electronVersion: string, arch: string): any { - const gypHome = path.join(homedir(), ".electron-gyp") - return Object.assign({}, process.env, { - npm_config_disturl: "https://atom.io/download/electron", - npm_config_target: electronVersion, - npm_config_runtime: "electron", - npm_config_arch: arch, - HOME: gypHome, - USERPROFILE: gypHome, - }) -} - -export function spawnNpmProduction(command: string, appDir: string, forceBuildFromSource: boolean, env?: any, additionalArgs?: any): Promise { - let npmExecPath = process.env.npm_execpath || process.env.NPM_CLI_JS - const npmExecArgs = [command, "--production"] - - if (npmExecPath == null || !npmExecPath.includes("yarn")) { - if (process.env.NPM_NO_BIN_LINKS === "true") { - npmExecArgs.push("--no-bin-links") - } - npmExecArgs.push("--cache-min", "999999999") - } - - if (npmExecPath == null) { - npmExecPath = process.platform === "win32" ? "npm.cmd" : "npm" - } - else { - npmExecArgs.unshift(npmExecPath) - npmExecPath = process.env.npm_node_execpath || process.env.NODE_EXE || "node" - } - if (forceBuildFromSource) { - npmExecArgs.push("--build-from-source") - } - - if (additionalArgs) { - if (Array.isArray(additionalArgs)) { - npmExecArgs.push(...additionalArgs) - } - else { - npmExecArgs.push(additionalArgs) - } - } - - return spawn(npmExecPath, npmExecArgs, { - cwd: appDir, - env: env || process.env - }) -} - export interface BaseExecOptions { cwd?: string env?: any @@ -238,6 +185,16 @@ export async function statOrNull(file: string): Promise { } } +export async function exists(file: string): Promise { + try { + await access(file) + return true + } + catch (e) { + return false + } +} + export async function computeDefaultAppDirectory(projectDir: string, userAppDir: string | null | undefined): Promise { if (userAppDir != null) { const absolutePath = path.resolve(projectDir, userAppDir) @@ -324,35 +281,4 @@ export function getCacheDirectory(): string { else { return path.join(homedir(), ".cache", "electron-builder") } -} - -let readInstalled: any = null -export function dependencies(dir: string, extraneousOnly: boolean, result: Set): Promise> { - if (readInstalled == null) { - readInstalled = BluebirdPromise.promisify(require("read-installed")) - } - return readInstalled(dir) - .then((it: any) => flatDependencies(it, result, new Set(), extraneousOnly)) -} - -function flatDependencies(data: any, result: Set, seen: Set, extraneousOnly: boolean): void { - const deps = data.dependencies - if (deps == null) { - return - } - - for (let d of Object.keys(deps)) { - const dep = deps[d] - if (typeof dep !== "object" || (!extraneousOnly && dep.extraneous) || seen.has(dep)) { - continue - } - - if (extraneousOnly === dep.extraneous) { - seen.add(dep) - result.add(dep.path) - } - else { - flatDependencies(dep, result, seen, extraneousOnly) - } - } } \ No newline at end of file diff --git a/src/yarn.ts b/src/yarn.ts new file mode 100644 index 00000000000..2f4918e085d --- /dev/null +++ b/src/yarn.ts @@ -0,0 +1,137 @@ +import BluebirdPromise from "bluebird-lst-c" +import * as path from "path" +import { task, log } from "./util/log" +import { homedir } from "os" +import { spawn, exists } from "./util/util" + +export function installDependencies(appDir: string, electronVersion: string, arch: string = process.arch, forceBuildFromSource: boolean, additionalArgs?: any): Promise { + return task(`Installing app dependencies for arch ${arch} to ${appDir}`, spawnNpmProduction(appDir, forceBuildFromSource, getGypEnv(electronVersion, arch), additionalArgs)) +} + +export function getGypEnv(electronVersion: string, arch: string): any { + const gypHome = path.join(homedir(), ".electron-gyp") + return Object.assign({}, process.env, { + npm_config_disturl: "https://atom.io/download/electron", + npm_config_target: electronVersion, + npm_config_runtime: "electron", + npm_config_arch: arch, + HOME: gypHome, + USERPROFILE: gypHome, + }) +} + +function spawnNpmProduction(appDir: string, forceBuildFromSource: boolean, env?: any, additionalArgs?: any): Promise { + let npmExecPath = process.env.npm_execpath || process.env.NPM_CLI_JS + const npmExecArgs = ["install", "--production"] + + const isNotYarn = npmExecPath == null || !npmExecPath.includes("yarn") + if (isNotYarn) { + if (process.env.NPM_NO_BIN_LINKS === "true") { + npmExecArgs.push("--no-bin-links") + } + npmExecArgs.push("--cache-min", "999999999") + } + + if (npmExecPath == null) { + npmExecPath = process.platform === "win32" ? "npm.cmd" : "npm" + } + else { + npmExecArgs.unshift(npmExecPath) + npmExecPath = process.env.npm_node_execpath || process.env.NODE_EXE || "node" + } + if (isNotYarn && forceBuildFromSource) { + npmExecArgs.push("--build-from-source") + } + + if (additionalArgs) { + if (Array.isArray(additionalArgs)) { + npmExecArgs.push(...additionalArgs) + } + else { + npmExecArgs.push(additionalArgs) + } + } + + return spawn(npmExecPath, npmExecArgs, { + cwd: appDir, + env: env || process.env + }) +} + +let readInstalled: any = null +export function dependencies(dir: string, extraneousOnly: boolean, result: Set): Promise> { + if (readInstalled == null) { + readInstalled = BluebirdPromise.promisify(require("read-installed")) + } + return readInstalled(dir) + .then((it: any) => flatDependencies(it, result, new Set(), extraneousOnly)) +} + +function flatDependencies(data: any, result: Set, seen: Set, extraneousOnly: boolean): void { + const deps = data.dependencies + if (deps == null) { + return + } + + for (let d of Object.keys(deps)) { + const dep = deps[d] + if (typeof dep !== "object" || (!extraneousOnly && dep.extraneous) || seen.has(dep)) { + continue + } + + if (extraneousOnly === dep.extraneous) { + seen.add(dep) + result.add(dep.path) + } + else { + flatDependencies(dep, result, seen, extraneousOnly) + } + } +} + +export async function rebuild(appDir: string, electronVersion: string, arch: string = process.arch, additionalArgs: Array) { + const deps = new Set() + await dependencies(appDir, false, deps) + const nativeDeps = await BluebirdPromise.filter(deps, it => exists(path.join(it, "binding.gyp")), {concurrency: 8}) + + if (nativeDeps.length === 0) { + return + } + + log(`Rebuilding native app dependencies for arch ${arch} to ${appDir}`) + + let execPath = process.env.npm_execpath || process.env.NPM_CLI_JS + const execArgs = ["run", "install", "--"] + + if (execPath == null) { + if (process.env.FORCE_YARN === "true") { + execPath = process.platform === "win32" ? "yarn.cmd" : "yarn" + } + else { + execPath = process.platform === "win32" ? "npm.cmd" : "npm" + } + } + else { + execArgs.unshift(execPath) + execPath = process.env.npm_node_execpath || process.env.NODE_EXE || "node" + } + + const gypHome = path.join(homedir(), ".electron-gyp") + const env = Object.assign({}, process.env, { + HOME: gypHome, + USERPROFILE: gypHome, + }) + + execArgs.push("--disturl=https://atom.io/download/electron") + execArgs.push(`--target=${electronVersion}`) + execArgs.push("--runtime=electron") + execArgs.push(`--arch=${arch}`) + execArgs.push(...additionalArgs) + + for (let dir of nativeDeps) { + await spawn(execPath, execArgs, { + cwd: dir, + env: env + }) + } +} \ No newline at end of file diff --git a/test/src/helpers/packTester.ts b/test/src/helpers/packTester.ts index 35c1b186141..dfefe822bd0 100755 --- a/test/src/helpers/packTester.ts +++ b/test/src/helpers/packTester.ts @@ -12,11 +12,11 @@ import { getArchSuffix, Target } from "out/platformPackager" import pathSorter from "path-sort" import DecompressZip from "decompress-zip" import { convertVersion } from "out/targets/squirrelPack" -import { spawnNpmProduction } from "out/util/util" import { TEST_DIR } from "./config" import { deepAssign } from "out/util/deepAssign" import { AssertContext } from "ava-tf" import { SquirrelWindowsOptions } from "out/options/winOptions" +import { spawn } from "out/util/util" if (process.env.TRAVIS !== "true") { process.env.CIRCLE_BUILD_NUM = 42 @@ -92,7 +92,9 @@ export async function assertPack(fixtureName: string, packagerOptions: PackagerO if (projectDirCreated != null) { await projectDirCreated(projectDir) if (checkOptions.npmInstallBefore) { - await spawnNpmProduction("install", projectDir, false) + await spawn(process.platform === "win32" ? "yarn.cmd" : "yarn", ["install", "--production"], { + cwd: projectDir + }) } } diff --git a/yarn.lock b/yarn.lock index 6efea4e1024..fc3a460a0ba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1,5 +1,7 @@ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. # yarn lockfile v1 + + "@develar/semantic-release@^6.3.21": version "6.3.23" resolved "https://registry.yarnpkg.com/@develar/semantic-release/-/semantic-release-6.3.23.tgz#80c8ecd7369cf4a5c442d59d24bd614c051dd004" @@ -1013,9 +1015,9 @@ block-stream@*: dependencies: inherits "~2.0.0" -bluebird-lst-c@^1.0.2, bluebird-lst-c@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/bluebird-lst-c/-/bluebird-lst-c-1.0.3.tgz#5bd879f3de7288201b08f31e2fe5d5c11e2b61de" +bluebird-lst-c@^1.0.2, bluebird-lst-c@^1.0.3, bluebird-lst-c@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/bluebird-lst-c/-/bluebird-lst-c-1.0.4.tgz#1278210da5b0750959c3d15971154ff08e07ad93" dependencies: bluebird "^3.4.6" @@ -1533,7 +1535,13 @@ dateformat@^1.0.11, dateformat@^1.0.12: get-stdin "^4.0.1" meow "^3.3.0" -debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@~2.2.0, debug@2: +debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@2: + version "2.3.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.3.0.tgz#3912dc55d7167fc3af17d2b85c13f93deaedaa43" + dependencies: + ms "0.7.2" + +debug@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" dependencies: @@ -1859,8 +1867,8 @@ forever-agent@~0.6.1: resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" form-data@~2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.1.tgz#4adf0342e1a79afa1e84c8c320a9ffc82392a1f3" + version "2.1.2" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.2.tgz#89c3534008b97eada4cbb157d58f6f5df025eae4" dependencies: asynckit "^0.4.0" combined-stream "^1.0.5" @@ -1870,9 +1878,9 @@ from@~0: version "0.1.3" resolved "https://registry.yarnpkg.com/from/-/from-0.1.3.tgz#ef63ac2062ac32acf7862e0d40b44b896f22f3bc" -fs-extra-p@^2.0.3, fs-extra-p@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/fs-extra-p/-/fs-extra-p-2.0.5.tgz#a9f9bee515880198da4528d81a85f43e0cf9837b" +fs-extra-p@^2.0.3, fs-extra-p@^2.0.5, fs-extra-p@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fs-extra-p/-/fs-extra-p-2.0.6.tgz#d6d83d9e517fc1d8b7557df78593a6258e1f381f" dependencies: bluebird-lst-c "^1.0.3" fs-extra-tf "^0.30.4" @@ -2813,11 +2821,7 @@ minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" -minimist@~0.0.1: - version "0.0.10" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" - -minimist@0.0.8: +minimist@~0.0.1, minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" @@ -2835,7 +2839,7 @@ modify-values@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.0.tgz#e2b6cdeb9ce19f99317a53722f3dbf5df5eaaab2" -ms@^0.7.1: +ms@^0.7.1, ms@0.7.2: version "0.7.2" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" @@ -3979,8 +3983,8 @@ typedarray@~0.0.5: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" typescript@^2.1.0-dev.20161101: - version "2.1.0-dev.20161107" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.1.0-dev.20161107.tgz#420e8ea43693dde3ab62853fd1edbafce53087a3" + version "2.1.1" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.1.1.tgz#41c2b64472f529331b2055c0424862b44ce58d42" uc.micro@^1.0.1, uc.micro@^1.0.3: version "1.0.3" @@ -4280,4 +4284,3 @@ zip-stream@^1.1.0: compress-commons "^1.1.0" lodash "^4.8.0" readable-stream "^2.0.0" -