One of the challenges you'll face when deploying an application in Seaside, especially if you're running headless on a Linux box somewhere, is how to upgrade your production application to the latest version of your code. For the longest time, I've done this the easy way, upgrade the production image off-line, upload it, and restart the services. It's simple and straight forward, but it blows away all active sessions and kicks off any users currently online. There are other more interesting ways.

Some people use VNC to connect to their production image, fire up Monticello, and hot upgrade the code. Personally, I've never been able to get a stable version of VNC going, there are always issues, the latest being that the client is messed up by the nice Squeak UI Enhancements hiding the menus. Too much hassle, I gave up, I don't trust VNC at all.

There's a simple web based method within Seaside under /tools/versionuploader, you can use the web version of Monticello to load packages into the running image and then save the image from there. It's simple and effective, if not a bit clunky, but it works great if you're willing to expose that to the internet wrapped with just basic http authentication. I've been using this, but I thought I could have a little fun hacking up something a little more interesting.

So what are we talking about here, just loading a few Monticello packages and saving the image, how hard could that be to automate? Turns out, not so hard if I use the Installer that comes in Damien's dev image (that my image is based upon). I decide to make my images upgrade themselves from a staging directory where I can just secure copy or ftp up the new versions plus an empty trigger file named "load" to kick the process off. I've been playing with it a few days now, seems to work pretty well. Here's what it takes to make a self upgrading image (assuming you use Monticello to package all your code, as I do).

It's just two methods really, one to load all the packages deleting them after each one is installed, assuming the load file exists...

loadChanges | dir | dir := (FileDirectory default directoryNamed: 'load-package') assureExistence. dir fileNames select: [ : ea | ea endsWith: '.mcz' ] thenDo: [ : fileName | ((dir fileExists: fileName) and: [ dir fileExists: #load ]) ifTrue: [Transcript show: (fileName , ': '). Installer installFile: (dir fullNameFor: fileName). Transcript cr. dir deleteFileNamed: fileName ] ]. (dir fileExists: #load) ifTrue: [ dir deleteFileNamed: #load. SmalltalkImage current saveSession]

And a second to kick off a background process that runs continually, say every few seconds, that kicks off #loadChanges.

watchForChanges [[ (Delay forSeconds: 5) wait. [ self loadChanges ] on: Error do: [ : error | Transcript show: error ] ] repeat ] forkAt: Processor userBackgroundPriority + 1 named: 'code loader'

Then I call watchForChanges in a class side #initialize somewhere on a utility class so the background process is kicked off automatically in any image I load my code into. Seems pretty slick so far, just upload the new packages, upload an empty file named load, the image upgrades itself, deletes all the files in the staging directory, saves itself, and chugs along like nothing happened at all. No session are lost, no users booted, if anything they might notice a slight pause for a bigger package loading.

This all of course assumes your production image is basically read only, i.e. has no code changes in it, as mine are. Enjoy!