A shepherd for your Appimages.
0

Configure Feed

Select the types of activity you want to include in your feed.

basic support for non-desktop apps

Aly Raffauf (Jun 18, 2026, 12:22 AM EDT) 615adb08 8def2de0

+32 -11
+2
internal/appherder/desktop.go
··· 256 256 257 257 if execCmd, ok := desktop.get("Exec", desktopEntrySection); ok { 258 258 desktop.set("Exec", patchExecCommand(execCmd, appimage), desktopEntrySection) 259 + } else { 260 + desktop.set("Exec", appimage, desktopEntrySection) 259 261 } 260 262 261 263 for _, section := range desktop.sections {
+14
internal/appherder/desktop_test.go
··· 137 137 } 138 138 } 139 139 140 + func TestPatchDesktopFileSetsExecWhenMissing(t *testing.T) { 141 + a := App{homeDir: func() (string, error) { return "/home/test", nil }} 142 + desktop := parseDesktopFile([]byte( 143 + "[Desktop Entry]\nType=Application\nName=Foo\nTerminal=true\n", 144 + )) 145 + if err := a.patchDesktopFile(desktop, "foo", false); err != nil { 146 + t.Fatal(err) 147 + } 148 + assertDesktopValue(t, desktop, desktopEntrySection, "Terminal", "true") 149 + assertDesktopValue(t, desktop, desktopEntrySection, "TryExec", "/home/test/AppImages/foo.appimage") 150 + assertDesktopValue(t, desktop, desktopEntrySection, "Exec", "/home/test/AppImages/foo.appimage") 151 + assertDesktopValue(t, desktop, desktopEntrySection, desktopOwnerKey, "true") 152 + } 153 + 140 154 func assertDesktopValue(t *testing.T, desktop *desktopFile, section string, key string, want string) { 141 155 t.Helper() 142 156
+16 -11
internal/appherder/install.go
··· 35 35 icon := resolveIcon(fsys) 36 36 appName = deriveAppName(desktop, desktopName, appimage) 37 37 38 + // No desktop file inside the AppImage: synthesize a terminal launcher so 39 + // CLI apps still get a menu entry and are tracked by managedApps. 40 + if desktop == nil { 41 + desktop = parseDesktopFile([]byte(fmt.Sprintf( 42 + "[Desktop Entry]\nType=Application\nName=%s\nTerminal=true\n", 43 + appNameFromPath(appimage), 44 + ))) 45 + } 46 + 38 47 // Patch in memory before any filesystem writes so a failure here installs nothing. 39 - if desktop != nil { 40 - if err := a.patchDesktopFile(desktop, appName, icon != ""); err != nil { 41 - return "", err 42 - } 48 + if err := a.patchDesktopFile(desktop, appName, icon != ""); err != nil { 49 + return "", err 43 50 } 44 51 45 52 // Roll back written files on a later failure rather than leaving a half-installed app. ··· 59 66 installed = append(installed, dest) 60 67 } 61 68 62 - if desktop != nil { 63 - dest, err := a.installDesktopFile(desktop, appName) 64 - if err != nil { 65 - rollback() 66 - return "", err 67 - } 68 - installed = append(installed, dest) 69 + dest, err := a.installDesktopFile(desktop, appName) 70 + if err != nil { 71 + rollback() 72 + return "", err 69 73 } 74 + installed = append(installed, dest) 70 75 71 76 // Materialize the AppImage last: when the source is already in ~/AppImages 72 77 // it gets moved, so an earlier failure must not roll back over the user's