helloSystem / launch

Command line tool to launch applications, will search for .app bundles and .AppDir directories in various directories, and will show launch errors in the GUI
BSD 2-Clause "Simplified" License
16 stars 6 forks source link

Adds support for .AppImage and implements parsing for .desktop #10

Open xaoseric opened 3 years ago

xaoseric commented 3 years ago

Adds support for .AppImage files and parses .desktop files with QSettings

xaoseric commented 3 years ago

134d3c379dae24472ad50ebe41b74921 Verified that parsing of .desktop files works as well.

xaoseric commented 3 years ago

46cf4dae5fafb463feb82507caa98be8 And one showing a Discord appimage working properly

probonopd commented 3 years ago

Thanks @xaoseric. Shouldn't we also parse the Name= key and make it possible to e.g., launch Discord even though the file name is Discord-3.1.0alpha3.git-hf876-x86_64.AppImage? No one wants to type Discord-3.1.0alpha3.git-hf876-x86_64. Then the question is, if there is Discord-3.1.0alpha3.git-hf876-x86_64.AppImage and Discord-3.2.0beta4_x86_64.AppImage, which one should be started by launch Discord? The one with the greater version, as in the (optional) X-AppImage-Version key in the .desktop file.

xaoseric commented 3 years ago

Ideally the Exec= which we parse in .desktop files would point to the correct executable. Looking for Name= and X-AppImage-Version= in .desktop could be a good option if it has a "-" in it, the goal was to get it working with Discord.AppImage for the moment so we can go back and add to it later.

The best solution would be a multi platform app store that could download .AppImage, .app and .AppDir files automatically and keep them updated in /Applications, /System and /Library.

One small bug I did find is that if we do launch PyCharm CE it says that it can't find PyCharm. We need to do launch "PyCharm CE" for it to find that .app directory.

probonopd commented 3 years ago

Ideally the Exec= which we parse in .desktop files would point to the correct executable.

This is not how AppImage works. What do you mean by "the correct executable"? The "correct executable" is the AppImage itself.

"TODO: Parse it for Exec="

Why do you want to do that? This key has no useful information for how the AppImage should be launched. Only the path to the AppImage is needed for that.

probonopd commented 3 years ago

if (file.fileName() == firstArg + ".desktop")

I think this is not what we should be checking for. Instead, we should be checking whether Name= matches.

Example:

launch Discord

should check if we have Name=Discord in the desktop file. If yes, then it is a candidate.

But to check this, we would need to have a way to peek inside the AppImage to read the desktiop file, wouldn't we?

xaoseric commented 3 years ago

"TODO: Parse it for Exec="

Why do you want to do that? This key has no useful information for how the AppImage should be launched. Only the path to the AppImage is needed for that.

Exec= points to the executble which allows none .app, .AppDir and .AppImage files to work ex: for Firefox it points to /usr/local/bin/firefox and it was what was in the todo notes before since we already check for the .AppImage extension before checking if a .desktop is a candidate.

if (file.fileName() == firstArg + ".desktop")

I think this is not what we should be checking for. Instead, we should be checking whether Name= matches.

Example:

launch Discord

should check if we have Name=Discord in the desktop file. If yes, then it is a candidate.

But to check this, we would need to have a way to peek inside the AppImage to read the desktiop file, wouldn't we?

Yes, we should also check that Name= also matches if the .desktop matches which at this point in parsing .desktop files, we are just parsing those directly so no need to look inside of any file.

As far as looking inside the AppImage, we could use libappimage to do that or we implement our own way of doing that which I think would probably be further down the line once we get the core functionality of it working we can go back and add in additional features like that.

https://docs.appimage.org/api/libappimage/

As it is on Linux, as long as it is executable, we don’t have to do anything on our end unless we want to check the .desktop file in the .AppImage.

For FreeBSD however, that is another matter: https://forums.freebsd.org/threads/appimage-support.75097/

probonopd commented 3 years ago

Exec= points to the executble which allows none .app, .AppDir and .AppImage files to work

