Helium Foot Software

Making your Mac more agile, more powerful and more fun

May 2008
Sun Mon Tue Wed Thu Fri Sat
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
About
Helium Foot Software develops MercuryMover: Don't let the mouse slow you down! Move and resize windows on your Mac with the keyboard.
Recent Entries
MercuryMover v1.1.2(05/11 22:57)
Hint, Hint(04/29 00:28)
Daring Did(04/17 00:28)
Ich Bin Ein Podcaster (04/08 00:38)
Helium Foot Site++ plus(03/27 23:17)
Daring Do(03/27 00:18)
Helium Foot Site++(03/25 00:21)
The Hook(03/16 23:49)
CAWUG-bunga(03/12 23:22)
Quintessence (03/12 23:14)
Recent Comments
Re: Ich Bin Ein Podcaster (Chad : 04/11 09:29)
Re: Helium Foot Site++ plus(keith alperin : 03/30 23:21)
Re: Helium Foot Site++ plus(Martin Pilkington : 03/28 11:02)
Re: The Hook(Jon Trainer : 03/17 15:43)
Re: The Hook(keith alperin : 03/17 10:10)
Re: The Hook(Jon Trainer : 03/17 07:58)
Re: CAWUG-bunga(keith alperin : 03/15 20:57)
Re: CAWUG-bunga(Chad : 03/13 22:36)
Re: Conversion-sation(Andy Brice : 03/07 12:01)
IMAPidle(Pierre : 12/14 07:51)
Recent Trackbacks
There are no trackbacks.
Categories
Helium Foot (12 items)
Software (1 items)
MercuryMover (23 items)
Blog (3 items)
MacSanta (3 items)
Marketing (9 items)
Philanthropy (0 items)
Archives
Blogroll

Syndicate this site (XML)

RSS/RDF 0.91

02 November
2007

Da Ploy

Where keith is feeling smug about his kickin' automated build and deploy process

It's a well known fact that we software developers are a lazy bunch. Part of what we like to do is eliminate future work for ourselves. A less well known fact, is that developers think that they're pretty smart. For the last few days i've been letting my lazy and smart streaks work together to automate my MercuryMover (The very soon to be released program that lets you move and resize windows on your Mac via the keyboard) build such that with one click i can:

  • build a specific distribution
  • build a specific version
  • create an installer package
  • zip the package
  • push it to my website
  • update the download link
  • update the appcast

Not only will this save me a lot of (very valuable) time as i frequently spin new builds in the run up to launch, but more importantly i'm certain that this procedure is going to save my posterior many times in the future. The build is designed to be fool proof (perfect for a developer like me) and prevent me from making dumb yet costly mistakes as i scramble to get a fix out.

I thought that i'd briefly describe each step and give the how and why in order to help out some other lazy people like me.

Build a Specific Distribution

I have two distributions: Pre-Release and Release. There is a corresponding xcode build configuration for each. Pre-Release builds expire 60 days after they are built (see this post from Daniel Jalkut for some background). To make that work, i have a build setting called EXPIREAFTERDAYS which is 60 in the Pre-Release configuration and 0 everywhere else. Whenever MercuryMover is invoked, it'll execute the following:

if (EXPIREAFTERDAYS > 0) {
        NSString* nowString =
        [NSString stringWithUTF8String:__DATE__];
        NSCalendarDate* nowDate =
                [NSCalendarDate dateWithNaturalLanguageString:nowString];
        NSCalendarDate* expireDate =
                [nowDate addTimeInterval:(60*60*24* EXPIREAFTERDAYS)];

        if ([expireDate earlierDate:[NSDate date]] == expireDate)
        {
                isExpired = YES;
        }
}

