Skip to content

Commit 0dbe357

Browse files
authored
feat: allowing additional entries in .desktop file, such as [Desktop Actions <actionName>] (#8572)
1 parent a25d04d commit 0dbe357

10 files changed

Lines changed: 175 additions & 37 deletions

File tree

.changeset/sweet-masks-sparkle.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"app-builder-lib": major
3+
---
4+
5+
feat: allowing additional entries in .desktop file, such as `[Desktop Actions <actionName>]`. Requires changing configuration `desktop` property to object to be more extensible in the future

packages/app-builder-lib/scheme.json

Lines changed: 83 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,15 @@
5858
]
5959
},
6060
"desktop": {
61-
"description": "The [Desktop file](https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html#desktop-files) entries (name to value)."
61+
"anyOf": [
62+
{
63+
"$ref": "#/definitions/LinuxDesktopFile"
64+
},
65+
{
66+
"type": "null"
67+
}
68+
],
69+
"description": "The [Desktop file](https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html#desktop-files)"
6270
},
6371
"executableArgs": {
6472
"anyOf": [
@@ -576,7 +584,15 @@
576584
]
577585
},
578586
"desktop": {
579-
"description": "The [Desktop file](https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html#desktop-files) entries (name to value)."
587+
"anyOf": [
588+
{
589+
"$ref": "#/definitions/LinuxDesktopFile"
590+
},
591+
{
592+
"type": "null"
593+
}
594+
],
595+
"description": "The [Desktop file](https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html#desktop-files)"
580596
},
581597
"executableArgs": {
582598
"anyOf": [
@@ -1111,7 +1127,15 @@
11111127
]
11121128
},
11131129
"desktop": {
1114-
"description": "The [Desktop file](https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html#desktop-files) entries (name to value)."
1130+
"anyOf": [
1131+
{
1132+
"$ref": "#/definitions/LinuxDesktopFile"
1133+
},
1134+
{
1135+
"type": "null"
1136+
}
1137+
],
1138+
"description": "The [Desktop file](https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html#desktop-files)"
11151139
},
11161140
"executableArgs": {
11171141
"anyOf": [
@@ -1645,7 +1669,15 @@
16451669
]
16461670
},
16471671
"desktop": {
1648-
"description": "The [Desktop file](https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html#desktop-files) entries (name to value)."
1672+
"anyOf": [
1673+
{
1674+
"$ref": "#/definitions/LinuxDesktopFile"
1675+
},
1676+
{
1677+
"type": "null"
1678+
}
1679+
],
1680+
"description": "The [Desktop file](https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html#desktop-files)"
16491681
},
16501682
"detectUpdateChannel": {
16511683
"default": true,
@@ -1941,6 +1973,35 @@
19411973
},
19421974
"type": "object"
19431975
},
1976+
"LinuxDesktopFile": {
1977+
"additionalProperties": false,
1978+
"description": "Example Spec: https://specifications.freedesktop.org/desktop-entry-spec/latest/example.html",
1979+
"properties": {
1980+
"desktopActions": {
1981+
"anyOf": [
1982+
{
1983+
"typeof": "function"
1984+
},
1985+
{
1986+
"type": "null"
1987+
}
1988+
],
1989+
"description": "`[Desktop Actions <ActionName>]` metadata entries (name to value).\n\nConfig Example:\n```js\ndesktopActions: {\n NewWindow: {\n Name: 'New Window',\n Exec: 'app --new-window',\n }\n}\n```"
1990+
},
1991+
"entry": {
1992+
"anyOf": [
1993+
{
1994+
"typeof": "function"
1995+
},
1996+
{
1997+
"type": "null"
1998+
}
1999+
],
2000+
"description": "`[Desktop Entry]` metadata entries (name to value). Overwrites default values calculated by electron-builder"
2001+
}
2002+
},
2003+
"type": "object"
2004+
},
19442005
"LinuxTargetSpecificOptions": {
19452006
"additionalProperties": false,
19462007
"properties": {
@@ -2010,7 +2071,15 @@
20102071
]
20112072
},
20122073
"desktop": {
2013-
"description": "The [Desktop file](https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html#desktop-files) entries (name to value)."
2074+
"anyOf": [
2075+
{
2076+
"$ref": "#/definitions/LinuxDesktopFile"
2077+
},
2078+
{
2079+
"type": "null"
2080+
}
2081+
],
2082+
"description": "The [Desktop file](https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html#desktop-files)"
20142083
},
20152084
"executableArgs": {
20162085
"anyOf": [
@@ -5463,7 +5532,15 @@
54635532
]
54645533
},
54655534
"desktop": {
5466-
"description": "The [Desktop file](https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html#desktop-files) entries (name to value)."
5535+
"anyOf": [
5536+
{
5537+
"$ref": "#/definitions/LinuxDesktopFile"
5538+
},
5539+
{
5540+
"type": "null"
5541+
}
5542+
],
5543+
"description": "The [Desktop file](https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html#desktop-files)"
54675544
},
54685545
"environment": {
54695546
"anyOf": [

packages/app-builder-lib/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export { MsiOptions } from "./options/MsiOptions"
3333
export { MsiWrappedOptions } from "./options/MsiWrappedOptions"
3434
export { CommonWindowsInstallerConfiguration } from "./options/CommonWindowsInstallerConfiguration"
3535
export { NsisOptions, NsisWebOptions, PortableOptions, CommonNsisOptions, CustomNsisBinary } from "./targets/nsis/nsisOptions"
36-
export { LinuxConfiguration, DebOptions, CommonLinuxOptions, LinuxTargetSpecificOptions, AppImageOptions, FlatpakOptions } from "./options/linuxOptions"
36+
export { LinuxConfiguration, DebOptions, CommonLinuxOptions, LinuxTargetSpecificOptions, AppImageOptions, FlatpakOptions, LinuxDesktopFile } from "./options/linuxOptions"
3737
export { SnapOptions, PlugDescriptor, SlotDescriptor } from "./options/SnapOptions"
3838
export { Metadata, AuthorMetadata, RepositoryInfo } from "./options/metadata"
3939
export { AppInfo } from "./appInfo"

packages/app-builder-lib/src/options/linuxOptions.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,33 @@
11
import { PlatformSpecificBuildOptions, TargetConfigType, TargetSpecificOptions } from "../index"
22

3+
/**
4+
* Example Spec: https://specifications.freedesktop.org/desktop-entry-spec/latest/example.html
5+
*/
6+
export interface LinuxDesktopFile {
7+
/**
8+
* `[Desktop Entry]` metadata entries (name to value). Overwrites default values calculated by electron-builder
9+
*/
10+
entry?: {
11+
[k: string]: string
12+
} | null
13+
/**
14+
* `[Desktop Actions <ActionName>]` metadata entries (name to value).
15+
*
16+
* Config Example:
17+
* ```js
18+
* desktopActions: {
19+
* NewWindow: {
20+
* Name: 'New Window',
21+
* Exec: 'app --new-window',
22+
* }
23+
* }
24+
* ```
25+
*/
26+
desktopActions?: {
27+
[ActionName: string]: any
28+
} | null
29+
}
30+
331
export interface LinuxConfiguration extends CommonLinuxOptions, PlatformSpecificBuildOptions {
432
/**
533
* Target package type: list of `AppImage`, `flatpak`, `snap`, `deb`, `rpm`, `freebsd`, `pacman`, `p5p`, `apk`, `7z`, `zip`, `tar.xz`, `tar.lz`, `tar.gz`, `tar.bz2`, `dir`.
@@ -56,9 +84,9 @@ export interface CommonLinuxOptions {
5684
readonly mimeTypes?: Array<string> | null
5785

5886
/**
59-
* The [Desktop file](https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html#desktop-files) entries (name to value).
87+
* The [Desktop file](https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html#desktop-files)
6088
*/
61-
readonly desktop?: any | null
89+
readonly desktop?: LinuxDesktopFile | null
6290

6391
/**
6492
* The executable parameters. Pass to executableName

packages/app-builder-lib/src/targets/LinuxTargetHelper.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export class LinuxTargetHelper {
103103
throw new Error("Specified exec is empty")
104104
}
105105
// https://github.com/electron-userland/electron-builder/issues/3418
106-
if (targetSpecificOptions.desktop != null && targetSpecificOptions.desktop.Exec != null) {
106+
if (targetSpecificOptions.desktop?.entry?.Exec) {
107107
throw new Error("Please specify executable name as linux.executableName instead of linux.desktop.Exec")
108108
}
109109

@@ -140,7 +140,7 @@ export class LinuxTargetHelper {
140140
// https://github.com/electron/electron/blob/2-0-x/atom/browser/native_window_views.cc#L226
141141
StartupWMClass: appInfo.productName,
142142
...extra,
143-
...targetSpecificOptions.desktop,
143+
...(targetSpecificOptions.desktop?.entry ?? {}),
144144
}
145145

146146
const description = this.getDescription(targetSpecificOptions)
@@ -194,6 +194,17 @@ export class LinuxTargetHelper {
194194
data += `\n${name}=${desktopMeta[name]}`
195195
}
196196
data += "\n"
197+
const desktopActions = targetSpecificOptions.desktop?.desktopActions ?? {}
198+
for (const [actionName, config] of Object.entries(desktopActions)) {
199+
if (!Object.keys(config ?? {}).length) {
200+
continue
201+
}
202+
data += `\n[Desktop Action ${actionName}]`
203+
for (const [key, value] of Object.entries(config ?? {})) {
204+
data += `\n${key}=${value}`
205+
}
206+
data += "\n"
207+
}
197208
return Promise.resolve(data)
198209
}
199210
}

test/snapshots/linux/linuxPackagerTest.js.snap

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@ StartupWMClass=Test App ßW
1111
X-Foo=bar
1212
Comment=Test Application (test quite “ #378)
1313
Categories=Development;
14+
15+
[Desktop Action Gallery]
16+
Exec=fooview --gallery
17+
Name=Browse Gallery
18+
19+
[Desktop Action Create]
20+
Exec=fooview --create-new
21+
Name=Create a new Foo!
22+
Icon=fooview-new
1423
"
1524
`;
1625

test/src/ExtraBuildResourcesTest.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,9 +181,9 @@ test.ifNotWindows(
181181
app({
182182
targets: linuxDirTarget,
183183
config: {
184-
electronDist: (_context) => {
184+
electronDist: _context => {
185185
return Promise.resolve(getElectronCacheDir())
186-
}
186+
},
187187
},
188188
})
189189
)

test/src/ignoreTest.ts

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -92,16 +92,14 @@ test.ifNotCiMac(
9292
return Promise.all([
9393
modifyPackageJson(projectDir, data => {
9494
data.devDependencies = {
95-
"semver": "6.3.1",
95+
semver: "6.3.1",
9696
...data.devDependencies,
9797
}
9898
}),
9999
])
100100
},
101101
packed: context => {
102-
return Promise.all([
103-
assertThat(path.join(context.getResources(Platform.LINUX), "app", "node_modules", "semver")).doesNotExist(),
104-
])
102+
return Promise.all([assertThat(path.join(context.getResources(Platform.LINUX), "app", "node_modules", "semver")).doesNotExist()])
105103
},
106104
}
107105
)
@@ -124,7 +122,7 @@ test.ifDevOrLinuxCi(
124122
modifyPackageJson(projectDir, data => {
125123
data.dependencies = {
126124
"electron-updater": "6.3.9",
127-
"semver":"6.3.1",
125+
semver: "6.3.1",
128126
...data.dependencies,
129127
}
130128
}),
@@ -137,9 +135,7 @@ test.ifDevOrLinuxCi(
137135
assertThat(path.join(context.getResources(Platform.LINUX, archFromString(process.arch)), "app", "node_modules", "electron-updater", "node_modules")).isDirectory(),
138136
assertThat(path.join(context.getResources(Platform.LINUX, archFromString(process.arch)), "app", "others", "node_modules")).doesNotExist(),
139137
assertThat(path.join(context.getResources(Platform.LINUX, archFromString(process.arch)), "app", "submodule-1-test", "node_modules")).isDirectory(),
140-
assertThat(
141-
path.join(context.getResources(Platform.LINUX, archFromString(process.arch)), "app", "submodule-1-test", "node_modules", "package.json")
142-
).isFile(),
138+
assertThat(path.join(context.getResources(Platform.LINUX, archFromString(process.arch)), "app", "submodule-1-test", "node_modules", "package.json")).isFile(),
143139
])
144140
},
145141
}
@@ -190,9 +186,9 @@ test.ifDevOrLinuxCi(
190186
targets: Platform.LINUX.createTarget(DIR_TARGET),
191187
config: {
192188
asar: false,
193-
// should use **/ instead of */,
189+
// should use **/ instead of */,
194190
// we use the related path to match, so the relative path is submodule-1-test/node_modules
195-
// */ will not match submodule-1-test/node_modules
191+
// */ will not match submodule-1-test/node_modules
196192
files: ["**/*", "**/submodule-1-test/node_modules/**"],
197193
},
198194
},
@@ -211,9 +207,7 @@ test.ifDevOrLinuxCi(
211207
packed: context => {
212208
return Promise.all([
213209
assertThat(path.join(context.getResources(Platform.LINUX, archFromString(process.arch)), "app", "submodule-1-test", "node_modules")).isDirectory(),
214-
assertThat(
215-
path.join(context.getResources(Platform.LINUX, archFromString(process.arch)), "app", "submodule-1-test", "node_modules", "package.json")
216-
).isFile(),
210+
assertThat(path.join(context.getResources(Platform.LINUX, archFromString(process.arch)), "app", "submodule-1-test", "node_modules", "package.json")).isFile(),
217211
assertThat(path.join(context.getResources(Platform.LINUX, archFromString(process.arch)), "app", "submodule-2-test", "node_modules")).doesNotExist(),
218212
])
219213
},
@@ -243,9 +237,7 @@ test.ifDevOrLinuxCi(
243237
])
244238
},
245239
packed: context => {
246-
return Promise.all([
247-
assertThat(path.join(context.getResources(Platform.LINUX, archFromString(process.arch)), "app", "submodule-1-test", "node_modules")).doesNotExist(),
248-
])
240+
return Promise.all([assertThat(path.join(context.getResources(Platform.LINUX, archFromString(process.arch)), "app", "submodule-1-test", "node_modules")).doesNotExist()])
249241
},
250242
}
251243
)
@@ -273,10 +265,8 @@ test.ifDevOrLinuxCi(
273265
])
274266
},
275267
packed: context => {
276-
return Promise.all([
277-
assertThat(path.join(context.getResources(Platform.LINUX, archFromString(process.arch)), "app", "submodule-1-test", "node_modules")).doesNotExist(),
278-
])
268+
return Promise.all([assertThat(path.join(context.getResources(Platform.LINUX, archFromString(process.arch)), "app", "submodule-1-test", "node_modules")).doesNotExist()])
279269
},
280270
}
281271
)
282-
)
272+
)

test/src/linux/linuxPackagerTest.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,25 @@ test.ifNotWindows.ifNotCiMac(
112112
config: {
113113
linux: {
114114
executableName: "Foo",
115+
// Example Spec: https://specifications.freedesktop.org/desktop-entry-spec/latest/example.html
115116
desktop: {
116-
"X-Foo": "bar",
117-
Terminal: "true",
117+
entry: {
118+
"X-Foo": "bar",
119+
Terminal: "true",
120+
},
121+
desktopActions: {
122+
Gallery: {
123+
Exec: "fooview --gallery",
124+
Name: "Browse Gallery",
125+
},
126+
Create: {
127+
Exec: "fooview --create-new",
128+
Name: "Create a new Foo!",
129+
Icon: "fooview-new",
130+
},
131+
EmptyEntry: {},
132+
NullEntry: null,
133+
},
118134
},
119135
},
120136
appImage: {
@@ -293,7 +309,9 @@ test.ifNotWindows(
293309
config: {
294310
linux: {
295311
desktop: {
296-
Exec: "foo",
312+
entry: {
313+
Exec: "foo",
314+
},
297315
},
298316
},
299317
},

typedoc.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ module.exports = {
1616
excludePrivate: true,
1717
excludeProtected: true,
1818
excludeNotDocumented: false,
19-
includeVersion: false,
19+
includeVersion: true,
2020
hideGroupHeadings: true,
2121
hidePageTitle: true,
2222
disableSources: true,

0 commit comments

Comments
 (0)