import { Arch, serializeToYaml } from "builder-util" import { outputFile } from "fs-extra" import { Lazy } from "lazy-val" import * as path from "path" import { Target } from "../core" import { LinuxPackager } from "../linuxPackager" import { AppImageOptions } from "../options/linuxOptions" import { getAppUpdatePublishConfiguration } from "../publish/PublishManager" import { executeAppBuilderAsJson, objectToArgs } from "../util/appBuilder" import { getNotLocalizedLicenseFile } from "../util/license" import { LinuxTargetHelper } from "./LinuxTargetHelper" import { createStageDir } from "./targetUtil" // https://unix.stackexchange.com/questions/375191/append-to-sub-directory-inside-squashfs-file export default class AppImageTarget extends Target { readonly options: AppImageOptions = { ...this.packager.platformSpecificBuildOptions, ...(this.packager.config as any)[this.name] } private readonly desktopEntry: Lazy constructor( ignored: string, private readonly packager: LinuxPackager, private readonly helper: LinuxTargetHelper, readonly outDir: string ) { super("appImage") this.desktopEntry = new Lazy(() => { const args = this.options.executableArgs?.join(" ") || "--no-sandbox" return helper.computeDesktopEntry(this.options, `AppRun ${args} %U`, { "X-AppImage-Version": `${packager.appInfo.buildVersion}`, }) }) } async build(appOutDir: string, arch: Arch): Promise { const packager = this.packager const options = this.options // https://github.com/electron-userland/electron-builder/issues/775 // https://github.com/electron-userland/electron-builder/issues/1726 // tslint:disable-next-line:no-invalid-template-strings const artifactName = packager.expandArtifactNamePattern(options, "AppImage", arch) const artifactPath = path.join(this.outDir, artifactName) await packager.info.emitArtifactBuildStarted({ targetPresentableName: "AppImage", file: artifactPath, arch, }) const c = await Promise.all([ this.desktopEntry.value, this.helper.icons, getAppUpdatePublishConfiguration(packager, arch, false /* in any case validation will be done on publish */), getNotLocalizedLicenseFile(options.license, this.packager, ["txt", "html"]), createStageDir(this, packager, arch), ]) const license = c[3] const stageDir = c[4] const publishConfig = c[2] if (publishConfig != null) { await outputFile(path.join(packager.getResourcesDir(stageDir.dir), "app-update.yml"), serializeToYaml(publishConfig)) } if ( this.packager.packagerOptions.effectiveOptionComputed != null && (await this.packager.packagerOptions.effectiveOptionComputed({ desktop: await this.desktopEntry.value })) ) { return } const args = [ "appimage", "--stage", stageDir.dir, "--arch", Arch[arch], "--output", artifactPath, "--app", appOutDir, "--configuration", JSON.stringify({ productName: this.packager.appInfo.productName, productFilename: this.packager.appInfo.productFilename, desktopEntry: c[0], executableName: this.packager.executableName, icons: c[1], fileAssociations: this.packager.fileAssociations, ...options, }), ] objectToArgs(args, { license, }) if (packager.compression === "maximum") { args.push("--compression", "xz") } await packager.info.emitArtifactBuildCompleted({ file: artifactPath, safeArtifactName: packager.computeSafeArtifactName(artifactName, "AppImage", arch, false), target: this, arch, packager, isWriteUpdateInfo: true, updateInfo: await executeAppBuilderAsJson(args), }) } }