diff --git a/src/builder.ts b/src/builder.ts index 433b69d088a..d31c51ea78a 100644 --- a/src/builder.ts +++ b/src/builder.ts @@ -76,13 +76,13 @@ export async function build(options: BuildOptions = {}): Promise { const packager = new Packager(options, repositoryInfo) if (options.publish != null && options.publish !== "never") { let publisher: BluebirdPromise = null - packager.artifactCreated((file, platform) => { + packager.artifactCreated(event => { if (publisher == null) { publisher = >createPublisher(packager, options, repositoryInfo, isPublishOptionGuessed) } if (publisher != null) { - publisher.then(it => publishTasks.push(>it.upload(file))) + publisher.then(it => publishTasks.push(>it.upload(event.file, event.artifactName))) } }) } diff --git a/src/gitHubPublisher.ts b/src/gitHubPublisher.ts index b8ee6ba8761..7e714827c09 100644 --- a/src/gitHubPublisher.ts +++ b/src/gitHubPublisher.ts @@ -15,7 +15,7 @@ import ProgressBar = require("progress") const __awaiter = require("./awaiter") export interface Publisher { - upload(path: string): Promise + upload(file: string, artifactName?: string): Promise } export interface PublishOptions { @@ -66,15 +66,15 @@ export class GitHubPublisher implements Publisher { } } - async upload(path: string): Promise { - const fileName = basename(path) + async upload(file: string, artifactName?: string): Promise { + const fileName = artifactName || basename(file) const release = await this.releasePromise if (release == null) { return null } const parsedUrl = parseUrl(release.upload_url.substring(0, release.upload_url.indexOf("{")) + "?name=" + fileName) - const fileStat = await stat(path) + const fileStat = await stat(file) let badGatewayCount = 0 uploadAttempt: for (let i = 0; i < 3; i++) { const progressBar = (process.stdin).isTTY ? new ProgressBar(`Uploading ${fileName} [:bar] :percent :etas`, { @@ -96,7 +96,7 @@ export class GitHubPublisher implements Publisher { "Content-Length": fileStat.size } }, this.token, (request, reject) => { - const fileInputStream = createReadStream(path) + const fileInputStream = createReadStream(file) fileInputStream.on("error", reject) fileInputStream .pipe(progressStream({ diff --git a/src/index.ts b/src/index.ts index 2e3ef35956d..e1a653c11a4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,3 @@ export { Packager } from "./packager" -export { PackagerOptions } from "./platformPackager" +export { PackagerOptions, ArtifactCreated } from "./platformPackager" export { AppMetadata, DevMetadata, Platform, getProductName } from "./metadata" \ No newline at end of file diff --git a/src/macPackager.ts b/src/macPackager.ts index 4876e1beb6f..f5469a1814b 100644 --- a/src/macPackager.ts +++ b/src/macPackager.ts @@ -53,7 +53,7 @@ export default class MacPackager extends PlatformPackager { } packageInDistributableFormat(outDir: string, appOutDir: string): Promise { - const artifactPath = path.join(appOutDir, this.metadata.name + "-" + this.metadata.version + ".dmg") + const artifactPath = path.join(appOutDir, `${this.appName}-${this.metadata.version}.dmg`) return BluebirdPromise.all([ new BluebirdPromise((resolve, reject) => { log("Creating DMG") @@ -83,17 +83,18 @@ export default class MacPackager extends PlatformPackager { emitter.on("error", reject) emitter.on("finish", () => resolve()) }) - .then(() => this.dispatchArtifactCreated(artifactPath)), + .then(() => this.dispatchArtifactCreated(artifactPath, `${this.metadata.name}-${this.metadata.version}.dmg`)), this.zipMacApp(appOutDir) - .then(it => this.dispatchArtifactCreated(it)) + .then(it => this.dispatchArtifactCreated(it, `${this.metadata.name}-${this.metadata.version}-mac.zip`)) ]) } private zipMacApp(outDir: string): Promise { log("Creating ZIP for Squirrel.Mac") + // we use app name here - see https://github.com/electron-userland/electron-builder/pull/204 + const resultPath = `${this.appName}-${this.metadata.version}-mac.zip` // -y param is important - "store symbolic links as the link instead of the referenced file" - const resultPath = `${this.metadata.name}-${this.metadata.version}-mac.zip` const args = ["-ryXq", resultPath, this.appName + ".app"] // todo move to options diff --git a/src/packager.ts b/src/packager.ts index 80b5e0eecfb..e03837fcff6 100644 --- a/src/packager.ts +++ b/src/packager.ts @@ -5,8 +5,8 @@ import { all, executeFinally } from "./promise" import { EventEmitter } from "events" import { Promise as BluebirdPromise } from "bluebird" import { InfoRetriever } from "./repositoryInfo" -import { AppMetadata, Platform, DevMetadata } from "./metadata" -import { PackagerOptions, PlatformPackager, BuildInfo } from "./platformPackager" +import { AppMetadata, DevMetadata } from "./metadata" +import { PackagerOptions, PlatformPackager, BuildInfo, ArtifactCreated } from "./platformPackager" import MacPackager from "./macPackager" import WinPackager from "./winPackager" import * as errorMessages from "./errorMessages" @@ -38,7 +38,7 @@ export class Packager implements BuildInfo { this.appDir = this.computeAppDirectory() } - artifactCreated(handler: (file: string, platform: Platform) => void): Packager { + artifactCreated(handler: (event: ArtifactCreated) => void): Packager { addHandler(this.eventEmitter, "artifactCreated", handler) return this } diff --git a/src/platformPackager.ts b/src/platformPackager.ts index 3bc296c90ea..dc0b954f665 100644 --- a/src/platformPackager.ts +++ b/src/platformPackager.ts @@ -80,8 +80,12 @@ export abstract class PlatformPackager return (directories == null ? null : directories.buildResources) || "build" } - protected dispatchArtifactCreated(file: string) { - this.info.eventEmitter.emit("artifactCreated", file, this.platform) + protected dispatchArtifactCreated(file: string, artifactName?: string) { + this.info.eventEmitter.emit("artifactCreated", { + file: file, + artifactName: artifactName, + platform: this.platform, + }) } async pack(platform: string, outDir: string, appOutDir: string, arch: string): Promise { @@ -155,4 +159,11 @@ function checkConflictingOptions(options: any): void { throw new Error(`Option ${name} is ignored, do not specify it.`) } } +} + +export interface ArtifactCreated { + readonly file: string + readonly artifactName?: string + + readonly platform: Platform } \ No newline at end of file diff --git a/src/winPackager.ts b/src/winPackager.ts index 20c09f07024..aff67bb375c 100644 --- a/src/winPackager.ts +++ b/src/winPackager.ts @@ -107,8 +107,6 @@ export default class WinPackager extends PlatformPackager { noMsi: true, }, this.customBuildOptions) - // we use metadata.name instead of appName because appName can contains unsafe chars - const installerExePath = path.join(installerOutDir, this.metadata.name + "Setup-" + version + archSuffix + ".exe") try { await require("electron-winstaller-fixed").createWindowsInstaller(options) } @@ -141,8 +139,8 @@ export default class WinPackager extends PlatformPackager { } const promises: Array> = [ - rename(path.join(installerOutDir, "Setup.exe"), installerExePath) - .then(it => this.dispatchArtifactCreated(it)), + rename(path.join(installerOutDir, "Setup.exe"), path.join(installerOutDir, `${this.appName}Setup-${version}${archSuffix}.exe`)) + .then(it => this.dispatchArtifactCreated(it, `${this.metadata.name}Setup-${version}${archSuffix}.exe`)), ] if (archSuffix === "") { diff --git a/test/src/helpers/packTester.ts b/test/src/helpers/packTester.ts index de62c9907c7..2c78c90cd1a 100755 --- a/test/src/helpers/packTester.ts +++ b/test/src/helpers/packTester.ts @@ -5,7 +5,7 @@ import { parse as parsePlist } from "plist" import { CSC_LINK, CSC_KEY_PASSWORD } from "./codeSignData" import { expectedLinuxContents } from "./expectedContents" import { readText } from "out/promisifed-fs" -import { Packager, PackagerOptions, Platform, getProductName } from "out" +import { Packager, PackagerOptions, Platform, getProductName, ArtifactCreated } from "out" import { normalizePlatforms } from "out/packager" import { exec } from "out/util" import pathSorter = require("path-sort") @@ -73,15 +73,15 @@ export async function assertPack(fixtureName: string, async function packAndCheck(projectDir: string, packagerOptions: PackagerOptions): Promise { const packager = new Packager(packagerOptions) - const artifacts: Map> = new Map() - packager.artifactCreated((file, platform) => { - assertThat(path.isAbsolute(file)).true() - let list = artifacts.get(platform) + const artifacts: Map> = new Map() + packager.artifactCreated(event => { + assertThat(path.isAbsolute(event.file)).true() + let list = artifacts.get(event.platform) if (list == null) { list = [] - artifacts.set(platform, list) + artifacts.set(event.platform, list) } - list.push(file) + list.push(event) }) await packager.build() @@ -90,44 +90,41 @@ async function packAndCheck(projectDir: string, packagerOptions: PackagerOptions return } - for (let key of artifacts.keys()) { - artifacts.set(key, pathSorter(artifacts.get(key))) - } - - const expandedPlatforms = normalizePlatforms(packagerOptions.platform) - if (expandedPlatforms.includes("darwin")) { - await checkOsXResult(packager, artifacts.get(Platform.OSX)) - } - else if (expandedPlatforms.includes("linux")) { - const productName = getProductName(packager.metadata, packager.devMetadata) - const expectedContents = expectedLinuxContents.map(it => { - if (it === "/opt/TestApp/TestApp") { - return "/opt/" + productName + "/" + productName - } - else if (it === "/usr/share/applications/TestApp.desktop") { - return `/usr/share/applications/${productName}.desktop` - } - else { - return it.replace(new RegExp("/opt/TestApp/", "g"), `/opt/${productName}/`) + for (let platform of normalizePlatforms(packagerOptions.platform)) { + if (platform === "darwin") { + await checkOsXResult(packager, artifacts.get(Platform.OSX)) + } + else if (platform === "linux") { + const productName = getProductName(packager.metadata, packager.devMetadata) + const expectedContents = expectedLinuxContents.map(it => { + if (it === "/opt/TestApp/TestApp") { + return "/opt/" + productName + "/" + productName + } + else if (it === "/usr/share/applications/TestApp.desktop") { + return `/usr/share/applications/${productName}.desktop` + } + else { + return it.replace(new RegExp("/opt/TestApp/", "g"), `/opt/${productName}/`) + } + }) + + // console.log(JSON.stringify(await getContents(projectDir + "/dist/TestApp-1.0.0-amd64.deb", productName), null, 2)) + // console.log(JSON.stringify(await getContents(projectDir + "/dist/TestApp-1.0.0-i386.deb", productName), null, 2)) + + assertThat(await getContents(projectDir + "/dist/TestApp-1.0.0-amd64.deb", productName)).deepEqual(expectedContents) + if (packagerOptions == null || packagerOptions.arch === null || packagerOptions.arch === "ia32") { + assertThat(await getContents(projectDir + "/dist/TestApp-1.0.0-i386.deb", productName)).deepEqual(expectedContents) } - }) - - // console.log(JSON.stringify(await getContents(projectDir + "/dist/TestApp-1.0.0-amd64.deb", productName), null, 2)) - // console.log(JSON.stringify(await getContents(projectDir + "/dist/TestApp-1.0.0-i386.deb", productName), null, 2)) - - assertThat(await getContents(projectDir + "/dist/TestApp-1.0.0-amd64.deb", productName)).deepEqual(expectedContents) - if (packagerOptions == null || packagerOptions.arch === null || packagerOptions.arch === "ia32") { - assertThat(await getContents(projectDir + "/dist/TestApp-1.0.0-i386.deb", productName)).deepEqual(expectedContents) } - } - else if (expandedPlatforms.includes("win32") && (packagerOptions == null || packagerOptions.target == null)) { - await checkWindowsResult(packagerOptions, artifacts.get(Platform.WINDOWS)) + else if (platform === "win32" && (packagerOptions == null || packagerOptions.target == null)) { + await checkWindowsResult(packager, packagerOptions, artifacts.get(Platform.WINDOWS)) + } } } -async function checkOsXResult(packager: Packager, artifacts: Array) { +async function checkOsXResult(packager: Packager, artifacts: Array) { const productName = getProductName(packager.metadata, packager.devMetadata) - const packedAppDir = path.join(path.dirname(artifacts[0]), (productName || packager.metadata.name) + ".app") + const packedAppDir = path.join(path.dirname(artifacts[0].file), (productName || packager.metadata.name) + ".app") const info = parsePlist(await readText(path.join(packedAppDir, "Contents", "Info.plist"))) assertThat(info).has.properties({ CFBundleDisplayName: productName, @@ -139,30 +136,43 @@ async function checkOsXResult(packager: Packager, artifacts: Array) { const result = await exec("codesign", ["--verify", packedAppDir]) assertThat(result[0].toString()).not.match(/is not signed at all/) - assertThat(artifacts.map(it => path.basename((it))).sort()).deepEqual([ + assertThat(artifacts.map(it => path.basename(it.file)).sort()).deepEqual([ + `${productName}-1.0.0-mac.zip`, + `${productName}-1.0.0.dmg`, + ].sort()) + + assertThat(artifacts.map(it => it.artifactName).sort()).deepEqual([ "TestApp-1.0.0-mac.zip", - "TestApp-1.0.0.dmg" + "TestApp-1.0.0.dmg", ].sort()) } -async function checkWindowsResult(packagerOptions: PackagerOptions, artifacts: Array) { - const expected32 = [ - "RELEASES-ia32", - "TestAppSetup-1.0.0-ia32.exe", - "TestApp-1.0.0-ia32-full.nupkg", - ] - const expected64 = [ - "RELEASES", - "TestAppSetup-1.0.0.exe", - "TestApp-1.0.0-full.nupkg", - ] - const expected = packagerOptions != null && packagerOptions.arch === "x64" ? expected64 : expected32.concat(expected64) - const filenames = artifacts.map(it => path.basename((it))) - assertThat(filenames.slice().sort()).deepEqual(expected.sort()) +async function checkWindowsResult(packager: Packager, packagerOptions: PackagerOptions, artifacts: Array) { + const productName = getProductName(packager.metadata, packager.devMetadata) + + function getWinExpected(archSuffix: string) { + return [ + `RELEASES${archSuffix}`, + `${productName}Setup-1.0.0${archSuffix}.exe`, + `TestApp-1.0.0${archSuffix}-full.nupkg`, + ] + } + + const archSuffix = packagerOptions != null && packagerOptions.arch === "x64" ? "" : "-ia32" + const expected = archSuffix == "" ? getWinExpected(archSuffix) : getWinExpected(archSuffix).concat(getWinExpected("")) + + const filenames = artifacts.map(it => path.basename(it.file)) + assertThat(filenames.slice().sort()).deepEqual(expected.slice().sort()) let i = filenames.indexOf("RELEASES-ia32") if (i !== -1) { - assertThat((await readText(artifacts[i])).indexOf("ia32")).not.equal(-1) + assertThat((await readText(artifacts[i].file)).indexOf("ia32")).not.equal(-1) + } + + if (archSuffix == "") { + const expectedArtifactNames = expected.slice() + expectedArtifactNames[1] = `TestAppSetup-1.0.0${archSuffix}.exe` + assertThat(artifacts.map(it => it.artifactName).filter(it => it != null)).deepEqual([`TestAppSetup-1.0.0${archSuffix}.exe`]) } }