Writing by Jason on Tuesday, 10 of August , 2010 at 12:43 pm
I didn’t see this elsewhere on the web, so I thought I’d note it down just in case anyone else has the same trouble.
If you create a ssh key for github using a passphrase and then attempt to use it from hudson the passphrase will not be unlocked and the git command will fail with this error:
<code>
ERROR: Error cloning remote repo 'origin' : Could not clone git@github.com:bookshelfapps/SongBook.git
ERROR: Cause: Error performing git clone -o origin git@github.com:bookshelfapps/SongBook.git /Volumes/Drobo1/ServerApps/Hudson/jobs/SongBook DS/workspace
Command returned status code 128: Cloning into /Volumes/Drobo1/ServerApps/Hudson/jobs/SongBook DS/workspace...
Permission denied (publickey).
fatal: The remote end hung up unexpectedly?
</code>
Testing in a terminal window everything will work fine. This is because Mac OS X puts the ssh passphrase in your keychain automatically, and the keychain is unlocked in an interactive login, but not in a daemon (which Hudson will most likely be running as).
The simplest solution is to remove the passphrase from the ssh key, using <code>ssh-keygen -p</code>. That’s what I did to prove this was the problem.
Category: Cocoa
Writing by Jason on Thursday, 15 of April , 2010 at 10:45 am
This is worth knowing about as it can cause an intermittent crash in your code that is very hard to track down.
I have a property on class Content called image. I synthesised the setter, but overrode the getter (incorrectly). This is what the getter looked like:
- (NSImage)image;
{
if( image ) return image;
return [NSImage imageNamed:@"MissingAlbumArt.png"];
}
Looks OK, but experienced cocoa developers will probably have already spotted the flaw already.
What can happen is that a user of the Content class reads the image and starts using it in a local function. Meanwhile another thread sets a new image on the Content. The original image is now released in the setter. The user now has a reference to a released object and may crash.
This is very timing related, so will only show up intermittently, which makes it hard to track down. I’d been seeing crash reports for this bug that I couldn’t explain for quite a while. Luckily I caught it in the debugger while testing some other code and had a chance to look at it in detail. The key to understanding it was when I noticed that the image that caused the crash was a different image to the one that Content contained.
This is how the code should have looked:
- (NSImage)image;
{
if( image ) return [[image retain]autorelease];
return [NSImage imageNamed:@"MissingAlbumArt.png"];
}
Now that I have found this bug I do recall being told to use the [[x retain]autorelease] pattern, but in this case I overlooked it.
Category: Cocoa
Writing by Jason on Friday, 6 of June , 2008 at 10:57 am
I’ve been having a hard time getting my migrations working. The trouble is that so much happens behind the scenes that when you do something wrong it’s hard to tell what it is.
A useful resource is (as usual) mmalc’s sample application. You can download it at http://homepage.mac.com/mmalc/CocoaExamples/MigratingDepartmentAndEmployees.zip. I haven’t used code from the sample in my app, but I have used the code to help see what is going on. I’m planning on using the automatic migration if possible rather than the manual method that is in the sample.
My automatic migration is triggered by something like this in my NSDocument subclass:
- (BOOL)configurePersistentStoreCoordinatorForURL:(NSURL *)url
ofType:(NSString *)fileType
modelConfiguration:(NSString *)configuration
storeOptions:(NSDictionary *)storeOptions
error:(NSError **)error;
{
NSDictionary *optionsDictionary =
[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES]
forKey:NSMigratePersistentStoresAutomaticallyOption];
BOOL result = [super configurePersistentStoreCoordinatorForURL:url
ofType:fileType
modelConfiguration:configuration
storeOptions:optionsDictionary
error:error];
return result;
}
The biggest trap I have found so far, and this took me a while to figure out but it seems kind of obvious now, is that if you change a model version then you have to create a new mapping model. I couldn’t figure out why my mapping model was not found, and that was the reason. I was getting errors like this:
Persistent store migration failed, missing mapping model.
I fixed the problem by recreating the mapping model. Possibly the compile process could figure this out and fix it, I don’t really know, but it took me a while to figure out what was going on. Maybe the Design.Mapping Model.Update Source Model menu fixes this?
(Basically I’m liveblogging this now.) Finally I have it working. The breakthrough was to give up on trying to keep the file extension the same, once I made that change it all started working fine. I’m sure I could probably make it work with enough effort, but I think it is a good idea to change the file extension.
I wouldn’t have been able to get this working without mmalc’s sample code, so big thanks to him for his support of the developer community.
Category: Cocoa
Writing by Jason on Sunday, 6 of April , 2008 at 4:29 pm
I’m writing this up because it took me ages to figure out. The code is very simple though.
What I wanted to do was to save the expanded/closed state of an outline view to the data file in my Core Data application. Cocoa has automatic support for saving the expanded state to the user preferences, but this was not sufficient for multi-document application.
First step, and the easy one is to save the expanded state. Start by adding a boolean attribute to your tree object, I called mine expandState.
Storing the state is easy, NSOutlineView provides two delegate methods that allow us to do what we want. So add the following code to your NSOutlineView delegate:
- (void)outlineViewItemDidExpand:(NSNotification *)notification
{
CDObject* group =
[[[notification userInfo]valueForKey:@"NSObject"]representedObject];
[group setExpandState:[NSNumber numberWithBool:YES]];
}
- (void)outlineViewItemDidCollapse:(NSNotification *)notification
{
CDObject* group =
[[[notification userInfo]valueForKey:@"NSObject"]representedObject];
[group setExpandState:[NSNumber numberWithBool:NO]];
}
Now the next step, and the bit that took me ages to sort out, is to set the expanded state when the NSOutlineView loads. My NSOutlineView uses bindings to get it’s data, so the place to hook this in was not obvious to me. -awakeFromNib is too early, and anything I tried in NSWindowController didn’t work either. I was thinking about this the wrong way. The answer was -reloadData in the NSOutlineView. A little NSLogging showed me that this method was called by the bindings code, so I went ahead and implemented this in my subclass as follows:
- (void)reloadData;
{
[super reloadData];
NSInteger i;
for( i = 0; i < [self numberOfRows]; i++ ) {
NSTreeNode* item = [self itemAtRow:i];
if( [[[item representedObject]expandState]boolValue] )
[self expandItem:item];
}
}
And that's all there is to it. Three small methods.
Category: Cocoa
Writing by Jason on Monday, 31 of March , 2008 at 10:33 am
Leopard server already comes with Subversion installed, but to use it on the network you need to make some changes. Leopard client also has Subversion installed, so this makes an almost simple combination to work with. Unfortunately there a few things that still need to be set up if you want networked subversion access. Here’s what I did (minus the mistakes, wrong turns and google searches):
On the server create a directory for subversion:
mkdir /Volumes/ServerRAID/Subversion
svnadmin create /Volumes/ServerRAID/Subversion/repo
Now edit httpd.conf:
sudo emacs /etc/apache2/httpd.conf
(not /etc/httpd/httpd.conf, I don’t know what that is there for but it doesn’t seem to be used)
Uncomment the lines that look like this:
LoadModule dav_module libexec/apache2/mod_dav.so
LoadModule dav_fs_module libexec/apache2/mod_dav_fs.so
LoadModule dav_svn_module libexec/apache2/mod_dav_svn.so
Add these lines at the end of the file:
DAVLockDB /Library/WebServer/davlocks/DAVLockDB
Include /etc/apache2/extra/httpd-svn.conf
Create /etc/apache2/extra/httpd-svn.conf. You’ll need to create the directory too. Make it look something like this:
<Location /svn>
DAV svn
SVNPath /Volumes/StorageRAID/Subversion/repo
AuthType Basic
AuthName "SilverServer Subversion Repository"
AuthUserFile /Volumes/StorageRAID/Subversion/htpasswd
Require valid-user
</location>
Create the DAVLock directory:
cd /Library/WebServer/
mkdir davlocks
sudo chown www:www davlocks
Restart Apache. I did this with Server Preferences. You can check the log in Server Preferences if you have a problem (Apache does not start).
All going well you can now create a project. On your client Mac now create the following directory structure:
[project]
|-trunk
|-branches
|-tags
where [project] is the name of your project. I made this structure in /tmp.
svn import project http://host_name/svn/repo/project \
-m "First Import"
Now you can check out the project like this:
svn checkout http://host/svn_dir/repo/project/trunk project
That’s it. Not too difficult at all.
Update
I’ve got some emails about this note, and helped a few people out with getting subversion up and going. To help others out that may have similar problems I’m summarizing the issues here.
MacPorts
If you installed Apache with MacPorts and you get this error when starting apache:
/opt/local/apache2/bin/apachectl: line 78: 88773 Bus error \
$HTTPD -k $ARGV
then you probably have not installed mod_dav_svn, or possibly your installed version is not the right version. What you need to do is run this command:
sudo port install subversion +mod_dav_svn
I didn’t try this myself but was told that it works.
Category: OS X Server