Opened 2 years ago

Closed 6 months ago

#2063 closed enhancement (fixed)

DMG installer for OSX

Reported by: Zlatin Balevsky Owned by: Meeh
Priority: minor Milestone: 0.9.35
Component: installer Version: 0.9.31
Keywords: usability, mac Cc:
Parent Tickets: Sensitive: no

Description

Parent ticket for tasks related to the building of a native-looking installer for OSX

Subtickets

#2064: Investigate if fx:deploy task can preserve jar hierarchiesclosedpsi
#2065: Acquire a developer certificate from Appleclosedzab
#2066: Analyze the wrapperclosedzab
#2067: Investigate if FX allows to install i2p as a serviceclosedzab
#2068: Verify in-network updates work with FX dmgclosedpsi
#2069: Use a compressed DMGclosedmeeh

Change History (22)

comment:1 Changed 2 years ago by Zlatin Balevsky

Add a subticket #2064.

comment:2 Changed 2 years ago by Zlatin Balevsky

Add a subticket #2065.

comment:3 Changed 2 years ago by Zlatin Balevsky

Add a subticket #2066.

comment:4 Changed 2 years ago by zzz

Component: package/otherinstaller
Version: 0.9.31

comment:5 Changed 2 years ago by Zlatin Balevsky

A general summary of where I think we are after a quick look at the different moving parts. We have two obvious options, an easy route (i.e. JavaFX) which will require us to make some compromises, or the difficult route (i.e. manual labor). There is also a "hybrid" option of using JavaFX with some "creativity", but I need to mull it over more before I can describe it.

The compromises I am referring to are related to :

  1. pack200 - don't think it's possible to mix fx with pack200.
  2. the wrapper - the wrapper comes with it's own executable whereas fx generates an executable.

I'm (optimistically) not mentioning in-network updates, pending results from psi's research on #2064

With the "manual labor" option we don't have to make any compromises, but the required effort is easily an order of magnitude larger.

comment:6 Changed 2 years ago by Zlatin Balevsky

Add a subticket #2067.

comment:7 Changed 2 years ago by Zlatin Balevsky

The hybrid option I'm considering is to add a new main(..) method just for OSX and point JavaFX to it. That main method would in turn call the wrapper binary.

There are benefits to this approach - we can perform any first-time-launch tasks there. One drawback I can think of is that I'm not sure how to make the wrapper use the same JRE that is used to launch the custom main method. This might be relevant only if we choose to bundle a JRE and there is already another JRE installed in the system.

comment:8 Changed 2 years ago by zzz

Unfortunately, I was rather slow to realize that with the wrapper, our entry point is not java at all, but a C executable, and that changes everything.

Or is it? Actually it's a shell script - i2prouter. Which sets JAVA_HOME, including the new official OSX way of calling /usr/libexec/java_home. That's how you tell the wrapper where Java is. But wait a second, that's for command line and service starting…

Or is it? On windows, the "restartable" shortcut we install on the desktop calls "\path\to\I2PSvc.exe -c wrapper.config" with a CWD of \path\to. And there's a JAVA_HOME setting in wrapper.config but it's commented out.

Having a 2nd JVM just for a main() to exec the wrapper is interesting but it sure isn't lightweight. Agreed it is flexible.

Oh, BTW, the million wrapper options are here: https://wrapper.tanukisoftware.com/doc/english/properties.html

None of the choices so far sound great.

comment:9 Changed 2 years ago by Zlatin Balevsky

As discovered by psi on IRC, it is possible to customize the Info.plist file with FX - which offers very powerful customization options, one of which is the entry point to the bundle. So technically we do not need a new main(..) method just for osx, we can point the bundle to a script. (This is actually even more powerful because we can use AppleScript which integrates very nicely with OSX)

The flip side of it being powerful is that there is a steep learning curve to both AppleScript and the Info.plist options. So either someone has to dig into that or we find a mac guru.

comment:10 Changed 2 years ago by Zlatin Balevsky

Add a subticket #2068.

comment:11 Changed 2 years ago by Zlatin Balevsky

Add a subticket #2069.

comment:12 Changed 2 years ago by zzz

Prompted by a conversation in IRC -

We rely on the following features of Izpack 4 for a cross-platform installer. While not all may be appropriate or possible in a "mac way" replacement (whether JavaFX, Izpack 5, or something else), it's important to list them and evaluate replacements against them:

  • Finds installed Java, does version check, notifies user and launches browser to java.net to download if not found
  • Language selection, Internationalized UI
  • Customizable with an itoopie icon
  • License display and click-through
  • User allowed to override install location (probably not needed for Mac)
  • Provide other installation options to user (service or not, desktop icons or not)
  • Progress feedback / error indications
  • Arbitrary hierarchy of installed files under the install dir
  • Installs both wrapper and non-wrapper startup scripts/icons
  • Variable substitution in scripts during install
  • Post-install shell script for further fixups
  • Post-install notice to user with further information about how to run and uninstall
  • Uninstaller (probably broken, probably not needed for Mac)
  • Also installable via command line with user interaction, or via script with all options set (rarely used)
  • Doesn't prohibit in-net updates
  • Buildable on linux as a part of the 'ant release' flow
  • Binaries and scripts used for building are small and checked into our source tree

To repeat, not all of the above can or should be achieved in a replacement, especially for a DMG install, but let's keep in mind where we are now.

comment:13 Changed 2 years ago by Zlatin Balevsky

So far, the DMG installer psi has built doesn’t have:

  • wrapper
  • in-network updates

To address the wrapper issue I see two options:

  • re-implement wrapper in AppleScript
  • hack the Tanuki binary to load the jvm from the bundle

A quick workaround for just the automatic restart bit of the wrapper is to add a shutdown hook in the router that launches a process that restarts the router.

To address the in-network update issue code changes will be required, but can be made to work with both torrent and url downloads.

comment:14 Changed 2 years ago by zzz

using the bundle JVM only requires setting JAVA_HOME in a property or wrapper.config, no 'hack' needed

comment:15 in reply to:  14 Changed 2 years ago by Zlatin Balevsky

Replying to zzz:

using the bundle JVM only requires setting JAVA_HOME in a property or wrapper.config, no 'hack' needed

I think that won't work because based on psi's testing the wrapper is looking for a $JAVA_HOME/bin/java executable which is not included in the bundle. But maybe we did the testing wrong.. worth trying on a virgin mac

comment:16 Changed 2 years ago by Meeh

I've put together a small list of useful links and projects related to this topic. https://gist.github.com/mikalv/824dac72ad77ae199979472ffb3915d9

comment:17 Changed 2 years ago by Meeh

However, just to find where the line goes between accepted and not. I'll post an proposal of how we can solve it, where I already know it's doable and has whats needed to implement it.

We could do a fancy DMG installer, with symlink to /Applications so the user can do the native drag'n-drop install of OSX apps.

However, on first launch it will start a native binary wrapper, which checks the /Library/Java/JavaVirtualMachines? location where system installations of JRE/JDKs has been installed the past 4-5 versions of OSX at least if not more. If it's found, it defaults to /Library/Java/Home? (which is a symlink to JAVA_HOME for the system selected JRE) - More or less just as /usr/libexec/java_home would do.

If a JDK/JRE is not found however, it will show a native dialog window with the Oracle EULA and ask the user to agree or not to the license, when not - we quit. When user accepts, first that user has to enter their sudo password as usual when using the built-in installer - after that, it trigger a download of the latest JRE from download.oracle.com in PKG format which is installed by the installer and the part that requires privilege escalation. It's done in headless mode at system level - and not in any way bundled with I2P.

Overrides like custom JRE locations and such is also possible and should be allowed/implemented support for - I just haven't spent much thoughts on . that part yet.

comment:18 Changed 23 months ago by Meeh

I've researched Sparkle and done some tests in Swift4/Obj-C (Like a n00b). But it seems to work quite fine. The catch is that we would need to keep our own modification of the SUBasicUpdateDriver.m core file for overriding proxy settings. It's the class the authors suggested to look into if the default API didn't fulfil our needs. https://github.com/sparkle-project/Sparkle/blob/master/Sparkle/SUBasicUpdateDriver.m

The Swift way for proxy:

let proxyPortTest = checkTcpPortForListen(port: 4444)
      let proxyPortStatus = proxyPortTest.0 ? "not in use" : "in use";
      NSLog("Checking port 4444 and it's %@ - Computer says: %@", proxyPortStatus, proxyPortTest.descr)
      
      sessionConfiguration.connectionProxyDictionary = [
        kCFNetworkProxiesHTTPEnable: true,
        kCFNetworkProxiesHTTPPort: 4444,
        kCFNetworkProxiesHTTPProxy: "127.0.0.1",
      ]
      
      var testGetRequest = URLRequest(url: i2pTestUrl!, cachePolicy: NSURLRequest.CachePolicy.reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 120.0)
      testGetRequest.httpMethod = "GET"
      let session = URLSession(configuration: sessionConfiguration)
      let task = session.dataTask(with: testGetRequest, completionHandler: { (data, response, error) in
        if let response = response {
          print(data)
          NSLog("Data response: %@", response)
        }
        if let error = error {
          print(error)
        }
      })
      task.resume()


comment:19 Changed 23 months ago by Meeh

So far my PoC has a trayicon, can open web console, can check if we listen on a port like the console or http proxy, and found the way to override proxy settings for http objects in Swift/Obj?-C. Next up is to build a release of Sparkle with our proxy modifications to SUBasicUpdateDriver.m.

comment:20 Changed 21 months ago by str4d

Keywords: usability mac added
Status: newopen

comment:21 Changed 18 months ago by zzz

Milestone: eventually0.9.35
Owner: set to Meeh
Status: openassigned

comment:22 Changed 6 months ago by Meeh

Resolution: fixed
Status: assignedclosed

The I2PLauncher now comes in form of DMG and/or ZIP.

Note: See TracTickets for help on using tickets.