Thursday, July 12, 2012

Automatically add Git hash to Info.plist in Xcode

Here is a script to add a git hash to your app Info.plist when building. The script can also add the git status (clean or not). It does not require preprocessing of the Info.plist, extra targets, or temporary files that must be ignored by git. The script works by modifying the Info.plist in the finished build, so it does not disturb the project working directory and your clean git state.

Open up the Info.plist file and add two properties with keys GitShaHash, and GitState. These values will be overwritten during the build process. I have seen similar scripts that work by storing the hash in CFBundleVersion instead. However, OS X uses CFBundleVersion to compare versions of the same app, and the Mac App Store also requires updates to have a sequential CFBundleVersion. Since the git hash is not sequential, it's better to just leave CFBundleVersion alone and relegate it to an under the hood value.

Select the project target, and add a run script build phase:

Copy and paste the script in. This is a perl scrip so the shell must be set to /usr/bin/perl . Note that the line “my $INFO …” is set for Mac app bundles, you can switch the commented lines around for iOS apps.

use strict;
use warnings;

die "$0: Must be run from Xcode" unless $ENV{"BUILT_PRODUCTS_DIR"};

my $FH;
my $output;

# SHA hash :

open $FH, "xcrun git rev-parse HEAD|" or die;
$output = <$FH>;
close($FH);

chomp( $output );
my $gitShaHash = $output;

# clean state :

$output = `xcrun git status`;

my $gitState;
if ( index( $output, "nothing to commit (working directory clean)" ) != -1 ) {
    $gitState = "clean";
} else {
    $gitState = "dirty";
}

print "gitShaHash : $gitShaHash\n";
print "gitState : $gitState";

# Update Info.plist in build product. Use defaults command

#my $INFO = "$ENV{BUILT_PRODUCTS_DIR}/$ENV{WRAPPER_NAME}/Info"; #iOS
my $INFO = "$ENV{BUILT_PRODUCTS_DIR}/$ENV{WRAPPER_NAME}/Contents/Info"; #OSX

my @defaults = ( 'defaults', 'write', $INFO, 'GitShaHash', "$gitShaHash" );
system( @defaults );

@defaults = ( 'defaults', 'write', $INFO, 'GitState', "$gitState" );
system( @defaults );

All done, now the values will be generated every time you build and the values can be accessed easily from the Info.plist using code:

NSURL *url = [[[NSBundle mainBundle] bundleURL] URLByAppendingPathComponent:@"Contents/Info.plist"];
NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfURL:url];

NSString *hash = [dictionary objectForKey:@"GitShaHash"];
NSString *state = [dictionary objectForKey:@"GitState"];

It’s a good idea to place the values within the about panel or a similar location. The hash can make it easier to track issues with beta testers. And the git state makes it easier to prevent dirty builds from being distributed.

3 comments:

Ross Franklin said...

Edited the script to use xcrun for git commands; enables the script to work on Xcode installations without the command line tools installed.

Yoshiki said...

How did you add this information to the credits window?

Jenny Lee said...

Excellent post...
outsource invoice processing services