Now when i first tried this, it failed and there are a few (3 by my count) builds of MyWi (MercuryMover's gestational name) out there that will never expire. Any enterprising developers out there know why? That EXPIREAFTERDAYS is just a build setting. As such it doesn't mean anything to the class where i implemented the check. To hook that up, you need to edit the value "Preprocessor Macros" for all configurations to say this:

EXPIREAFTERDAYS=$(EXPIREAFTERDAYS)

As you can imagine, this is like adding a #define EXPIREAFTERDAYS to my classes. Without this kind of automation, i wouldn't try to have self expiring builds. There would be too great a chance that one would get deployed to my site, and there would N ticking time bombs out on the internets where N is the number of people that have purchased a license and are running an expiring build.

Build a Specific Version

I wanted my build and deploy script to take the version as a parameter so that i could simplify keeping the version in sync everywhere and also to enforce my self imposed source control policies. Before i developed this build, i just had an xcode target that would push a build to my site. More than once i would push the build (complete with version number) without bothering to tag the sources. That's all well and good while you're in beta, but after launch, i'll need to be able to determine the precise version in order to provide support.

This was trickier than i thought, but only due to path issues. The script is simple:

#!/bin/bash

#first export the tagged project from svn
rm -rf /tmp/MercuryMover
mkdir /tmp/MercuryMover
cd /tmp/MercuryMover
svn export file:///usr/local/svnroot/repos/MercuryMover/tags/$1/src/MercuryMover

#build
cd MercuryMover
xcodebuild clean
xcodebuild -target Deploy.Production -configuration Pre-Release APPLICATION_VERSION=$1 SRCROOT=`pwd` SOURCE_ROOT=`pwd`
echo "done"

the trickier stuff happens in a script that is run by the Deploy.Production target. Eagle eyed readers will notice that the configuration is hard coded. This was on purpose to make it really difficult to screw up.

Installer

This might be a controversial decision amongst the members of the Mac developer community, but i really can't do a drag and drop install. MercuryMover ships as a PreferencePane. Inside that PreferencePane is a program called MercuryMoverAgent.app . This agent is what listens for the hotkeys and does the window moving and resizing magic. If you currently have MercuryMover enabled, you can't even drag a new MercuryMover.prefPane to your PreferencePanes folder because MercuryMoverAgent is running. You can double click on your new version of MercuryMover.prefPane that you downloaded to your desktop and System Preferences will dutifully upgrade your existing install; however, it will not restart MercuryMoverAgent.app . Thus, in order to see the changes in the new version, you would have to manually disable and re-enable MercuryMover (which stops and restarts MercuryMoverAgent.app).

Enter Installer.app . With a pretty vanilla .pkg file i can use a preflight script to kill MercuryMoverAgent and a postflight script to start it again. Using PackageMaker, i was able to specify what goes into my .pkg via the gui which yeilds a .pmproj file. I can then use the command line packagemaker as part of my Deploy.Production run script phase:

/Developer/Tools/packagemaker -build -proj ${PACKAGEMAKER_PROJECT_FILE_PATH} -p ${PACKAGE_DESTINATION}

The only thing i couldn't figure out how to do with the command line packagemaker was set the .pkg's version number. This is an important value as Installer.app keeps receipts of the packages that you install and uses that version number to determine how to handle your .pkg. I ended up punting and getting my pyth on to update the Info.plist file inside MercuryMover.pkg/Contents/ . In a future rev, i may try to engineer a drag and drop install. When i go Leopard only, i can use FSEvent to determine when the PreferencePane changes, but for now this will have to suffice.

Zip

I went with a zip file instead of a disk image for two reasons. One, the consensus of the MacSB yahoo group seems to be that zip files are simpler for users. Two, the consensus of me is that zip files are simpler for the industrious developer. I can zip the installer with one line:

ditto -ck --sequesterRsrc --keepParent ${PACKAGE_DESTINATION} ${ZIPPED_PATH}

Push to Website

The benefit of this is obvious. Especially since it's kind of a slow step to push the bits over the wire. I made a hierarchy on my site with release and pre-release directories under /files . This is another check against accidentally deploying the wrong file. I just use vanilla scp to push the file.

Update Download Link

The zipped .pkg file has the version name embedded in it (ie MercuryMover_0.9.4.zip) in order to groove with Sparkle . However, i don't really want to update my site every time i spin a build, so i cheat with a symlink. Once the build has been pushed, i just do a:

ssh -l username heliumfoot.com "rm -f ${SYMBOLIC_LINK_NAME}"
ssh -l username heliumfoot.com "ln -s ${DESTINATION_DIRECTORY}${ZIPPED_NAME} ${SYMBOLIC_LINK_NAME}"

easy. perfect.

Update appcast.xml

Another no brainer. When i was doing these by hand, i grew to hate the appcast file. And it's so short! I got my pyth on for this one too. My script is quick and dirty but it works. I'm aided by the fact that i don't really have automatic updates, but rather automatic update checking (powered by Sparkle). The MercuryMover UI didn't really groove with the Sparkle info panels, so i rolled my own solution which uses Sparkle to check for updates, and I show my own UI. Without the Sparkle windows, i didn't need release notes which really simplified the appcast processing. I'm sure i'll put them in later when i get a better update system going, but this will be fine for now.


Apparently, i had a lot to say about my build. My wife often calls me the smug chef whenever i'm particularly happy with some dish i made. Anyone care to call me the smug deployer?


31 days until launch.

Posted by kalperin at 01:08 | Comments (4)
<< Inch by Inch | Main | Is Mousing Faster? >>
Comments
Re: Da Ploy

You can double click on your new version of MercuryMover.prefPane that you downloaded to your desktop and System Preferences will dutifully upgrade your existing install; however, it will not restart MercuryMoverAgent.app.

Unless your prefpane restarts MercuryMoverAgent if necessary.



Posted by: Peter Hosey at November 02,2007 04:59
Re: Da Ploy

Very informative post.
I have been thinking about all these issues - suicide code, versioning, installer method, updating. They really are pretty universal concerns for any developer. This posting seems to cover a lot of bases and I for one will be trying to reproduce this sort of workflow.

Innermost digits up.

Posted by: jonathan mitchell at November 02,2007 05:11
Re: Da Ploy

Peter is right of course. The prefpane could restart the agent, and his comment points out that i overstated the effect that System Preferences not restarting the agent had on my decision to use an installer. However, this solution does not account for the use case where the user tries to drag the prefpane to their Library/PreferencePanes/ directory. In this case, the finder will give them a "MercuryMoverAgent.app in use" error. The real solution here is to work on my auto-install. With some work, i can make better use of Sparkle, but still provide a custom UI that will work with MecuryMover. Upgrading from within the app is definitely a superior user experience and sidesteps these issues.

Posted by: kalperin at November 02,2007 16:26
Re: Da Ploy

I am against installers in just about every situation. I personally think they are horribly over abused on OS X. Having said that, there should be some way to avoid using an installer in this case.

Not to get too technical but, wouldn't it be possible that the preference pane on start up sends out a distributed notification with its version number? Then the in memory helper could listen for this and shut itself down. I also wonder if there are any notifications that get sent out when a pane is replaced.

Also, I am guessing your helper app is inside of the application itself. I would also suggest, on the pref pane's start up, copying the helper and running it from the app support directory. This should give you greater flexibility with upgrades.

In just about every situation, an installer can be avoided and for the general "health" of OS X, I strongly advocate that. If we, as independent developers, get our customers used to using installers and even worse, entering admin passwords, we are doing them a disservice and lose some of that "Just Works" feel that OS X is famous for.

Just my thoughts on installers. I am glad to read that you are using a zip file, I think you will find a reduced amount of support going that way. I know I did.

Marcus

Posted by: Marcus S. Zarra at November 02,2007 17:57
Post a comment