Repository navigation
Add desktop entries for AppImage casks - #24343
Conversation
📝 Summary
Priority: Merge Risk:
|
There was a problem hiding this comment.
Actionable comments posted: 2
ℹ️ Review info
-
⚙️ Run configuration
-
- Source mode: Auto
- Repository: Unavailable
- Branch: Unavailable
- File: No configuration file from this repository applied
- Commit: Unavailable
- Layers: Organization UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
f09aa3c3-bcc3-4794-99fd-ab43b368eed3
📒 Files selected for processing: 5
-
Library/Homebrew/cask/artifact/appimage.rbLibrary/Homebrew/test/cask/artifact/appimage_spec.rbLibrary/Homebrew/test/support/fixtures/cask/Casks/with-appimage.rbLibrary/Homebrew/test/support/fixtures/cask/naked.AppImagedocs/Cask-Cookbook.md
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.
-
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

Minor · Preserve env options in rewritten Exec values. · appimage.rb:203-222
Library/Homebrew/cask/artifact/appimage.rb:203-222
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winPreserve
envoptions in rewrittenExecvalues.For
Exec=env --unset=FOO AppRun %U,desktop_entry_exectreats--unset=FOOas the old executable. The rewritten command drops the option and passesAppRunas an argument to the AppImage. This violates the requirement to retain environment settings and arguments.Parse leading
envoptions before selecting the original executable. Keep them beforeDESKTOPINTEGRATION=1.Suggested fix
words = exec.scan(/(?:"(?:\\.|[^"\\])*"|'[^']*'|[^\s"])+/).map(&:to_s) words.shift if words.first == "env" + env_options = words.take_while { |word| word.start_with?("-") } + words = words.drop(env_options.length) variables = words.take_while { |word| word.match?(/\A[A-Za-z_]\w*=/) } arguments = words.drop(variables.length + 1) variables.reject! { |variable| variable.start_with?("DESKTOPINTEGRATION=") } - [exec_environment, *variables, exec_path, *arguments].join(" ") + environment = if env_options.empty? + exec_environment + else + exec_environment.sub(/\Aenv\b/, "env #{env_options.join(" ")}") + end + [environment, *variables, exec_path, *arguments].join(" ")

Minor · Rebuild the MIME cache after removing desktop entries. · appimage.rb:163-172
Library/Homebrew/cask/artifact/appimage.rb:163-172
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winRebuild the MIME cache after removing desktop entries.
When an installed MIME-bearing entry is removed,
applications/mimeinfo.cachecan still list its desktop file. Applications that use this cache can offer the uninstalled AppImage as a MIME handler, then fail because its desktop entry no longer exists.Suggested fix
cask.staged_path.glob("*.desktop").select(&:symlink?).each do |desktop_entry_link| desktop_entry = desktop_entry_link.readlink if desktop_entry.exist? || desktop_entry.symlink? ohai "Removing desktop entry '#{desktop_entry}'" desktop_entry.unlink end desktop_entry_link.unlink end + + return unless (desktop_entry_dir/"mimeinfo.cache").exist? + return unless (update_desktop_database = which("update-desktop-database")) + + command.run(update_desktop_database, args: [desktop_entry_dir], print_stderr: false)

