diff --git a/.idea/dictionaries/develar.xml b/.idea/dictionaries/develar.xml
index fe554bc3971..1ea56ad7ed3 100644
--- a/.idea/dictionaries/develar.xml
+++ b/.idea/dictionaries/develar.xml
@@ -33,6 +33,7 @@
gnubin
graphicsmagick
grün
+ gsettings
gtar
guid
headerimage
diff --git a/docker/4/Dockerfile b/docker/4/Dockerfile
index f1f92365f06..aa21e2eb77a 100644
--- a/docker/4/Dockerfile
+++ b/docker/4/Dockerfile
@@ -1,6 +1,6 @@
FROM electronuserland/electron-builder:base
-ENV NODE_VERSION 4.4.6
+ENV NODE_VERSION 4.4.7
# https://github.com/npm/npm/issues/4531
RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
diff --git a/docker/appImage.sh b/docker/appImage.sh
new file mode 100755
index 00000000000..e058ee141c7
--- /dev/null
+++ b/docker/appImage.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+set -e
+
+dir=${PWD##*/}
+rm -rf ../${dir}.7z
+7za a -m0=lzma2 -mx=9 -mfb=64 -md=64m -ms=on ../${dir}.7z .
\ No newline at end of file
diff --git a/docker/base/Dockerfile b/docker/base/Dockerfile
index cccae8527cf..6d803b2929a 100644
--- a/docker/base/Dockerfile
+++ b/docker/base/Dockerfile
@@ -21,7 +21,7 @@ ENV FORCE_COLOR true
# libcurl4-openssl-dev, libtool and automake are required to build osslsigncode
RUN apt-get update -y && \
- apt-get install --no-install-recommends -y bsdtar build-essential autoconf automake libcurl4-openssl-dev libtool libssl-dev icnsutils graphicsmagick gcc-multilib g++-multilib libgnome-keyring-dev lzip rpm yasm && \
+ apt-get install --no-install-recommends -y xorriso bsdtar build-essential autoconf automake libcurl4-openssl-dev libtool libssl-dev icnsutils graphicsmagick gcc-multilib g++-multilib libgnome-keyring-dev lzip rpm yasm && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* && \
curl -L http://tukaani.org/xz/xz-$XZ_VERSION.tar.xz | tar -xJ && cd xz-$XZ_VERSION && ./configure && make && make install && cd .. && rm -rf xz-$XZ_VERSION && ldconfig && \
diff --git a/docs/Options.md b/docs/Options.md
index 52beb1322a8..fb3d969f073 100644
--- a/docs/Options.md
+++ b/docs/Options.md
@@ -35,7 +35,7 @@ Here documented only `electron-builder` specific options:
| --- | ---
| **name** | The application name.
| productName |
As [name](#AppMetadata-name), but allows you to specify a product name for your executable which contains spaces and other special characters not allowed in the [name property](https://docs.npmjs.com/files/package.json#name}).
-| **description** | The application description.
+| description | The application description.
| homepage | The url to the project [homepage](https://docs.npmjs.com/files/package.json#homepage) (NuGet Package projectUrl
(optional) or Linux Package URL (required)).
If not specified and your project repository is public on GitHub, it will be https://github.com/${user}/${project}
by default.
| license | *linux-only.* The [license](https://docs.npmjs.com/files/package.json#license) name.
diff --git a/src/appInfo.ts b/src/appInfo.ts
index 141e1da67e0..ab1c71533a7 100644
--- a/src/appInfo.ts
+++ b/src/appInfo.ts
@@ -10,7 +10,7 @@ import sanitizeFileName = require("sanitize-filename")
const __awaiter = require("./util/awaiter")
export class AppInfo {
- readonly description = smarten(this.metadata.description)
+ readonly description = smarten(this.metadata.description!)
// windows-only
versionString = {
@@ -27,7 +27,7 @@ export class AppInfo {
readonly productFilename: string
constructor(public metadata: AppMetadata, private devMetadata: DevMetadata) {
- let buildVersion = metadata.version
+ let buildVersion = metadata.version!
this.version = buildVersion
const buildNumber = this.buildNumber
@@ -40,7 +40,7 @@ export class AppInfo {
}
get companyName() {
- return this.metadata.author.name
+ return this.metadata.author!.name
}
get buildNumber(): string | null {
@@ -76,7 +76,7 @@ export class AppInfo {
if (copyright != null) {
return copyright
}
- return `Copyright © ${new Date().getFullYear()} ${this.metadata.author.name || this.productName}`
+ return `Copyright © ${new Date().getFullYear()} ${this.metadata.author!.name || this.productName}`
}
async computePackageUrl(): Promise {
diff --git a/src/builder.ts b/src/builder.ts
index a9f671883dc..a239f445ab6 100644
--- a/src/builder.ts
+++ b/src/builder.ts
@@ -23,8 +23,9 @@ export async function createPublisher(packager: Packager, options: PublishOption
throw new Error(`Please specify 'repository' in the dev package.json ('${packager.devPackageFile}')`)
}
else {
- log(`Creating Github Publisher — user: ${info.user}, project: ${info.project}, version: ${packager.metadata.version}`)
- return new GitHubPublisher(info.user, info.project, packager.metadata.version, options, isPublishOptionGuessed)
+ const version = packager.metadata.version!
+ log(`Creating Github Publisher — user: ${info.user}, project: ${info.project}, version: ${version}`)
+ return new GitHubPublisher(info.user, info.project, version, options, isPublishOptionGuessed)
}
}
diff --git a/src/metadata.ts b/src/metadata.ts
index cc903ebdb65..95a6741303f 100755
--- a/src/metadata.ts
+++ b/src/metadata.ts
@@ -9,7 +9,7 @@ export interface Metadata {
# Application `package.json`
*/
export interface AppMetadata extends Metadata {
- readonly version: string
+ readonly version?: string
/*
The application name.
@@ -25,11 +25,11 @@ export interface AppMetadata extends Metadata {
/*
The application description.
*/
- readonly description: string
+ readonly description?: string
readonly main?: string | null
- readonly author: AuthorMetadata
+ readonly author?: AuthorMetadata
/*
The url to the project [homepage](https://docs.npmjs.com/files/package.json#homepage) (NuGet Package `projectUrl` (optional) or Linux Package URL (required)).
diff --git a/src/packager.ts b/src/packager.ts
index 5440bb24d73..07ec80452a7 100644
--- a/src/packager.ts
+++ b/src/packager.ts
@@ -63,7 +63,15 @@ export class Packager implements BuildInfo {
this.isTwoPackageJsonProjectLayoutUsed = this.appDir !== this.projectDir
const appPackageFile = this.projectDir === this.appDir ? devPackageFile : path.join(this.appDir, "package.json")
- this.metadata = appPackageFile === devPackageFile ? (this.options.appMetadata || this.devMetadata) : deepAssign(await readPackageJson(appPackageFile), this.options.appMetadata)
+ if (appPackageFile === devPackageFile) {
+ if (this.options.appMetadata != null) {
+ this.devMetadata = deepAssign(this.devMetadata, this.options.appMetadata)
+ }
+ this.metadata = this.devMetadata
+ }
+ else {
+ this.metadata = deepAssign(await readPackageJson(appPackageFile), this.options.appMetadata)
+ }
this.checkMetadata(appPackageFile, devPackageFile)
checkConflictingOptions(this.devMetadata.build)
@@ -147,7 +155,7 @@ export class Packager implements BuildInfo {
throw new Error(`Please specify '${missedFieldName}' in the application package.json ('${appPackageFile}')`)
}
- const checkNotEmpty = (name: string, value: string) => {
+ const checkNotEmpty = (name: string, value: string | n) => {
if (isEmptyOrSpaces(value)) {
reportError(name)
}
@@ -178,10 +186,10 @@ export class Packager implements BuildInfo {
}
else {
const author = appMetadata.author
- if (author == null) {
+ if (author == null) {
throw new Error(`Please specify "author" in the application package.json ('${appPackageFile}') — it is used as company name.`)
}
- else if (author.email == null && this.options.targets!.has(Platform.LINUX)) {
+ else if (author.email == null && this.options.targets!.has(Platform.LINUX)) {
throw new Error(util.format(errorMessages.authorEmailIsMissed, appPackageFile))
}
diff --git a/src/targets/LinuxTargetHelper.ts b/src/targets/LinuxTargetHelper.ts
index ef14e2e3dc0..8b62f3b8e7f 100644
--- a/src/targets/LinuxTargetHelper.ts
+++ b/src/targets/LinuxTargetHelper.ts
@@ -64,7 +64,7 @@ export class LinuxTargetHelper {
}
}
- async computeDesktopEntry(relativeExec: boolean): Promise {
+ async computeDesktopEntry(exec?: string): Promise {
const appInfo = this.packager.appInfo
const custom = this.packager.platformSpecificBuildOptions.desktop
@@ -73,13 +73,11 @@ export class LinuxTargetHelper {
}
const productFilename = appInfo.productFilename
- const appExec = relativeExec ? `"${productFilename}"` : `"${installPrefix}/${productFilename}/${productFilename}"`
-
const tempFile = path.join(await this.tempDirPromise, `${productFilename}.desktop`)
await outputFile(tempFile, this.packager.platformSpecificBuildOptions.desktop || `[Desktop Entry]
Name=${appInfo.productName}
Comment=${this.packager.platformSpecificBuildOptions.description || appInfo.description}
-Exec=${appExec}
+Exec=${(exec == null ? `"${installPrefix}/${productFilename}/${productFilename}"` : exec)}
Terminal=false
Type=Application
Icon=${appInfo.name}
diff --git a/src/targets/appImage.ts b/src/targets/appImage.ts
index 1ff9e0effd8..94fe267b02a 100644
--- a/src/targets/appImage.ts
+++ b/src/targets/appImage.ts
@@ -1,7 +1,7 @@
import { PlatformPackager, TargetEx } from "../platformPackager"
import { LinuxBuildOptions, Arch } from "../metadata"
import * as path from "path"
-import { exec, unlinkIfExists } from "../util/util"
+import { exec, unlinkIfExists, spawn, debug } from "../util/util"
import { open, write, createReadStream, createWriteStream, close, chmod } from "fs-extra-p"
import { LinuxTargetHelper } from "./LinuxTargetHelper"
import { getBin } from "../util/binDownload"
@@ -11,58 +11,51 @@ import { Promise as BluebirdPromise } from "bluebird"
const __awaiter = require("../util/awaiter")
const appImageVersion = "AppImage-5"
-const appImagePathPromise = getBin("AppImage", appImageVersion, `https://dl.bintray.com/electron-userland/bin/${appImageVersion}.7z`, "")
+const appImagePathPromise = getBin("AppImage", appImageVersion, `https://dl.bintray.com/electron-userland/bin/${appImageVersion}.7z`, "19833e5db3cbc546432de8ddc8a54181489e6faad4944bd1f3138adf4b771259")
export default class AppImageTarget extends TargetEx {
- private readonly desktopEntry: Promise
-
constructor(private packager: PlatformPackager, private helper: LinuxTargetHelper, private outDir: string) {
super("appImage")
-
- this.desktopEntry = helper.computeDesktopEntry(true)
}
async build(appOutDir: string, arch: Arch): Promise {
const packager = this.packager
- const destination = path.join(this.outDir, packager.generateName(null, arch, true))
+ const image = path.join(this.outDir, packager.generateName(null, arch, true))
const appInfo = packager.appInfo
-
- await unlinkIfExists(destination)
+ await unlinkIfExists(image)
const appImagePath = await appImagePathPromise
const args = [
"-joliet", "on",
"-volid", "AppImage",
- "-dev", destination,
+ "-dev", image,
"-padding", "0",
- "-map", appOutDir, "/usr/bin", "--",
- "-map", await this.desktopEntry, `/${appInfo.name}.desktop`, "--",
- "-map", path.join(appImagePath, arch === Arch.ia32 ? "32": "64", "AppRun"), "/AppRun", "--",
+ "-map", appOutDir, "/usr/bin",
+ "-map", path.join(__dirname, "..", "..", "templates", "linux", "AppRun.sh"), `/AppRun`,
+ "-move", `/usr/bin/${appInfo.productFilename}`, "/usr/bin/app",
]
-
for (let [from, to] of (await this.helper.icons)) {
- args.push("-map", from, `/usr/share/icons/default/${to}`, "--",)
+ args.push("-map", from, `/usr/share/icons/default/${to}`)
}
// must be after this.helper.icons call
if (this.helper.maxIconPath == null) {
throw new Error("Icon is not provided")
}
- args.push("-map", this.helper.maxIconPath, "/.DirIcon", "--",)
- args.push("-map", this.helper.maxIconPath, `/${appInfo.name}${path.extname(this.helper.maxIconPath)}`, "--",)
+ args.push("-map", this.helper.maxIconPath, "/.DirIcon")
- args.push("-zisofs", `level=${packager.devMetadata.build.compression === "store" ? "0" : "9"}:block_size=128k:by_magic=off`, "-chown_r", "0")
- args.push("/", "--", "set_filter_r", "--zisofs", "/")
+ // args.push("-zisofs", `level=0:block_size=128k:by_magic=off`, "-chown_r", "0")
+ // args.push("/", "--", "set_filter_r", "--zisofs", "/")
- await exec("xorriso", args)
+ await exec(process.platform === "darwin" ? path.join(appImagePath, "xorriso") : "xorriso", args)
- const fd = await open(destination, "r+")
+ const fd = await open(image, "r+")
try {
await new BluebirdPromise((resolve, reject) => {
- const rd = createReadStream(path.join(appImagePath, arch === Arch.ia32 ? "32" : "64", "AppRun"))
+ const rd = createReadStream(path.join(appImagePath, arch === Arch.ia32 ? "32" : "64", "runtime"))
rd.on("error", reject)
- const wr = createWriteStream(destination, {fd: fd, autoClose: false})
+ const wr = createWriteStream(image, {fd: fd, autoClose: false})
wr.on("error", reject)
wr.on("finish", resolve)
rd.pipe(wr)
@@ -75,8 +68,15 @@ export default class AppImageTarget extends TargetEx {
await close(fd)
}
- await chmod(destination, "0755")
+ await chmod(image, "0755")
+ // we archive because you cannot distribute exe as is - e.g. Ubuntu clear exec flag and user cannot just click on AppImage to run
+ // also, LZMA compression - 29MB vs zip 42MB
+ // we use slow xz instead of 7za because 7za doesn't preserve exec file permissions for xz
+ await spawn("xz", ["--compress", "--force", image], {
+ cwd: path.dirname(image),
+ stdio: ["ignore", debug.enabled ? "inherit" : "ignore", "inherit"],
+ })
- packager.dispatchArtifactCreated(destination)
+ packager.dispatchArtifactCreated(image)
}
}
\ No newline at end of file
diff --git a/src/targets/fpm.ts b/src/targets/fpm.ts
index 7b026ba71f4..c332641a0d7 100644
--- a/src/targets/fpm.ts
+++ b/src/targets/fpm.ts
@@ -3,7 +3,6 @@ import { smarten, PlatformPackager, TargetEx } from "../platformPackager"
import { use, exec } from "../util/util"
import * as path from "path"
import { downloadFpm } from "../util/binDownload"
-import { tmpdir } from "os"
import { readFile, outputFile } from "fs-extra-p"
import { Promise as BluebirdPromise } from "bluebird"
import { LinuxTargetHelper, installPrefix } from "./LinuxTargetHelper"
@@ -27,7 +26,7 @@ export default class FpmTarget extends TargetEx {
super(name)
this.scriptFiles = this.createScripts(helper.tempDirPromise)
- this.desktopEntry = helper.computeDesktopEntry(false)
+ this.desktopEntry = helper.computeDesktopEntry()
}
private async createScripts(tempDirPromise: Promise): Promise> {
@@ -62,7 +61,7 @@ export default class FpmTarget extends TargetEx {
}
const options = this.options
- const author = options.maintainer || `${packager.appInfo.metadata.author.name} <${packager.appInfo.metadata.author.email}>`
+ const author = options.maintainer || `${appInfo.metadata.author!.name} <${appInfo.metadata.author!.email}>`
const synopsis = options.synopsis
const args = [
"-s", "dir",
diff --git a/src/util/util.ts b/src/util/util.ts
index c8ff8a184d0..a410cba6d75 100644
--- a/src/util/util.ts
+++ b/src/util/util.ts
@@ -7,7 +7,7 @@ import { readJson, stat, Stats, unlink } from "fs-extra-p"
import { yellow, red } from "chalk"
import debugFactory = require("debug")
import IDebugger = debug.IDebugger
-import { warn, task } from "./log"
+import { warn, task, log } from "./log"
import { createHash } from "crypto"
//noinspection JSUnusedLocalSymbols
@@ -79,6 +79,12 @@ export function exec(file: string, args?: Array | null, options?: ExecOp
return new BluebirdPromise((resolve, reject) => {
execFile(file, args, options, function (error, stdout, stderr) {
if (error == null) {
+ if (debug.enabled) {
+ if (stderr.length !== 0) {
+ log(stderr)
+ }
+ // log(stdout)
+ }
resolve(stdout)
}
else {
diff --git a/templates/linux/AppRun.sh b/templates/linux/AppRun.sh
new file mode 100755
index 00000000000..0961e95e890
--- /dev/null
+++ b/templates/linux/AppRun.sh
@@ -0,0 +1,239 @@
+#!/bin/bash
+
+# The purpose of this script is to provide lightweight desktop integration
+# into the host system without special help from the host system.
+# If you want to use it, then place this in usr/bin/$APPNAME.wrapper
+# and set it as the Exec= line of the .desktop file in the AppImage.
+#
+# For example, to install the appropriate icons for Scribus,
+# put them into the AppDir at the following locations:
+#
+# ./usr/share/icons/default/128x128/apps/scribus.png
+# ./usr/share/icons/default/128x128/mimetypes/application-vnd.scribus.png
+#
+# Note that the filename application-vnd.scribus.png is derived from
+# and must be match MimeType=application/vnd.scribus; in scribus.desktop
+# (with "/" characters replaced by "-").
+#
+# Then, change Exec=scribus to Exec=scribus.wrapper and place the script
+# below in usr/bin/scribus.wrapper and make it executable.
+# When you run AppRun, then AppRun runs the wrapper script below
+# which in turn will run the main application.
+#
+# TODO:
+# Handle multiple versions of the same AppImage?
+# Handle removed AppImages? Currently we are just setting TryExec=
+# See http://specifications.freedesktop.org/thumbnail-spec/thumbnail-spec-latest.html#DELETE
+# Possibly move this to the C runtime that is part of every AppImage?
+
+# Exit on errors
+set -e
+
+THIS="$0"
+
+HERE="$(dirname "$(readlink -f "${THIS}")")"
+export PATH="${HERE}/usr/bin:${HERE}/usr/sbin:${PATH}"
+export LD_LIBRARY_PATH="${HERE}/usr/lib:${LD_LIBRARY_PATH}"
+export XDG_DATA_DIRS="${HERE}/usr/share:${XDG_DATA_DIRS}"
+export GSETTINGS_SCHEMA_DIR="${HERE}/usr/share/glib-2.0/schemas:${GSETTINGS_SCHEMA_DIR}"
+
+# Be verbose if $DEBUG=1 is set
+if [ ! -z "$DEBUG" ] ; then
+ env
+ set -x
+fi
+
+args=("$@") # http://stackoverflow.com/questions/3190818/
+NUMBER_OF_ARGS="$#"
+
+# Please do not change $VENDORPREFIX as it will allow for desktop files
+# belonging to AppImages to be recognized by future AppImageKit components
+# such as desktop integration daemons
+VENDORPREFIX=appimagekit
+
+echo "$APPDIR"
+
+BIN="${HERE}/usr/bin/app"
+
+trap atexit EXIT
+
+# Note that the following handles 0, 1 or more arguments (file paths)
+# which can include blanks but uses a bashism; can the same be achieved
+# in POSIX-shell? (FIXME)
+# http://stackoverflow.com/questions/3190818
+atexit()
+{
+if [ $NUMBER_OF_ARGS -eq 0 ] ; then
+ exec "${BIN}"
+else
+ exec "${BIN}" "${args[@]}"
+fi
+}
+
+error()
+{
+ if [ -x /usr/bin/zenity ] ; then
+ LD_LIBRARY_PATH="" zenity --error --text "${1}" 2>/dev/null
+ elif [ -x /usr/bin/kdialog ] ; then
+ LD_LIBRARY_PATH="" kdialog --msgbox "${1}" 2>/dev/null
+ elif [ -x /usr/bin/Xdialog ] ; then
+ LD_LIBRARY_PATH="" Xdialog --msgbox "${1}" 2>/dev/null
+ else
+ echo "${1}"
+ fi
+ exit 1
+}
+
+yesno()
+{
+ TITLE=$1
+ TEXT=$2
+ if [ -x /usr/bin/zenity ] ; then
+ LD_LIBRARY_PATH="" zenity --question --title="$TITLE" --text="$TEXT" || exit 0
+ elif [ -x /usr/bin/kdialog ] ; then
+ LD_LIBRARY_PATH="" kdialog --caption "Disk auswerfen?" --title "$TITLE" -yesno "$TEXT" || exit 0
+ elif [ -x /usr/bin/Xdialog ] ; then
+ LD_LIBRARY_PATH="" Xdialog --title "$TITLE" --clear --yesno "$TEXT" 10 80 || exit 0
+ else
+ echo "zenity, kdialog, Xdialog missing. Skipping ${THIS}."
+ exit 0
+ fi
+}
+
+check_prevent()
+{
+ FILE=$1
+ if [ -e "$FILE" ] ; then
+ exit 0
+ fi
+}
+
+# Exit immediately of one of these files is present
+# (e.g., because the desktop environment wants to handle desktop integration itself)
+check_prevent "$HOME/.local/share/$VENDORPREFIX/no_desktopintegration"
+check_prevent "/usr/share/$VENDORPREFIX/no_desktopintegration"
+check_prevent "/etc/$VENDORPREFIX/no_desktopintegration"
+
+# Exit immediately if appimaged is running
+pidof appimaged 2>/dev/null && exit 0
+
+# Exit immediately if $DESKTOPINTEGRATION is not empty
+if [ ! -z "$DESKTOPINTEGRATION" ] ; then
+ exit 0
+fi
+
+check_dep()
+{
+ DEP=$1
+ if [ -z $(which $DEP) ] ; then
+ echo "$DEP is missing. Skipping ${THIS}."
+ exit 0
+ fi
+}
+
+# Check whether dependencies are present in base system (we do not bundle these)
+# http://cgit.freedesktop.org/xdg/desktop-file-utils/
+check_dep desktop-file-validate
+check_dep update-desktop-database
+check_dep desktop-file-install
+check_dep xdg-icon-resource
+check_dep xdg-mime
+check_dep xdg-desktop-menu
+
+DESKTOPFILE=$(find "$APPDIR" -maxdepth 1 -name "*.desktop" | head -n 1)
+echo "$DESKTOPFILE"
+DESKTOPFILE_NAME=$(basename $DESKTOPFILE)
+
+if [ ! -f "$DESKTOPFILE" ] ; then
+ echo "Desktop file is missing. Please run ${THIS} from within an AppImage."
+ exit 0
+fi
+
+if [ -z "$APPIMAGE" ] ; then
+ APPIMAGE="$APPDIR/AppRun"
+ # Not running from within an AppImage; hence using the AppRun for Exec=
+fi
+
+# Construct path to the icon according to
+# http://specifications.freedesktop.org/thumbnail-spec/thumbnail-spec-latest.html
+ABS_APPIMAGE=$(readlink -e "$APPIMAGE")
+ICONURL="file://$ABS_APPIMAGE"
+MD5=$(echo -n $ICONURL | md5sum | cut -c -32)
+ICONFILE="$HOME/.cache/thumbnails/normal/$MD5.png"
+if [ ! -f "$ICONFILE" ] ; then
+ echo "$ICONFILE is missing. Probably not running ${THIS} from within an AppImage."
+ echo "Hence falling back to using .DirIcon"
+ ICONFILE="$APPDIR/.DirIcon"
+fi
+
+# $XDG_DATA_DIRS contains the default paths /usr/local/share:/usr/share
+# desktop file has to be installed in an applications subdirectory
+# of one of the $XDG_DATA_DIRS components
+if [ -z "$XDG_DATA_DIRS" ] ; then
+ echo "\$XDG_DATA_DIRS is missing. Please run ${THIS} from within an AppImage."
+ exit 0
+fi
+
+# Determine where the desktop file should be installed
+if [[ $EUID -ne 0 ]]; then
+ DESTINATION_DIR_DESKTOP="$HOME/.local/share/applications"
+ SYSTEM_WIDE=""
+else
+ # TODO: Check $XDG_DATA_DIRS
+ DESTINATION_DIR_DESKTOP="/usr/local/share/applications"
+ SYSTEM_WIDE="--mode system" # for xdg-mime and xdg-icon-resource
+fi
+
+# Check if the desktop file is already there
+# and if so, whether it points to the same AppImage
+if [ -e "$DESTINATION_DIR_DESKTOP/$VENDORPREFIX-$DESKTOPFILE_NAME" ] ; then
+ # echo "$DESTINATION_DIR_DESKTOP/$VENDORPREFIX-$DESKTOPFILE_NAME already there"
+ EXEC=$(grep "^Exec=" "$DESTINATION_DIR_DESKTOP/$VENDORPREFIX-$DESKTOPFILE_NAME" | head -n 1 | cut -d " " -f 1)
+ # echo $EXEC
+ if [ "Exec=$APPIMAGE" == "$EXEC" ] ; then
+ exit 0
+ fi
+fi
+
+# We ask the user only if we have found no reason to skip until here
+if [ -z "$SKIP" ] ; then
+ yesno "Install" "Should a desktop file for $APPIMAGE be installed?"
+fi
+
+APP=$(echo "$DESKTOPFILE_NAME" | sed -e 's/.desktop//g')
+
+# If the user has agreed, rewrite and install the desktop file, and the MIME information
+if [ -z "$SKIP" ] ; then
+ # desktop-file-install is supposed to install .desktop files to the user's
+ # applications directory when run as a non-root user,
+ # and to /usr/share/applications if run as root
+ # but that does not really work for me...
+ desktop-file-install --rebuild-mime-info-cache \
+ --vendor=$VENDORPREFIX --set-key=Exec --set-value="${APPIMAGE} %U" \
+ --set-key=X-AppImage-Comment --set-value="Generated by ${THIS}" \
+ --set-icon=$ICONFILE --set-key=TryExec --set-value=$APPIMAGE $DESKTOPFILE \
+ --dir "$DESTINATION_DIR_DESKTOP"
+ chmod a+x "$DESTINATION_DIR_DESKTOP/"*
+ RESOURCE_NAME=$(echo "$VENDORPREFIX-$DESKTOPFILE_NAME" | sed -e 's/.desktop//g')
+ echo $RESOURCE_NAME
+
+ # Install the icon files for the application; TODO: scalable
+ ICONS=$(find "${APPDIR}/usr/share/icons/" -wholename "*/apps/${APP}.png" || true)
+ for ICON in $ICONS ; do
+ ICON_SIZE=$(echo "${ICON}" | rev | cut -d "/" -f 3 | rev | cut -d "x" -f 1)
+ xdg-icon-resource install --context apps --size ${ICON_SIZE} "${ICON}" "${RESOURCE_NAME}"
+ done
+
+ # Install mime type
+ find "${APPDIR}/usr/share/mime/" -type f -name *xml -exec xdg-mime install $SYSTEM_WIDE --novendor {} \; || true
+
+ # Install the icon files for the mime type; TODO: scalable
+ ICONS=$(find "${APPDIR}/usr/share/icons/" -wholename "*/mimetypes/*.png" || true)
+ for ICON in $ICONS ; do
+ ICON_SIZE=$(echo "${ICON}" | rev | cut -d "/" -f 3 | rev | cut -d "x" -f 1)
+ xdg-icon-resource install --context mimetypes --size ${ICON_SIZE} "${ICON}" $(basename $ICON | sed -e 's/.png//g')
+ done
+
+ xdg-desktop-menu forceupdate
+ gtk-update-icon-cache # for MIME
+fi
\ No newline at end of file
diff --git a/test/src/helpers/packTester.ts b/test/src/helpers/packTester.ts
index 1aa913f3ca0..1f554ab6ff6 100755
--- a/test/src/helpers/packTester.ts
+++ b/test/src/helpers/packTester.ts
@@ -137,6 +137,10 @@ async function checkLinuxResult(projectDir: string, packager: Packager, checkOpt
return result
}
+ if (nameToTarget.has("appimage")) {
+ return
+ }
+
assertThat(getFileNames(artifacts)).containsAll(getExpected())
if (!nameToTarget.has("deb")) {
diff --git a/test/src/linuxPackagerTest.ts b/test/src/linuxPackagerTest.ts
index 1729174b367..0d0b8753a4a 100755
--- a/test/src/linuxPackagerTest.ts
+++ b/test/src/linuxPackagerTest.ts
@@ -9,10 +9,14 @@ const __awaiter = require("out/util/awaiter")
test.ifNotWindows("deb", () => assertPack("test-app-one", platform(Platform.LINUX)))
-// test.ifDevOrLinuxCi("AppImage", () => assertPack("test-app-one", {
-// targets: Platform.LINUX.createTarget("appimage")
-// }
-// ))
+test.ifDevOrLinuxCi("AppImage", () => assertPack("test-app-one", {
+ targets: Platform.LINUX.createTarget("appimage"),
+ appMetadata: {
+ name: "Bar",
+ productName: "Bar",
+ },
+ }
+))
test.ifDevOrLinuxCi("targets", () => assertPack("test-app-one", {
targets: Platform.LINUX.createTarget(),