diff --git a/docs/Options.md b/docs/Options.md index 245bfe8e4c5..93774232d3d 100644 --- a/docs/Options.md +++ b/docs/Options.md @@ -270,12 +270,12 @@ Windows specific build options. | Name | Description | --- | --- -| target | Target package type: list of `nsis`, `squirrel`, `7z`, `zip`, `tar.xz`, `tar.lz`, `tar.gz`, `tar.bz2`, `dir`. Defaults to `nsis`. -| signingHashAlgorithms | Array of signing algorithms used. Defaults to `['sha1', 'sha256']` +| target |

Target package type: list of nsis, appx, squirrel, 7z, zip, tar.xz, tar.lz, tar.gz, tar.bz2, dir. Defaults to nsis.

AppX package can be built only on Windows 10.

+| signingHashAlgorithms |

Array of signing algorithms used. Defaults to ['sha1', 'sha256']

Fo AppX sha256 is always used.

| icon | The path to application icon. Defaults to `build/icon.ico` (consider using this convention instead of complicating your configuration). | legalTrademarks | The trademarks and registered trademarks. -| certificateFile | The path to the *.pfx certificate you want to sign with. Required only if you build on macOS and need different certificate than the one set in `CSC_LINK` - see [Code Signing](https://github.com/electron-userland/electron-builder/wiki/Code-Signing). -| certificatePassword | The password to the certificate provided in `certificateFile`. Required only if you build on macOS and need to use a different password than the one set in `CSC_KEY_PASSWORD` - see [Code Signing](https://github.com/electron-userland/electron-builder/wiki/Code-Signing). +| certificateFile |

The path to the *.pfx certificate you want to sign with. Please use it only if you cannot use env variable CSC_LINK (WIN_CSC_LINK) for some reason. Please see [Code Signing](https://github.com/electron-userland/electron-builder/wiki/Code-Signing).

+| certificatePassword |

The password to the certificate provided in certificateFile. Please use it only if you cannot use env variable CSC_KEY_PASSWORD (WIN_CSC_KEY_PASSWORD) for some reason. Please see [Code Signing](https://github.com/electron-userland/electron-builder/wiki/Code-Signing).

| certificateSubjectName | The name of the subject of the signing certificate. Required only for EV Code Signing and works only on Windows. | rfc3161TimeStampServer | The URL of the RFC 3161 time stamp server. Defaults to `http://timestamp.comodoca.com/rfc3161`. @@ -306,9 +306,8 @@ Development dependencies are never copied in any case. You don't need to ignore [Multiple patterns](#multiple-glob-patterns) are supported. You can use `${os}` (expanded to mac, linux or win according to current platform) and `${arch}` in the pattern. If directory matched, all contents are copied. So, you can just specify `foo` to copy `foo` directory. -Remember that default pattern `**/*` is not added to your custom, so, you have to add it explicitly — e.g. `["**/*", "!ignoreMe${/*}"]`. - -`package.json` is added to your custom in any case. +Remember that default pattern `**/*` **is not added to your custom** if some of your patterns is not ignore (i.e. not starts with `!`). + `package.json` is added to your custom in any case. May be specified in the platform options (e.g. in the `build.mac`). diff --git a/package.json b/package.json index bee4294bdf0..94cfe1464b5 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "update-wiki": "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", "precommit": "validate-commit-msg" }, "repository": "electron-userland/electron-builder", diff --git a/src/codeSign.ts b/src/codeSign.ts index f9577d0601f..e787c02ffb0 100644 --- a/src/codeSign.ts +++ b/src/codeSign.ts @@ -207,7 +207,7 @@ async function _findIdentity(type: CertType, qualifier?: string | null, keychain 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/fileMatcher.ts b/src/fileMatcher.ts index 355f9c28a0d..7774f44fd51 100644 --- a/src/fileMatcher.ts +++ b/src/fileMatcher.ts @@ -34,6 +34,10 @@ export class FileMatcher { return this.patterns.length === 0 } + containsOnlyIgnore(): boolean { + return !this.isEmpty() && this.patterns.find(it => !it.startsWith("!")) == null + } + getParsedPatterns(fromDir?: string): Array { // https://github.com/electron-userland/electron-builder/issues/733 const minimatchOptions = {dot: true} @@ -41,9 +45,9 @@ export class FileMatcher { const parsedPatterns: Array = [] const pathDifference = fromDir ? path.relative(fromDir, this.from) : null - for (let i = 0; i < this.patterns.length; i++) { - let expandedPattern = this.expandPattern(this.patterns[i]) - if (pathDifference) { + for (const p of this.patterns) { + let expandedPattern = this.expandPattern(p) + if (pathDifference != null) { expandedPattern = path.join(pathDifference, expandedPattern) } @@ -72,10 +76,10 @@ export class FileMatcher { } } -export function deprecatedUserIgnoreFilter(ignore: any, appDir: string) { +export function deprecatedUserIgnoreFilter(ignore: Array | ((file: string) => boolean), appDir: string) { let ignoreFunc: any - if (typeof (ignore) === "function") { - ignoreFunc = function (file: string) { return !ignore(file) } + if (typeof ignore === "function") { + ignoreFunc = function (file: string) { return !(ignore)(file) } } else { if (!Array.isArray(ignore)) { @@ -83,8 +87,8 @@ export function deprecatedUserIgnoreFilter(ignore: any, appDir: string) { } ignoreFunc = function (file: string) { - for (let i = 0; i < ignore.length; i++) { - if (file.match(ignore[i])) { + for (const i of >ignore) { + if (file.match(i)) { return false } } diff --git a/src/platformPackager.ts b/src/platformPackager.ts index 360f63109a6..7254487d9aa 100644 --- a/src/platformPackager.ts +++ b/src/platformPackager.ts @@ -197,7 +197,7 @@ export abstract class PlatformPackager const patterns = this.getFileMatchers("files", appDir, path.join(resourcesPath, "app"), false, fileMatchOptions, platformSpecificBuildOptions) let defaultMatcher = patterns == null ? new FileMatcher(appDir, path.join(resourcesPath, "app"), fileMatchOptions) : patterns[0] - if (defaultMatcher.isEmpty()) { + if (defaultMatcher.isEmpty() || defaultMatcher.containsOnlyIgnore()) { defaultMatcher.addPattern("**/*") } else { @@ -252,7 +252,7 @@ export abstract class PlatformPackager //noinspection ES6MissingAwait const promises = [promise, unlinkIfExists(path.join(resourcesPath, "default_app.asar")), unlinkIfExists(path.join(appOutDir, "version")), this.postInitApp(appOutDir)] if (this.platform !== Platform.MAC) { - promises.push(rename(path.join(appOutDir, "LICENSE"), path.join(appOutDir, "LICENSE.electron.txt")) .catch(() => {/* ignore */})) + promises.push(rename(path.join(appOutDir, "LICENSE"), path.join(appOutDir, "LICENSE.electron.txt")).catch(() => {/* ignore */})) } if (this.info.electronVersion != null && this.info.electronVersion[0] === "0") { // electron release >= 0.37.4 - the default_app/ folder is a default_app.asar file diff --git a/src/targets/appx.ts b/src/targets/appx.ts index 1fa5b2d31a2..dc5f9c5027c 100644 --- a/src/targets/appx.ts +++ b/src/targets/appx.ts @@ -9,12 +9,18 @@ import BluebirdPromise from "bluebird-lst-c" import { Target } from "./targetFactory" import { getSignVendorPath } from "../windowsCodeSign" import sanitizeFileName from "sanitize-filename" +import { release } from "os" export default class AppXTarget extends Target { private readonly options: AppXOptions = Object.assign({}, this.packager.platformSpecificBuildOptions, this.packager.devMetadata.build.appx) constructor(private readonly packager: WinPackager, private readonly outDir: string) { super("appx") + + const osVersion = release() + if (process.platform !== "win32" || parseInt(osVersion.substring(0, osVersion.indexOf(".")), 10) < 10) { + throw new Error("AppX is supported only on Windows 10") + } } // no flatten - use asar or npm 3 or yarn diff --git a/src/util/filter.ts b/src/util/filter.ts index 19f65662ddd..07e97c66fcc 100644 --- a/src/util/filter.ts +++ b/src/util/filter.ts @@ -17,7 +17,7 @@ export function hasMagic(pattern: Minimatch) { return true } - for (let i of set[0]) { + for (const i of set[0]) { if (typeof i !== "string") { return true } @@ -44,7 +44,6 @@ export function createFilter(src: string, patterns: Array, ignoreFile } let relative = it.substring(src.length + 1) - if (path.sep === "\\") { relative = relative.replace(/\\/g, "/") } @@ -56,7 +55,7 @@ export function createFilter(src: string, patterns: Array, ignoreFile // https://github.com/joshwnj/minimatch-all/blob/master/index.js function minimatchAll(path: string, patterns: Array, stat: Stats): boolean { let match = false - for (let pattern of patterns) { + for (const pattern of patterns) { // If we've got a match, only re-test for exclusions. // if we don't have a match, only re-test for inclusions. if (match !== pattern.negate) { diff --git a/test/README.md b/test/README.md index 9d0f5caa2ff..58ac51ee519 100755 --- a/test/README.md +++ b/test/README.md @@ -1,5 +1,3 @@ -In addition to [required system packages](./multi-platform-build.md), on MacOS `dpkg` is required to run Linux tests: `brew install dpkg` - # Inspect output if test uses temporary directory Set environment variable `TEST_APP_TMP_DIR` (e.g. `/tmp/electron-builder-test`). Specified directory will be used instead of random temporary directory and *cleared* on each run. diff --git a/test/jestSetup.js b/test/jestSetup.js index 875417259ad..d62a3e52f89 100644 --- a/test/jestSetup.js +++ b/test/jestSetup.js @@ -33,4 +33,7 @@ test.ifNotCiMac = isCi && process.platform === "darwin" ? skip : test test.ifDevOrWinCi = !isCi || isWindows ? test : skip test.ifDevOrLinuxCi = !isCi || process.platform === "linux" ? test : skip -test.ifWinCi = isCi && isWindows ? test : skip \ No newline at end of file +test.ifWinCi = isCi && isWindows ? test : skip + +delete process.env.CSC_NAME +process.env.CSC_IDENTITY_AUTO_DISCOVERY = "false" \ No newline at end of file diff --git a/test/src/ArtifactPublisherTest.ts b/test/src/ArtifactPublisherTest.ts index 1cb4f1c5b0c..f5f10d31d5a 100644 --- a/test/src/ArtifactPublisherTest.ts +++ b/test/src/ArtifactPublisherTest.ts @@ -11,6 +11,12 @@ if (process.env.CI && process.platform === "win32") { }) } +if (process.env.ELECTRON_BUILDER_OFFLINE === "true") { + fit("Skip ArtifactPublisherTest suite — ELECTRON_BUILDER_OFFLINE is defined", () => { + console.warn("[SKIP] Skip ArtifactPublisherTest suite — ELECTRON_BUILDER_OFFLINE is defined") + }) +} + function getRandomInt(min: number, max: number) { return Math.floor(Math.random() * (max - min + 1)) + min } diff --git a/test/src/filesTest.ts b/test/src/filesTest.ts index 8260340b29b..8e469dfb36c 100644 --- a/test/src/filesTest.ts +++ b/test/src/filesTest.ts @@ -12,7 +12,7 @@ test.ifDevOrLinuxCi("files", app({ devMetadata: { build: { asar: false, - files: ["**/*", "!ignoreMe${/*}"] + files: ["!ignoreMe${/*}"] } } }, { @@ -25,7 +25,7 @@ test.ifDevOrLinuxCi("files", app({ })) test("extraResources", async () => { - for (let platform of getPossiblePlatforms().keys()) { + for (const platform of getPossiblePlatforms().keys()) { const osName = platform.buildConfigurationKey const winDirPrefix = "lib/net45/resources/" @@ -90,78 +90,79 @@ test("extraResources", async () => { } }) -test("extraResources - one-package", async () => { - for (let platform of [process.platform === "win32" ? Platform.WINDOWS : Platform.LINUX]) { - const osName = platform.buildConfigurationKey +test("extraResources - one-package", () => { + const platform = process.platform === "win32" ? Platform.WINDOWS : Platform.LINUX + const osName = platform.buildConfigurationKey - const winDirPrefix = "lib/net45/resources/" + const winDirPrefix = "lib/net45/resources/" - //noinspection SpellCheckingInspection - await assertPack("test-app-one", { - // to check NuGet package - targets: platform.createTarget(platform === Platform.WINDOWS ? "squirrel" : DIR_TARGET), - devMetadata: { - build: { - asar: true, - }, + //noinspection SpellCheckingInspection + return assertPack("test-app-one", { + // to check NuGet package + targets: platform.createTarget(platform === Platform.WINDOWS ? "squirrel" : DIR_TARGET), + devMetadata: { + build: { + asar: true, }, - }, { - projectDirCreated: projectDir => { - return BluebirdPromise.all([ - modifyPackageJson(projectDir, data => { - data.build.extraResources = [ - "foo", - "bar/hello.txt", - "bar/${arch}.txt", - "${os}/${arch}.txt", - ] + }, + }, { + projectDirCreated: projectDir => { + return BluebirdPromise.all([ + modifyPackageJson(projectDir, data => { + data.build.extraResources = [ + "foo", + "bar/hello.txt", + "bar/${arch}.txt", + "${os}/${arch}.txt", + ] - data.build[osName] = { - extraResources: [ - "platformSpecificR" - ], - extraFiles: [ - "platformSpecificF" - ], - } - }), - outputFile(path.join(projectDir, "foo/nameWithoutDot"), "nameWithoutDot"), - outputFile(path.join(projectDir, "bar/hello.txt"), "data"), - outputFile(path.join(projectDir, `bar/${process.arch}.txt`), "data"), - outputFile(path.join(projectDir, `${osName}/${process.arch}.txt`), "data"), - outputFile(path.join(projectDir, "platformSpecificR"), "platformSpecificR"), - outputFile(path.join(projectDir, "ignoreMe.txt"), "ignoreMe"), - ]) - }, - packed: async context => { - const base = path.join(context.outDir, platform.buildConfigurationKey + `${platform === Platform.MAC ? "" : "-unpacked"}`) - let resourcesDir = path.join(base, "resources") - if (platform === Platform.MAC) { - resourcesDir = path.join(base, "TestApp.app", "Contents", "Resources") - } - const appDir = path.join(resourcesDir, "app") + data.build[osName] = { + extraResources: [ + "platformSpecificR" + ], + extraFiles: [ + "platformSpecificF" + ], + } + }), + outputFile(path.join(projectDir, "foo/nameWithoutDot"), "nameWithoutDot"), + outputFile(path.join(projectDir, "bar/hello.txt"), "data"), + outputFile(path.join(projectDir, `bar/${process.arch}.txt`), "data"), + outputFile(path.join(projectDir, `${osName}/${process.arch}.txt`), "data"), + outputFile(path.join(projectDir, "platformSpecificR"), "platformSpecificR"), + outputFile(path.join(projectDir, "ignoreMe.txt"), "ignoreMe"), + ]) + }, + packed: async context => { + const base = path.join(context.outDir, platform.buildConfigurationKey + `${platform === Platform.MAC ? "" : "-unpacked"}`) + let resourcesDir = path.join(base, "resources") + if (platform === Platform.MAC) { + resourcesDir = path.join(base, "TestApp.app", "Contents", "Resources") + } + const appDir = path.join(resourcesDir, "app") - await assertThat(path.join(resourcesDir, "foo")).isDirectory() - await assertThat(path.join(appDir, "foo")).doesNotExist() + await assertThat(path.join(resourcesDir, "foo")).isDirectory() + await assertThat(path.join(appDir, "foo")).doesNotExist() - await assertThat(path.join(resourcesDir, "foo", "nameWithoutDot")).isFile() - await assertThat(path.join(appDir, "foo", "nameWithoutDot")).doesNotExist() + await assertThat(path.join(resourcesDir, "foo", "nameWithoutDot")).isFile() + await assertThat(path.join(appDir, "foo", "nameWithoutDot")).doesNotExist() - await assertThat(path.join(resourcesDir, "bar", "hello.txt")).isFile() - await assertThat(path.join(resourcesDir, "bar", `${process.arch}.txt`)).isFile() - await assertThat(path.join(appDir, "bar", `${process.arch}.txt`)).doesNotExist() + await assertThat(path.join(resourcesDir, "bar", "hello.txt")).isFile() + await assertThat(path.join(resourcesDir, "bar", `${process.arch}.txt`)).isFile() + await assertThat(path.join(appDir, "bar", `${process.arch}.txt`)).doesNotExist() - await assertThat(path.join(resourcesDir, osName, `${process.arch}.txt`)).isFile() - await assertThat(path.join(resourcesDir, "platformSpecificR")).isFile() - await assertThat(path.join(resourcesDir, "ignoreMe.txt")).doesNotExist() - }, - expectedContents: platform === Platform.WINDOWS ? pathSorter(expectedWinContents.concat( - winDirPrefix + "bar/hello.txt", - winDirPrefix + "bar/x64.txt", - winDirPrefix + "foo/nameWithoutDot", - winDirPrefix + "platformSpecificR", - winDirPrefix + "win/x64.txt" - )) : null, - }) - } + await BluebirdPromise.all([ + assertThat(path.join(resourcesDir, osName, `${process.arch}.txt`)).isFile(), + assertThat(path.join(resourcesDir, "platformSpecificR")).isFile(), + assertThat(path.join(resourcesDir, "ignoreMe.txt")).doesNotExist(), + ]) + }, + expectedContents: platform === Platform.WINDOWS ? pathSorter(expectedWinContents.concat( + winDirPrefix + "bar/hello.txt", + winDirPrefix + "bar/x64.txt", + winDirPrefix + "foo/nameWithoutDot", + winDirPrefix + "platformSpecificR", + winDirPrefix + "win/x64.txt" + )) : null, + }) }) \ No newline at end of file diff --git a/test/src/helpers/runTests.ts b/test/src/helpers/runTests.ts index 03e0d2da2a9..0c0ae20a3d9 100755 --- a/test/src/helpers/runTests.ts +++ b/test/src/helpers/runTests.ts @@ -107,7 +107,6 @@ async function runTests() { } process.env.SKIP_WIN = skipWin - process.env.CSC_IDENTITY_AUTO_DISCOVERY = "false" process.env.TEST_DIR = TEST_DIR const rootDir = path.join(__dirname, "..", "..", "..") diff --git a/test/src/httpRequestTest.ts b/test/src/httpRequestTest.ts index 3f9b30f537f..8372b6082fe 100644 --- a/test/src/httpRequestTest.ts +++ b/test/src/httpRequestTest.ts @@ -4,8 +4,14 @@ import { randomBytes } from "crypto" import { assertThat } from "./helpers/fileAssert" import * as path from "path" +if (process.env.ELECTRON_BUILDER_OFFLINE === "true") { + fit("Skip httpRequestTest — ELECTRON_BUILDER_OFFLINE is defined", () => { + console.warn("[SKIP] Skip httpRequestTest — ELECTRON_BUILDER_OFFLINE is defined") + }) +} + test.ifDevOrLinuxCi("download to nonexistent dir", async () => { - const tempFile = path.join(tmpdir(), `${process.pid}-${randomBytes(8).toString("hex")}`, Date.now().toString(), "foo.txt") + const tempFile = path.join(process.env.TEST_DIR || tmpdir(), `${process.pid}-${randomBytes(8).toString("hex")}`, Date.now().toString(16), "foo.txt") await download("https://drive.google.com/uc?export=download&id=0Bz3JwZ-jqfRONTkzTGlsMkM2TlE", tempFile) await assertThat(tempFile).isFile() }) \ No newline at end of file diff --git a/test/src/mac/macArchiveTest.ts b/test/src/mac/macArchiveTest.ts index fb3173af19e..c930f43b8ac 100644 --- a/test/src/mac/macArchiveTest.ts +++ b/test/src/mac/macArchiveTest.ts @@ -5,7 +5,9 @@ test.ifMac("invalid target", () => assertThat(createMacTargetTest(["ttt"], test("only zip", createMacTargetTest(["zip"], ["Test App ßW-1.1.0-mac.zip"])) -test.ifMac("pkg", createMacTargetTest(["pkg"], ["Test App ßW-1.1.0.pkg"])) +if (process.env.CSC_KEY_PASSWORD != null) { + test.ifMac("pkg", createMacTargetTest(["pkg"], ["Test App ßW-1.1.0.pkg"])) +} test("tar.gz", createMacTargetTest(["tar.gz"], ["Test App ßW-1.1.0-mac.tar.gz"])) diff --git a/test/src/nsisUpdaterTest.ts b/test/src/nsisUpdaterTest.ts index 23ed9adf8b7..0d1e612e0dc 100644 --- a/test/src/nsisUpdaterTest.ts +++ b/test/src/nsisUpdaterTest.ts @@ -4,10 +4,15 @@ import * as path from "path" import { TmpDir } from "out/util/tmp" import { outputFile } from "fs-extra-p" import { safeDump } from "js-yaml" -import { GenericServerOptions } from "out/options/publishOptions" -import { GithubOptions } from "out/options/publishOptions" +import { GenericServerOptions, GithubOptions } from "out/options/publishOptions" import BluebirdPromise from "bluebird-lst-c" +if (process.env.ELECTRON_BUILDER_OFFLINE === "true") { + fit("Skip ArtifactPublisherTest suite — ELECTRON_BUILDER_OFFLINE is defined", () => { + console.warn("[SKIP] Skip ArtifactPublisherTest suite — ELECTRON_BUILDER_OFFLINE is defined") + }) +} + const NsisUpdaterClass = require("../../nsis-auto-updater/out/nsis-auto-updater/src/NsisUpdater").NsisUpdater const g = (global)