clowd / Clowd.Squirrel

Quick and easy installer and automatic updates for cross-platform dotnet applications
426 stars 39 forks source link

Not able to find SquirrelAwareApp in subfolders #96

Closed vegardlarsen closed 2 years ago

vegardlarsen commented 2 years ago

After scratching my head for a bit, reviewing #81, #2 and #21, I believe the intention is for this library to be able to detect SquirrelAwareApps in any subfolder:

You can place your app files into any lib directory. lib/blah is valid, and so is lib/framework.

However, the code only looks for files in one directory.

Specifically, DirectoryInfo.EnumerateFiles does not recurse into subdirectories unless you specify it:

            di.EnumerateFiles("*.*", new EnumerationOptions() { RecurseSubdirectories = true })
                .Where(x => Utility.FileHasExtension(x.Name, ".exe"))
                .Select(x => x.FullName)
                .Where(x => (GetSquirrelAwareVersion(x) ?? -1) >= minimumVersion)
                .ToList();

Or, perhaps simpler:

            di.EnumerateFiles("*.exe", new EnumerationOptions() { RecurseSubdirectories = true })
                .Select(x => x.FullName)
                .Where(x => (GetSquirrelAwareVersion(x) ?? -1) >= minimumVersion)
                .ToList();

I will be attaching a PR to this shortly, with the fix attached.

vegardlarsen commented 2 years ago

After reviewing this in Visual Studio as I was preparing the PR, I found it was probably more readable to use di.EnumerateFiles("*.exe", SearchOption.AllDirectories) than EnumerationOptions.

caesay commented 2 years ago

Hi there, and thanks very much for the PR! Unfortunately as it sits this change in #97 is not sufficient, as there are several other places in the codebase that expect the SquirrelAwareApp will be in the top level directory (not in a sub folder). Off the top of my head, Update.exe --processStart and the StubExecutable's will only look to launch the program in a top level folder.

I never intended to allow sub folders to contain SquirrelAwareApp's, can you describe your use-case in more detail?

Your quoted comment refers to the structure of the generated nupkg, which stores the application files (for legacy reasons) inside the nupkg within a lib\{frameworkName} folder, where frameworkName can be any top level folder name. There can only be one lib\{frameworkName} folder in the package, and it will be extracted to the final app destination during install and updates.

The above detail is only needed if you are creating the nupkg yourself, and passing it into the releasify command. If you are using the pack command, this nupkg will be generated for you by Squirrel - and you do not need to know or care about the structure of the nupkg.

vegardlarsen commented 2 years ago

Thank you for the feedback. I did suspect that I may not have a full understanding of how this works.

I have been using Squirrel.Windows to distribute MaxTo, but as I am migrating to .NET 6 it has some well-understood limitations that your fork seems to address well. The special case with MaxTo, is that it needs to have architecture-specific versions of itself installed (due to how global hooks work in Windows). I solve this by building and publishing MaxTo with different architectures, which gives me a folder structure I want to distribute that contains the .exe files inside architecture-specific folders: x64, x86 and perhaps arm64.

When the installer runs, I need to run at least one of these (likely the x86 one as it can run on any platform). Do you have any good ideas for how to package an app such as that?

caesay commented 2 years ago

Is MaxTo.exe the only binary in the package? In the current Squirrel architecture, I'd recommend creating 3 separate installers, each containing only one architecture. Squirrel does not have support for creating universal packages with multiple cpu architectures at the moment (although I have been thinking about we might achieve this).

Simply allowing Squirrel to search in sub directories would not actually help: because it would just find the first one alphabetically (arm64) and would always try to run this, which would fail on x64/x86.

One idea I've had is to allow a package folder structure something like the following:

So anything shared across the architectures will not be duplicated. At install/update time the installer would only extract the file that is appropriate for the current system, rename it to MaxTo.exe and discard the others.

This would probably be the simplest approach, but would require some development in any case.

vegardlarsen commented 2 years ago

Thank you for the ideas. I think your suggested solution won't work for me, because in my case I need to install and run multiple executables (two per architecture), not just the native one. So, if I am on an x64 system, it should run both x86 and x64 versions.

For now, I am trying to change my logic a bit, and putting the x86 binaries (which runs basically everywhere) in the root folder. Then after my program is running, I can start the other processes I need. I think that avoid the whole problem I ran into above.

Now, if I can just figure out how much I will need to restructure my code... :)

caesay commented 2 years ago

You could create a small binary, MaxToLauncher.exe which is SquirrelAware and in the root folder. Your MaxTo.exe binaries could remain in the architecture sub folders, and the launcher can run the correct exe's for the current platform via the Squirrel hook. Good luck!