diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 91f89ff9794..29e5559f470 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -101,7 +101,7 @@ Use one of the shared run configurations as a template and: ## Run Test using CLI ```sh -TEST_APP_TMP_DIR=/tmp/electron-builder-test NODE_PATH=. ./node_modules/.bin/ava --match="boring" test/out/nsisTest.js +TEST_APP_TMP_DIR=/tmp/electron-builder-test NODE_PATH=. ./node_modules/.bin/jest --env jest-environment-node-debug '/TestFileName\.\w+$' ``` where `TEST_APP_TMP_DIR` is specified to easily inspect and use test build, `boring` is the test name and `test/out/nsisTest.js` is the path to test file. \ No newline at end of file diff --git a/src/builder.ts b/src/builder.ts index e63c2b23938..c948d0fdc46 100644 --- a/src/builder.ts +++ b/src/builder.ts @@ -29,6 +29,8 @@ export interface CliOptions extends PackagerOptions, PublishOptions { dir?: boolean platform?: string + + project?: string } function addValue(map: Map>, key: K, value: T) { @@ -158,6 +160,11 @@ export function normalizeOptions(args: CliOptions): BuildOptions { delete result.ia32 delete result.x64 delete result.armv7l + + if (result.project != null) { + result.projectDir = result.project + } + delete result.project return result } @@ -231,6 +238,7 @@ export async function build(rawOptions?: CliOptions): Promise> { } } + //noinspection JSMismatchedCollectionQueryUpdate const artifactPaths: Array = [] packager.artifactCreated(event => { if (event.file != null) { diff --git a/src/cli/cliOptions.ts b/src/cli/cliOptions.ts index 8a5226de300..c557cbd9180 100644 --- a/src/cli/cliOptions.ts +++ b/src/cli/cliOptions.ts @@ -81,7 +81,16 @@ export function createYargs(): any { .option("extraMetadata", { alias: ["em"], group: buildGroup, - describe: "Inject properties to application package.json (asar only)", + describe: "Inject properties to package.json (asar only)", + }) + .option("prepackaged", { + alias: ["pd"], + group: buildGroup, + describe: "The path to prepackaged app (to pack into distributable format). Also requires `project`", + }) + .option("project", { + group: buildGroup, + describe: "The path to project directory (required only for `prepackaged`)", }) .strict() .group(["help", "version"], "Other:") diff --git a/src/packager.ts b/src/packager.ts index 73db795e82c..5ce27273da4 100644 --- a/src/packager.ts +++ b/src/packager.ts @@ -120,7 +120,7 @@ export class Packager implements BuildInfo { const platformToTarget: Map> = new Map() // custom packager - don't check wine - let checkWine = this.options.platformPackagerFactory == null + let checkWine = this.options.prepackaged == null && this.options.platformPackagerFactory == null for (const [platform, archToType] of this.options.targets!) { if (platform === Platform.MAC && process.platform === Platform.WINDOWS.nodeName) { throw new Error("Build for macOS is supported only on macOS, please see /~https://github.com/electron-userland/electron-builder/wiki/Multi-Platform-Build") @@ -241,6 +241,10 @@ export class Packager implements BuildInfo { } private async installAppDependencies(platform: Platform, arch: Arch): Promise { + if (this.options.prepackaged != null) { + return + } + const options = this.config if (options.nodeGypRebuild === true) { log(`Executing node-gyp rebuild for arch ${Arch[arch]}`) diff --git a/src/platformPackager.ts b/src/platformPackager.ts index d0021ab3a3e..b7bc79a82c1 100644 --- a/src/platformPackager.ts +++ b/src/platformPackager.ts @@ -56,6 +56,8 @@ export interface PackagerOptions { readonly effectiveOptionComputed?: (options: any) => Promise readonly extraMetadata?: any + + readonly prepackaged?: string } export interface BuildInfo { @@ -161,7 +163,7 @@ export abstract class PlatformPackager } async pack(outDir: string, arch: Arch, targets: Array, postAsyncTasks: Array>): Promise { - const appOutDir = this.computeAppOutDir(outDir, arch) + const appOutDir = this.options.prepackaged || this.computeAppOutDir(outDir, arch) await this.doPack(outDir, appOutDir, this.platform.nodeName, arch, this.platformSpecificBuildOptions) this.packageInDistributableFormat(appOutDir, arch, targets, postAsyncTasks) } @@ -177,6 +179,10 @@ export abstract class PlatformPackager } protected async doPack(outDir: string, appOutDir: string, platformName: string, arch: Arch, platformSpecificBuildOptions: DC) { + if (this.info.options.prepackaged != null) { + return + } + const asarOptions = this.computeAsarOptions(platformSpecificBuildOptions) const fileMatchOptions: FileMatchOptions = { arch: Arch[arch], diff --git a/src/targets/archive.ts b/src/targets/archive.ts index fa61ea146c5..d356f1892c5 100644 --- a/src/targets/archive.ts +++ b/src/targets/archive.ts @@ -3,6 +3,7 @@ import { CompressionLevel } from "../metadata" import * as path from "path" import { unlink } from "fs-extra-p" import { path7za } from "7zip-bin" +import { exists } from "../util/fs" class CompressionDescriptor { constructor(public flag: string, public env: string, public minLevel: string, public maxLevel: string = "-9") { @@ -72,9 +73,19 @@ export async function archive(compression: CompressionLevel | n, format: string, args.push(outFile, withoutDir ? "." : path.basename(dirToArchive)) - await spawn(path7za, args, { - cwd: withoutDir ? dirToArchive : path.dirname(dirToArchive), - }) + try { + await spawn(path7za, args, { + cwd: withoutDir ? dirToArchive : path.dirname(dirToArchive), + }) + } + catch (e) { + if (e.code === "ENOENT" && !(await exists(dirToArchive))) { + throw new Error(`Cannot create archive: "${dirToArchive}" doesn't exist`) + } + else { + throw e + } + } return outFile } \ No newline at end of file diff --git a/src/targets/fpm.ts b/src/targets/fpm.ts index af3eff0d758..0d3dfff8daf 100644 --- a/src/targets/fpm.ts +++ b/src/targets/fpm.ts @@ -3,7 +3,7 @@ import { smarten } from "../platformPackager" import { use, exec } from "../util/util" import * as path from "path" import { getBin } from "../util/binDownload" -import { readFile, outputFile } from "fs-extra-p" +import { readFile, outputFile, ensureDir } from "fs-extra-p" import BluebirdPromise from "bluebird-lst-c" import { LinuxTargetHelper, installPrefix } from "./LinuxTargetHelper" import * as errorMessages from "../errorMessages" @@ -11,6 +11,7 @@ import { TmpDir } from "../util/tmp" import { LinuxPackager } from "../linuxPackager" import { log } from "../util/log" import { Target } from "./targetFactory" +import { unlinkIfExists } from "../util/fs" const template = require("lodash.template") @@ -71,6 +72,10 @@ export default class FpmTarget extends Target { log(`Building ${target}`) const destination = path.join(this.outDir, this.packager.generateName(target, arch, true /* on Linux we use safe name — without space */)) + await unlinkIfExists(destination) + if (this.packager.info.options.prepackaged != null) { + await ensureDir(this.outDir) + } const scripts = await this.scriptFiles const packager = this.packager @@ -157,7 +162,7 @@ export default class FpmTarget extends Target { args.push(mapping.join("=/usr/share/icons/hicolor/")) } - args.push(`${await this.desktopEntry}=/usr/share/applications/${appInfo.productFilename}.desktop`) + args.push(`${await this.desktopEntry}=/usr/share/applications/${this.packager.executableName}.desktop`) await exec(await fpmPath, args) diff --git a/src/targets/snap.ts b/src/targets/snap.ts index 1b5d2decdfb..6e75d406608 100644 --- a/src/targets/snap.ts +++ b/src/targets/snap.ts @@ -10,7 +10,6 @@ import { safeDump } from "js-yaml" import { spawn } from "../util/util" import { homedir } from "os" import { Target } from "./targetFactory" -import BluebirdPromise from "bluebird-lst-c" export default class SnapTarget extends Target { private readonly options: SnapOptions = Object.assign({}, this.packager.platformSpecificBuildOptions, (this.packager.config)[this.name]) @@ -33,7 +32,7 @@ export default class SnapTarget extends Target { const isUseUbuntuPlatform = options.ubuntuAppPlatformContent != null if (isUseUbuntuPlatform) { // ubuntu-app-platform requires empty directory - await BluebirdPromise.all([this.helper.icons, emptyDir(path.join(extraSnapSourceDir, "ubuntu-app-platform"))]) + await emptyDir(path.join(extraSnapSourceDir, "ubuntu-app-platform")) } const snap: any = {} @@ -95,7 +94,7 @@ export default class SnapTarget extends Target { if (isUseUbuntuPlatform) { snap.parts.extra = { plugin: "dump", - source: extraSnapSourceDir + source: isUseDocker ? `/out/${path.basename(snapDir)}/${path.basename(extraSnapSourceDir)}` : extraSnapSourceDir } } @@ -108,14 +107,13 @@ export default class SnapTarget extends Target { if (isUseDocker) { await spawn("docker", ["run", "--rm", "-v", `${packager.info.projectDir}:/project`, - "-v", `/tmp/apt-cache:/var/cache/apt/archives`, "-v", `${homedir()}/.electron:/root/.electron`, // dist dir can be outside of project dir "-v", `${this.outDir}:/out`, "electronuserland/electron-builder:latest", "/bin/bash", "-c", `snapcraft --version && cp -R /out/${path.basename(snapDir)} /s/ && cd /s && snapcraft snap --target-arch ${toLinuxArchString(arch)} -o /out/${snapName}`], { cwd: packager.info.projectDir, - stdio: ["ignore", "inherit", "pipe"], + stdio: ["ignore", "inherit", "inherit"], }) } else { diff --git a/test/out/__snapshots__/BuildTest.js.snap b/test/out/__snapshots__/BuildTest.js.snap index e93478beef4..6a8229dd7e5 100644 --- a/test/out/__snapshots__/BuildTest.js.snap +++ b/test/out/__snapshots__/BuildTest.js.snap @@ -2,6 +2,7 @@ exports[`test cli 1`] = ` Object { "draft": undefined, "extraMetadata": undefined, + "prepackaged": undefined, "prerelease": undefined, "publish": undefined, "targets": Map { @@ -20,6 +21,7 @@ exports[`test cli 2`] = ` Object { "draft": undefined, "extraMetadata": undefined, + "prepackaged": undefined, "prerelease": undefined, "publish": undefined, "targets": Map { @@ -40,6 +42,7 @@ exports[`test cli 3`] = ` Object { "draft": undefined, "extraMetadata": undefined, + "prepackaged": undefined, "prerelease": undefined, "publish": undefined, "targets": Map { @@ -60,6 +63,7 @@ exports[`test cli 4`] = ` Object { "draft": undefined, "extraMetadata": undefined, + "prepackaged": undefined, "prerelease": undefined, "publish": undefined, "targets": Map { @@ -78,6 +82,7 @@ exports[`test cli 5`] = ` Object { "draft": undefined, "extraMetadata": undefined, + "prepackaged": undefined, "prerelease": undefined, "publish": undefined, "targets": Map { @@ -96,6 +101,7 @@ exports[`test cli 6`] = ` Object { "draft": undefined, "extraMetadata": undefined, + "prepackaged": undefined, "prerelease": undefined, "publish": undefined, "targets": Map { @@ -128,6 +134,7 @@ exports[`test cli 7`] = ` Object { "draft": undefined, "extraMetadata": undefined, + "prepackaged": undefined, "prerelease": undefined, "publish": undefined, "targets": Map { @@ -148,6 +155,7 @@ exports[`test cli 8`] = ` Object { "draft": undefined, "extraMetadata": undefined, + "prepackaged": undefined, "prerelease": undefined, "publish": undefined, "targets": Map { @@ -168,6 +176,7 @@ exports[`test cli 9`] = ` Object { "draft": undefined, "extraMetadata": undefined, + "prepackaged": undefined, "prerelease": undefined, "publish": undefined, "targets": Map { @@ -188,6 +197,7 @@ exports[`test cli 10`] = ` Object { "draft": undefined, "extraMetadata": undefined, + "prepackaged": undefined, "prerelease": undefined, "publish": undefined, "targets": Map { diff --git a/test/src/BuildTest.ts b/test/src/BuildTest.ts index cd8460a1606..b183462b3a5 100644 --- a/test/src/BuildTest.ts +++ b/test/src/BuildTest.ts @@ -12,14 +12,13 @@ import { move, outputJson } from "fs-extra-p" import BluebirdPromise from "bluebird-lst-c" import * as path from "path" import { assertThat } from "./helpers/fileAssert" -import { BuildOptions, Platform, PackagerOptions, DIR_TARGET } from "out" -import { normalizeOptions } from "out/builder" +import { BuildOptions, Platform, PackagerOptions, DIR_TARGET, Arch } from "out" +import { normalizeOptions, build } from "out/builder" import { createYargs } from "out/cli/cliOptions" import { extractFile } from "asar-electron-builder" import { ELECTRON_VERSION } from "./helpers/config" import isCi from "is-ci" import { checkWineVersion } from "out/packager" -import { Arch } from "out/metadata" test("cli", async () => { const yargs = createYargs() @@ -220,3 +219,16 @@ function currentPlatform(): PackagerOptions { targets: Platform.fromString(process.platform).createTarget(DIR_TARGET), } } + +test.ifDevOrLinuxCi("prepackaged", app({ + targets: Platform.LINUX.createTarget(DIR_TARGET), +}, { + packed: async (context) => { + await build(normalizeOptions({ + prepackaged: path.join(context.outDir, "linux-unpacked"), + project: context.projectDir, + linux: ["deb"] + })) + await assertThat(path.join(context.projectDir, "dist", "TestApp_1.1.0_amd64.deb")).isFile() + } +})) diff --git a/test/src/helpers/expectedContents.ts b/test/src/helpers/expectedContents.ts index 4a168150507..13074665b01 100755 --- a/test/src/helpers/expectedContents.ts +++ b/test/src/helpers/expectedContents.ts @@ -23,7 +23,7 @@ export const expectedLinuxContents = ["/", "/opt/TestApp/resources/app.asar", "/opt/TestApp/resources/electron.asar", "/usr/share/applications/", - "/usr/share/applications/TestApp.desktop", + "/usr/share/applications/testapp.desktop", "/usr/share/doc/", "/usr/share/icons/", "/usr/share/doc/testapp/", diff --git a/test/src/helpers/packTester.ts b/test/src/helpers/packTester.ts index d96d141b49c..2e8417eb5d0 100755 --- a/test/src/helpers/packTester.ts +++ b/test/src/helpers/packTester.ts @@ -229,9 +229,6 @@ async function checkLinuxResult(outDir: string, packager: Packager, checkOptions if (it === "/opt/TestApp/TestApp") { return `/opt/${productFilename}/TestApp` } - else if (it === "/usr/share/applications/TestApp.desktop") { - return `/usr/share/applications/${productFilename}.desktop` - } else { return it.replace(new RegExp("/opt/TestApp/", "g"), `/opt/${productFilename}/`) }