Review info
-
Run configuration-
- Source mode: Auto
- Repository: Unavailable
- Branch: Unavailable
- File: No configuration file from this repository applied
- Commit: Unavailable
- Layers: Organization UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
45cd8d6e-1053-42f9-8365-ef00b34023b5
Files selected for processing: 3-
Library/Homebrew/cask/artifact/appimage.rbLibrary/Homebrew/test/cask/artifact/appimage_spec.rbdocs/Cask-Cookbook.md
Files skipped from review as they are similar to previous changes: 2-
- docs/Cask-Cookbook.md
- Library/Homebrew/cask/artifact/appimage.rb
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.
-
MikeMcQuaid
left a comment
There was a problem hiding this comment.
Thanks for the PR! I like the idea but unfortunately the logic here is way too brittle as it's relying on a variety of hard-to-follow regex to actually parse and make changes to a file format.
Instead, look for libraries and/or tools, provided by ideally the XDG system and if not Homebrew, we can rely on here instead to actually parse and read these files.
Thanks, that's a fair point. We can use The complexity is really in two regexes
Second can be replaced entirely with Ruby's Fwiw, I did test with all 250 current appimages before submitting :) Edit: Maybe we could replace the first too if we pointed the desktop entry to a symlink from the caskroom composed only of safe characters. That's assuming the user didn't use a custom brew prefix. |
|
@syntactic-spice leaning on |
|
@MikeMcQuaid Please take a look. Unfortunately Added separate |
MikeMcQuaid
left a comment
There was a problem hiding this comment.
Much easier to follow now thanks! A few more requests:
- please inline any methods only used once
- try to move any more desktop-file-specific logic into desktop_file_parser
- in general let's see if you can slim the diff here a bit more just to the specific case(s) you specifically need for now
9074704 to
1c5d543
Compare
|
Thanks. Inlined methods and moved all desktop-file-specific logic to Edit: removed some overlapping tests |
- To make AppImage casks on Linux appear in the desktop application
menu, install each root-level embedded desktop entry into
`${HOMEBREW_XDG_DATA_HOME:-~/.local/share}/applications`.
- Keep the entry's filename so URL handlers, dock pins and notifications
that look up the app's desktop ID keep working.
- Add `Cask::DesktopFileParser` to parse and manipulate desktop files.
- Point every `Exec` at the AppImage through `env DESKTOPINTEGRATION=1`,
keeping its variables and arguments.
- Point `Icon` at the named icon copied into the versioned Caskroom,
falling back to `.DirIcon`.
- Track ownership with Caskroom symlinks, fail on an existing entry
unless `--force` or `--adopt`, and rewrite it on upgrade.
1c5d543 to
18960f3
Compare
There was a problem hiding this comment.
🔇 Additional comments: 2
Library/Homebrew/test/cask/artifact/appimage_spec.rb: 1
89-89: 🎯 Functional CorrectnessNo per-example
ENVcleanup is required.
Library/Homebrew/test/spec_helper.rbsnapshotsENVbefore each example and restores it in the example teardown. The assignments in this spec therefore do not affect later examples.Library/Homebrew/test/cask/desktop_file_parser_spec.rb-135-135: 1
135-135:Minor · Functional Correctness · Quick win
⚠️ Unverified finding
Verification ran but could not confirm this finding. It is shown for review, not as a verified issue.Assert that
TryExecis removed.The PR objective requires removal of
TryExec, but this assertion requiresreplace_programto retain it. The parser also rewrites the key rather than removing it. Update the parser and assert thatget("TryExec")returnsnil.

Review info
-
Run configuration-
- Source mode: Auto
- Repository: Unavailable
- Branch: Unavailable
- File: No configuration file from this repository applied
- Commit: Unavailable
- Layers: Organization UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
a745c787-8652-4bca-b7de-123bb3d52941
Files selected for processing: 2-
Library/Homebrew/test/cask/artifact/appimage_spec.rbLibrary/Homebrew/test/cask/desktop_file_parser_spec.rb
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.
-
Please also see this discussion.
Currently, AppImage casks (Linux) copy the executable, but do not extract their embedded desktop entries. That means the applications never appear in the launcher/menu.
${HOMEBREW_XDG_DATA_HOME:-~/.local/share}/applications.Execat the AppImage throughenv DESKTOPINTEGRATION=1, keeping its variables and arguments.Iconat the icon copied into the versioned Caskroom, falling back to.DirIcon.--forceor--adopt, and rewrite it on upgrade.There was an earlier attempt (#23674), where an objection was raised about writing to the user's home directory. But I think that's moot now, since after #23771 AppImages themselves are written to the home dir and fonts have always been written to the home dir on Linux too.
More details
--appimage-extract '*.desktop'and install every top-level desktop entry. A candidate counts only if it is a regular file of at most 64 KiB whose first non-blank, non-comment line is[Desktop Entry]. Some*.desktopfiles are not entries: opencode-desktop'sai.opencode.desktopis its 228 MB executable, and its entry isai.opencode.desktop.desktop.Exec: everyExecline, including[Desktop Action]groups, is rewritten to run the AppImage in~/Applications. The entry's own variables and arguments are kept andDESKTOPINTEGRATION=1is set, because someAppRunscripts integrate themselves into the desktop otherwise. For example:Exec=env GDK_BACKEND=x11 UBUNTU_MENUPROXY=0 audacity %FbecomesExec=env DESKTOPINTEGRATION=1 GDK_BACKEND=x11 UBUNTU_MENUPROXY=0 "<AppImage>" %FTryExecis also pointed to the AppImage.Icon=is extracted from the top of the image, following symlinks into the image. If it isn't found, we retry without a known extension, then falls back to.DirIcon. The icon is copied into the versioned Caskroom folder andIcon=points at it by absolute path.File ownership
--forceor--adoptis passed. If replacing an existing entry, that entry's ownership is transferred to the new cask by deleting any existing links to it from another cask (can be the casebrew benchmarkresults.brewcommands to reproduce the bug?brew lgtm(style, typechecking and tests) locally?Claude Opus 5.5 was used for the initial implementation and iterations after manual review. It was also used to gather empirical evidence from the current ~250 available casks about edge cases and common non-spec-compliant patterns. It was used to analyze
appimaged(reference implementation) andGear Lever(popular AppImage manager) code to check how they handle those patterns. And to write an app to let me manually test behavior of different desktop environments to make some decisions.