libgdx / packr

Packages your JAR, assets and a JVM for distribution on Windows, Linux and Mac OS X
Apache License 2.0
2.56k stars 171 forks source link

Add jrePath option to config.json so self-updating apps can update the JRE #191

Closed petoncle closed 3 years ago

petoncle commented 3 years ago

Currently, the packr launcher expects the JRE to be in a folder called ./jre. See the hardcoded strings: https://github.com/libgdx/packr/blob/06d78f472e038fcc46342e8cc87d6b34e517729f/PackrLauncher/src/main/cpp/win32/packr_win32.cpp#L286 I think that being able to change the JRE folder would allow the user to make a self-updating app that updates both the .jar and the JRE (and config.json). For that to work we could add a jrePath field to config.json. Let's take an example. After the first installation (version 1 of the app, that I will call v1) the app folder looks like this:

Then, the app downloads a new version of itself (some files are downloaded and placed inside the app folder) and the app folder becomes:

To complete the self update, the app launches another app.exe instance and closes itself. The new instance of app.exe now runs app-v2.jar and proceeds to delete files (just as a cleaning step) so the folder becomes:

(Note: app.exe could probably be updated too by suffixing its file name with the version but I wanted to keep this initial example simple.)

Let me know if that use case makes sense or if I'm missing something! Is there another known technique for making self-updating apps with packr?

karlsabo commented 3 years ago

I always solve the file locking issue by having the updater be a stand alone application.

  1. App detects there's a new update and downloads the latest Updater.
  2. App launches Updater and exits.
  3. Updater installs the latest App.
  4. Updater launches updated App and exits.
petoncle commented 3 years ago

Interesting approach! I may have been unclear in my first message and I want to emphasize that my main concern is to update the Business app's JRE (that is what the solution described in my first message is trying to achieve). I would like to ask you a few questions to try to understand how your method solves that problem.

  1. I am assuming your Updater app is a Java application -- basically a .jar file -- and it needs a JRE to run. Is that assumption correct or do you actually have a tool that can generate a native Updater app that works on Windows, Linux and Mac?
  2. Do you ship the Updater app with its own JRE? That sounds a bit cumbersome but if this is how you do it, could you give more details about it?
  3. If you don't ship the Updater app with its own JRE, I guess you're running the Updater app with the Business app's JRE. But then the problem of updating the Business app's JRE is not solved since you can not update a running JRE? However, if this approach is used in combination with the solution described in my first message (add jrePath option to config.json), it makes it possible to update the Business app, the JRE and the native launcher (everything, basically!). What do you think?
karlsabo commented 3 years ago
  1. Yes, I used a Java application, but GraalVM and jpackage would work as native solutions.
  2. If the JRE needed to be updated then a new one gets downloaded and extracted as part of the App pulling the Updater.
  3. Yep, if you need to update the JRE you have to download it and use then when running the Updater.

project

flow

  1. App checks URL for newer version.
  2. App downloads Updater payload.
  3. App launches Updater per configuration instructions. E.g., reuse JRE or use from payload.
  4. App exits.
  5. Updater waits for App to exit, then extracts and copies all files from its payload.
  6. Updater launches App.
  7. Updater exits.
  8. App cleans up Updater payload.

Most apps launch an installer that uninstalls previous version and then does its thing, think Notepad++. Apps where updates are more critical, install update services which run a standalone updater program, think Firefox and Chrome. They install "Google Update Service" and "Mozilla Maintenance Service" so you don't get the UAC prompts every time they update.

petoncle commented 3 years ago

Thanks a lot for the detailed explanations @karlsabo! Like you said, we have two choices for the Updater app (assuming one of our goals is to update the JRE):

  1. Use GraalVM and jpackage to create native Updater apps. This way, the Updater app doesn't need a JRE to run.
  2. Have the business app download both the Updater app and a JRE, then run the Updater with that new JRE.

I'm more interested in solution 2. as 1. requires a custom build process just for the updater and I'd prefer to keep things simple😏!

For 2., we make the old business app download the Updater app with a JRE. But where should it put the new JRE? We can't put it in the business app folder as that would conflict with the (old) app's JRE. [We could put the new JRE in a temp folder, then run the Updater app with that, then make the Updater copy-and-replace the JRE from this temp folder to the business app's ./jre folder. But that sounds so complicated!] And I come back to my initial proposition: if we could choose the JRE folder name (e.g. ./jre for the old business app, and ./jre-v2 for the Updater app and the new business app) then that problem would be solved easily. And it would allow self-updating apps to update the JRE too, without any standalone updater app. Are there any drawbacks to letting the user choose the JRE folder name? This new jrePath option could even have a default value (jre) to ensure backward compatibility.

karlsabo commented 3 years ago

Thanks for the PR, I have no problem with allowing this as a configuration option.

As for your updater, I will caution you on this, imagine you have a lib directory like the test app, e.g.:

PackrAllTestApp-3.0.3-SNAPSHOT\lib>ls
PackrAllTestApp-3.0.3-SNAPSHOT.jar  log4j-core-2.13.1.jar        slf4j-api-1.7.30.jar
log4j-api-2.13.1.jar                log4j-slf4j-impl-2.13.1.jar

Back when I worked on an updater for Windows, all those JAR files are locked too, so you need to make sure all files get a version change, which can be a big headache. In Maven, SNAPSHOT versions don't change, but the JAR often does.

petoncle commented 3 years ago

Back when I worked on an updater for Windows, all those JAR files are locked too, so you need to make sure all files get a version change, which can be a big headache. In Maven, SNAPSHOT versions don't change, but the JAR often does.

You're right if you're shipping SNAPSHOT jars externally you're in trouble. I usually have a single fat jar (all dependencies are put inside this single .jar file) with no external jar dependencies so I don't think this can be a problem for me.

karlsabo commented 3 years ago

This enhancement is available in a snapshot release:

https://artifactory.nimblygames.com/artifactory/ng-public-snapshot/com/badlogicgames/packr/packr-all/3.1.0-SNAPSHOT/ packr-all-3.1.0-20210221.221700-10.jar 21-Feb-2021 22:17 3.47 MB

I'll release 4.0 early next month. Thanks @petoncle for PRs!