I think you don't understand how AppImages work. To launch an AppImage, you need to use the path to the AppImage, whereas the Exec= key contains the name and arguments of the executable that in turn get used inside the AppImage (if the AppRun in that particular AppImage uses this information at all).

So, to launch an AppImage from the outside world, all you need to know is the path to the AppImage.

My objective is to do away with .desktop files altogether.

Shouldn't we also parse the Name= key and make it possible to e.g., launch Discord even though the file name is Discord-3.1.0alpha3.git-hf876-x86_64.AppImage? No one wants to type Discord-3.1.0alpha3.git-hf876-x86_64.

So thinking about it again maybe we could do something much simpler which would cover at least >80% of cases:

  1. Look for files with the .AppImage suffix
  2. If there are at least 2 - characters in the filename, remove everything behind the second-last - character from the filename
  3. there is only one - character in the filename, remove everything behind the -
  4. Replace _ with
  5. Compare the result against what the user wants to launch to determine whether we have a candidate
  6. If there are at least 2 - characters in the filename, assume that what is between the first and second - is the version; this will be needed to decide amongst several candidates (to pick the latest version)

Result:

launch Discord would work with, e.g.,

But it would fail on

launch "Discord Beta" would work with, e.g.,

But it would fail on

xaoseric commented 3 years ago

I think you don't understand how AppImages work. To launch an AppImage, you need to use the path to the AppImage, whereas the Exec= key contains the name and arguments of the executable that in turn get used inside the AppImage (if the AppRun in that particular AppImage uses this information at all).

A .AppImage is an executable on Linux. Quoting the faq from the docs. https://docs.appimage.org/user-guide/faq.html#question-how-do-i-run-an-appimage

How do I run an AppImage? Make it executable and double-click it.

So what we would need to do is check if we are running on BSD or Linux, if it is running on a BSD based system we can than implement any compatibility that needs to be done to make it work.

According the AppImage spec repo, there is an AppRun contained in the root of the filesystem image, supporting type 2 onward would make the most sense.

https://github.com/AppImage/AppImageSpec/blob/master/draft.md#type-2-image-format

So thinking about it again maybe we could do something much simpler which would cover at least >80% of cases:

  1. Look for files with the .AppImage suffix
  2. If there are at least 2 - characters in the filename, remove everything behind the second-last - character from the filename
  3. there is only one - character in the filename, remove everything behind the -
  4. Replace _ with
  5. Compare the result against what the user wants to launch to determine whether we have a candidate
  6. If there are at least 2 - characters in the filename, assume that what is between the first and second - is the version; this will be needed to decide amongst several candidates (to pick the latest version)

Yes, that is a good idea, that can be a feature enhancement further on down the line now that we know we have something that is working.

xaoseric commented 3 years ago

Opened an issue referencing the improvements for the AppImage handling so we can get this merged in and discuss how we want to improve it in that issue.

probonopd commented 3 years ago

So what we would need to do is check if we are running on BSD or Linux, if it is running on a BSD based system we can than implement any compatibility that needs to be done to make it work.

Yes, that is being discussed at https://github.com/AppImage/AppImageKit/issues/98. But that whole topic is more complex. Don't worry about Linux AppImages on FreeBSD yet. Work is underway.

Let's make this issue here only about finding and launching the correct AppImage.

So thinking about it again maybe we could do something much simpler which would cover at least >80% of cases: (...)

Yes, that is a good idea, that can be a feature enhancement further on down the line

Do you think you can make it part of this PR?

xaoseric commented 3 years ago

Do you think you can make it part of this PR?

Yes, certainly will try.

xaoseric commented 3 years ago

@probonopd the PR now supports the changes as requested.

probonopd commented 3 years ago

Thank you very much. Looks promising. However I am running into a segfault. I created three files with this content and made them executable:

#!/bin/sh

echo "This works" > /dev/stderr
exit 1

Then ran:

% ./launch Test
# Found "/home/user/Applications/Test-3.0.AppImage"
# Found "/home/user/Applications/Test.AppImage"
# Found "/home/user/Applications/Test-2.0.AppImage"
(...)
Took 16 milliseconds to find candidates via the filesystem
Candidates: (QFileInfo(/home/user/Applications/Test-3.0.AppImage), QFileInfo(/home/user/Applications/Test.AppImage), QFileInfo(/home/user/Applications/Test-2.0.AppImage))
0
zsh: segmentation fault  ./launch Test

Possibly it doesn't know what to do when some candidates have a version and some don't. In those situations, it should use the one with the highest version number, in this example ~/home/user/Applications/Test-3.0.AppImage.

xaoseric commented 3 years ago

Awesome, so it is getting closer to being on the right track then, once we get it working, the same concept should work for .app and .AppDir files. You could probably merge the stuff into a develop branch so that way the changes don't affect the stable main branch, and once there is enough changes to warrant a new release, we can merge into the master. Next thing we probably will want to do is create some type of database for keeping track of the available apps so doing say "launch --scan" would scan the system for available apps and add them to a database.

probonopd commented 3 years ago
% ./launch Test
# Descending into "/Applications/Developer"
# Descending into "/Applications/Video"
# Descending into "/Applications/Autostart"
# Descending into "/Applications/Preferences"
# Descending into "/Applications/Graphics"
# Descending into "/Applications/Games"
# Descending into "/Applications/Utilities"
# Descending into "/Applications/Audio"
# Descending into "/Applications/Developer Preview"
# Descending into "/Applications/3D Printing"
Took 17 milliseconds to find candidates via the filesystem
Candidates: ()

% LANG=C ls -lh ~/Applications/Test*
-rwxr-xr-x  1 user  user    50B Mar 15 19:54 /home/user/Applications/Test-2.0.AppImage
-rwxr-xr-x  1 user  user    50B Mar 15 19:54 /home/user/Applications/Test-3.0.AppImage
-rwxr-xr-x  1 user  user    50B Mar 15 19:54 /home/user/Applications/Test.AppImage

% cat /home/user/Applications/Test.AppImage
#!/bin/sh

echo "This works" > /dev/stderr
exit 1

Why does it not find them?

xaoseric commented 3 years ago

@probonopd reverted back to old if statement, should be fixed now.

probonopd commented 3 years ago

Hello @xaoseric, it now identifies the candidates correctly but segfaults while trying to decide which one to use:

FreeBSD% ./launch Test
# Found "/home/user/Applications/Test-2.0.AppImage"
# Found "/home/user/Applications/Test.AppImage"
# Found "/home/user/Applications/Test-3.0.AppImage"
# Descending into "/Applications/Video"
# Descending into "/Applications/Developer"
# Descending into "/Applications/Autostart"
# Descending into "/Applications/3D Printing"
# Descending into "/Applications/Utilities"
# Descending into "/Applications/Audio"
# Descending into "/Applications/Office"
# Descending into "/Applications/Developer Preview"
# Descending into "/Applications/Preferences"
# Descending into "/Applications/Graphics"
Took 13 milliseconds to find candidates via the filesystem
Candidates: (QFileInfo(/home/user/Applications/Test-2.0.AppImage), QFileInfo(/home/user/Applications/Test.AppImage), QFileInfo(/home/user/Applications/Test-3.0.AppImage))
QFileInfo(/home/user/Applications/Test-2.0.AppImage)
0
zsh: segmentation fault  ./launch Test

Note that in my case /home/user/Applications/Test-2.0.AppImage is not really an AppImage, but I doubt it would make any difference?

FreeBSD% file /home/user/Applications/Test-2.0.AppImage
/home/user/Applications/Test-2.0.AppImage: POSIX shell script, ASCII text executable

The expected behavior would be that it recognizes that 3.0 is the highest version number among the three candidates and hence selects that candidate.

xaoseric commented 3 years ago

Odd, it correctly identifies the new version on my linux setup, could it be a difference between BSD and Linux in the way that Qt handles things?

probonopd commented 3 years ago

Possible. Any way you could download a helloSystem ISO and test it there?

xaoseric commented 3 years ago

I'll be setting up a test system with helloSystem this weekend.

probonopd commented 3 years ago

Hello @xaoseric how did your tests go?