Friday, July 07, 2006

Automating pkg and app Bundle Installs

This is the second in a series of posts about a simple nightly update system for OS X

In the previous post I showed a script that mounted a dmg image, looked for packages and application bundles inside it, and called other scripts to install these packages and application bundles. In this post, I'll show the scripts I call "pkg-install" and "app-install", which do do actual work of installing software.

First, app-install, which installs application bundles by simply copying them into the /Applications directory.

Script app-install
#!/usr/bin/perl

use strict;
use File::Basename;

my $a = shift;

# Check to see if app of this version is already installed, and
# install if not.
my $appdir = basename($a);
if ( -d "/Applications/$appdir" ) {
chomp ( my $vnew =
`cat $a/Contents/Info.plist|grep -A1 CFBundleShortVersionString|tail -1` );
$vnew =~ s/\<\/*string\>//g;
$vnew =~ s/(^\s+|\s+$)//g;
chomp ( my $vold =
`cat /Applications/$appdir/Contents/Info.plist|grep -A1 CFBundleShortVersionString|tail -1` );
$vold =~ s/\<\/*string\>//g;
$vold =~ s/(^\s+|\s+$)//g;
if ( $vold == $vnew ) {
print "Application bundle of the same version ($vnew)";
print "is already installed.\n";
print "Skipping installation.\n";
exit;
}
}
print "Installing $a in /Applications...\n";
print `cp -r -p $a /Applications/`;
$? && die "Copy failed for application bundle $a\n";

The script takes a single argument, the name of the app directory, and starts by checking to see if an application of the same version is already installed. It does this by first looking for an app directory of the same name under /Applications and then, if it finds one, looking at the "CFBundleShortVersionString" key in the Info.plist files of the two application bundles. If the key is the same in both cases, the script assumes that the applications are identical and skips the installation. Unfortunately, there's no reliable way to tell if an already-installed version is newer than the candidate for installation, since version "numbers" are unique to each application and often contain more than just digits. Could a program reliably decide whether version 1.3-beta2 is newer than 1.3-development-1.7? The app-install script doesn't address this, and just assumes that if it's told to install a different version (older or newer), that's that the person invoking app-install really wants to do.

For pkg bundles (and multi-packages) I have a second script, called "pkg-install".
Script pkg-install
#!/usr/bin/perl

use strict;
use File::Basename;

my $p = shift;

# Check to see if pkg of this version is already installed, and
# install if not.
my $pkgdir = basename($p);
if ( -d "/Library/Receipts/$pkgdir" ) {
chomp ( my $vnew =
`cat $p/Contents/Info.plist|grep -A1 CFBundleShortVersionString|tail -1` );
$vnew =~ s/\<\/*string\>//g;
$vnew =~ s/(^\s+|\s+$)//g;
chomp ( my $vold =
`cat /Library/Receipts/$pkgdir/Contents/Info.plist|grep -A1 CFBundleShortVersionString|tail -1` );
$vold =~ s/\<\/*string\>//g;
$vold =~ s/(^\s+|\s+$)//g;
if ( $vold == $vnew ) {
print "Package of the same version ($vnew) is already installed.\n";
print "Skipping installation.\n";
exit;
}
}
print "Installing $p...\n";
print `installer -pkg $p -target /`;
$? && die "installer failed for package $p\n";

Again, the script first checks for an already-installed package of the same version. In this case, the script looks in the /Library/Receipts directory, where installer places metadata about packages after it installs them. If a package of the same version isn't already installed, the script then invokes installer to install the candidate package.

No comments: