• How to work around problems with Xcode SCM and svn+ssh

    I use source control religiously, even for my own stupid projects at home. To this end, I run a SVN server on an old Quicksilver G4 tower that I use as my file server. Now that I am getting proficient enough with Xcode to do some damage, I decided to set up my SVN repo so I can manage my source while I work. Xcode’s SCM support is missing a few features and can thus be characterized as “modest” when compared with similar tools like Subversive in Eclipse, but it’s still a welcome addition to what I have come to regard as a generally-excellent IDE.

    I run connectivity with my home SVN server over svn+ssh using transparent authentication. Unfortunately, Xcode doesn’t seem to handle svn+ssh connections the way you might expect them to. Worst of all, if you don’t set things up the way Xcode wants, it half-works, which is really f***ing confusing. Fear not, however – by operating somewhat abnormally, you can get it to work.

    The default svn+ssh case
    I have a server called zaphod that acts as my SVN host. My SVN repo on zaphod is stored in the path /Users/porgesm/Documents/code/svn-repos. My user account on this machine is porgesm, and my user account on my local machine is mporges. To enable transparent authentication, I have a SSH config file that looks like this.

    Host zaphod
    	Hostname zaphod.local
    	Compression yes
    	User porgesm

    zaphod.local uses networking magic to find the IP of the machine named zaphod in my network when I am at home. With this config, I can use SVN from the command line as follows. Note that all I need to enter for the server info is “zaphod”, and my ssh config file expands this appropriately to use any and all config settings I have defined for the host aliased zaphod in the config file.

    maxbookpro:~ mporges$ svn list svn+ssh://zaphod/Users/porgesm/Documents/code/svn-repos
    CommonLib/
    FacePlant/
    HibernateMavenProject/
    MavenReleasePlugin/
    Pudding/
    SampleMavenProject/
    SomeAPI/
    StrikeTracker2.0/
    SvnCpp/
    cxf/

    This all works beautifully… unless you are in Xcode.

    Xcode brings the FUBAR
    So in Xcode (v3.1.4 – I don’t have another version to test with), my first (and incorrect) attempt to set up my SVN repo was as follows. Note the grayed-out label at the bottom where it says “Host Offline” – this is a very important detail that I missed at first.

    Basically, the configuration pictured will let you browse and modify the repository from the Repositories window without issue, but if you try to compare, commit, update, or anything else from your project, all those options are grayed out in the menus and changed files don’t appear in the “SCM Results” tab of your workspace. This is the “half-works” situation I was talking about, which was confusing as hell.

    At first, I thought Xcode was just broken. Since I also run a generic SVN service on zaphod, I tried setting up a repository using svn://zaphod.local as the URL, and it worked fine. So I figured there had to be something about the way I was defining my svn+ssh URL that Xcode doesn’t like, even though that URL works perfectly from the command line.

    Xcode un-FUBAR’d
    The solution is to expand the details of your SSH configuration in to the svn+ssh URL that you enter in to Xcode, and to enter your SSH account password for Xcode to use. Stupid, I know, but this is the only solution I could find that works. You would think that Xcode would hand off to the native SVN client the same way that Subclipse does for things like this, but that would be wishful thinking.

    Once you enter all your details, you will get an “Authenticated” message at the bottom of the screen, and all the options that you expect to be able to use in Xcode will magically start working as expected.

    And now, the caveat
    Of course, there is a caveat to this workaround. Using my SSH config file, I can use the alias zaphod to access my server from anywhere on the Internet by changing the server address in my config file from zaphod.local to my dynamic DNS server and configuring the obfuscated SSH port number that I use. This is useful for when I am not at home, since I like to use SSH to log in to zaphod remotely from coffee shops and hotels to tunnel various services that the server runs (such as iTunes sharing, AFP for mounting remote directories, VNC, etc.).

    By explicitly specifying the server address in Xcode as in the solution above, I now have to keep the Xcode SCM repository details up to date with my SSH config file. This defeats the purpose of the SSH config file, which is to store all your SSH settings in one place for SSH to find and use. Suckage.

    The best solution I can think of for this stupidity is to set up an SSH tunnel on a local port, and configure Xcode to point to that port on localhost. The downside of this is that I can’t use SCM with Xcode at home without having my tunnel lit up; but at least I’ll know that my SSH settings are all maintained in one place instead of scattered around various files.

    To set up what I am describing, I would configure my SSH entry for zaphod as follows. The LocalForward entry causes SSH to tunnel port 3690 on my local machine to port 3690 on zaphod.local, which is where the standard SVN service runs. Note that the LocalForward is always set up from the perspective of the SSH host; in this case, my G4 tower in my local network. It’s basically just looping back to itself in the config file shown below.

    Host zaphod
    	Hostname servername.dynamic-dns-server-provider.com
    	Port [obfuscated port number]
    	Compression yes
    	User porgesm
            #svn loopback for Xcode SVN
            LocalForward 3690 zaphod.local:3690

    I can then light up this tunnel with the command ssh zaphod in Terminal; the tunnel stays lit up as long as I have network connectivity and my Terminal session stays open. I would then use the SVN URL svn://localhost:3690 in Xcode to ride the tunnel securely across the Internet and access the SVN service on zaphod as if I was at home, as shown in the picture below. Note that when you go this route, there’s no need to provide authentication details since the SSH tunnel has already authorized me and I haven’t set up my svn:// service to require authentication. If I had set up the svn:// service to require authentication, I could enter my credentials in the fields provided in Xcode.

    I filed this as a bug with Apple (#7646524), so hopefully they will fix it in a future release.

    Category: Uncategorized | Tags: