Welcome to HelloEngine

Welcome to HelloEngine, a minimal, but functional, Update Engine program. HelloEngine.m has some comments about how Update Engine works. This file has instructions on how to run the sample, and to get your own Stuff into Update Engine's pipeline.

Here's how to run things:

You'll see a lot of output. This is Update Engine letting you know what's going on, and can be useful when debugging things. It also looks cool.

The very last line of the output will look something like:

2008-09-12 11:51:01.933 HelloEngine[7055/0x0b0e] [lvl=2] main Update Succeeded

The last word from HelloEngine will be "Succeeded" or "Failed". If it Succeeded, try running /tmp/HiEngine.sh

Getting HelloEngine to run your own stuff

There are a couple of steps to get HelloEngine to run your stuff:

.engine_install

First you'll need to edit the install script, which you can find at Samples/HelloEngine/TestAppDMGSource/.engine_install. This script is what does the heavy lifting of updating your product.

.engine_install is required for Update Engine to function properly. You can also create .engine_preinstall and .engine_postinstall scripts to do preflight and postflight work. Outside of these scripts, Update Engine doesn't care what you have on the disk image.

The standard-out of the preinstall script gets added to the environment of the install and postinstall scripts under the environment variable KS_PREINSTALL_OUT. Likewise, the standard-out of the install script gets added to the environment of the postinstall script under the environment variable KS_INSTALL_OUT. This way you can pass information from one script phase to the next.

The scripts can be written in any language you want, but you'll want to stick to the languages (and language versions) that exist on your user's machines - they probably don't want to download and install the Object Fortran++ Scripting Bridge so that you can update your software. Likewise, if you're using Python, don't use any Python 2.5 features if you intend to update users on Tiger, who only have python 2.3.

.engine_install and the others don't even have to be a script, they can be compiled C programs if that makes sense for what you need to do.

The first argument of the script is the directory to the mount point of the disk image that Update Engine downloads. Be aware that this path can contain spaces, so do whatever string-space-quoting hygiene is necessary for your language of choice.

Inside of the script, you're welcome to do whatever you want, assuming it can be done as the user ID that the HelloEngine tool is run as. If you run it as root, then you can do most anything, please don't delete the user's /Application directory.

Build your Disk Image

Once you've written your script, you'll need to build your disk image. The easy way to do this is to drop all your stuff into a directory and use Disk Utility to create the disk image. The command to do so is hidden under File > New > New Disk Image From Folder. Choose the TestAppDMGSource folder, or whatever folder you're using for your product update, and make sure you're making a compressed, unencrypted disk image.

Here's a command for doing the same thing that's easier to put into an Xcode project:

hdiutil create -ov -imagekey zlib-level=9 -fs HFS+ -format UDZO -scrub
-srcfolder $DIR $DISKIMAGENAME

In a real-world application, this can (and should) be the disk image that you give your users. This saves you from having to build, test, and deploy two different payloads for your product. The reason the scripts have the leading dot is so that they won't interfere with the Finder's presentation of the disk image when it's mounted by the user.

Caculate Hash and File Size

Once you have the disk image, you'll need to calculate a checksum, and put the checksum and size into the ServerResponse.plist file.

First, calculate the checksum:

$ openssl sha1 -binary TestApp.dmg | openssl base64
This will output something like
BnKj+Bpy4KspQ4qzlkRn6EIaJ4g=
After you figure out how to pronounce that, you'll need to figure out how big the file is:
$ ls -l TestApp.dmg
-rw-r--r--   1 bork bork  50405 Sep 11 18:29 TestApp.dmg
Edit your ServerResponse.plist Edit the ServerResponse.plist and plug the values into the Size and Hash settings:

...
  <array>
    <dict>
      <key>ProductID</key>
      <string>com.google.HelloEngineTest</string>
      <key>Predicate</key>
      <string>Ticket.version != '2.0'</string>
      <key>Codebase</key>
      <string>file:///tmp/TestApp.dmg</string>

      <key>Size</key>
      <string>50405</string>
      <key>Hash</key>
      <string>BnKj+Bpy4KspQ4qzlkRn6EIaJ4g=</string>

    </dict>
  </array>
...

In a real development environment, you'll probably want an Xcode script build phase to do this automatically for you.

Now you can use your ServerResponse.plist and your TestApp.dmg with HelloEngine to get your stuff run.

What can go wrong

There are some moving pieces involved, and some things that might go wrong as you get things working. Here are some common ones: These result in ServerResponse.plist having a size and/or hash that's out of sync with the TestApp.dmg file. Update Engine will reject any paylod that doesn't match the size and hash. Make sure that your .engine_install file has the executable bit, set with chmod +x .engine_install The current working directory for your script is undefined, and will probably be the directory where you are running the HelloEngine command from. This can confuse your testing if you run HelloEngine with your current working directory being that of your TestAppDMGSource directory. The script will work fine when you run the install script by hand, but will fail when Update Engine is doing its thing.

Make sure you get the path to your mounted disk from the first argument to the script, and make sure that you account for the the fact that it might contain spaces. Even if your disk image name doesn't contain spaces, the system might mount it at "/Volumes/Payload 1" if /Volumes/Payload is already in use.