Opened 17 months ago

Last modified 10 months ago

#2063 assigned enhancement

DMG installer for OSX

Reported by: zab Owned by: meeh
Priority: minor Milestone: 0.9.35
Component: installer Version: 0.9.31
Keywords: usability, mac Cc:
Parent Tickets:

Description

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

Subtickets (add)

#2064: Investigate if fx:deploy task can preserve jar hierarchiestaskclosedpsi
#2065: Acquire a developer certificate from Appletaskopenzab
#2066: Analyze the wrappertaskclosedzab
#2067: Investigate if FX allows to install i2p as a servicetaskclosedzab
#2068: Verify in-network updates work with FX dmgtaskclosedpsi
#2069: Use a compressed DMGenhancementassignedmeeh

Change History (21)

comment:1 Changed 17 months ago by zab

Add a subticket #2064.

comment:2 Changed 17 months ago by zab

Add a subticket #2065.

comment:3 Changed 17 months ago by zab

Add a subticket #2066.

comment:4 Changed 17 months ago by zzz

  • Component changed from package/other to installer
  • Version set to 0.9.31

comment:5 Changed 17 months ago by zab

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 17 months ago by zab

Add a subticket #2067.

comment:7 Changed 17 months ago by zab

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 17 months 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 17 months ago by zab

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 17 months ago by zab

Add a subticket #2068.

comment:11 Changed 17 months ago by zab

Add a subticket #2069.

comment:12 Changed 17 months 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 17 months ago by zab

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 follow-up: Changed 17 months 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 17 months ago by zab

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 16 months 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 16 months 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 15 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 15 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 13 months ago by str4d

  • Keywords usability mac added
  • Status changed from new to open

comment:21 Changed 10 months ago by zzz

  • Milestone changed from eventually to 0.9.35
  • Owner set to meeh
  • Status changed from open to assigned
Note: See TracTickets for help on using tickets.