Wednesday, February 16, 2011

Deploying documentation to GitHub pages with Jenkins / Hudson

GitHub Pages is a neat feature of GitHub that allows you to serve static files by using a special gh-pages branch in a repository. Having got Jenkins set up to build my jdbc-persist project, I thought it would be neat to set up something to take the Javadoc generated during a build and automatically push it to the gh-pages branch when the build finishes.

My project uses Maven for building, and I didn't see any way to have the same job that builds the project handle pushing the documentation on a separate branch, so I set up a second job (which I called jdbc-persist-site) to do that.

The process isn't terribly elegant. The first job uses the Maven site:site goal to generate both Javadoc and a whole Maven project site. The second project first pulls my gh-pages branch. It then runs the following batch script (wouldn't be much different as a .sh script):

This basically just grabs the generated site from the other job manually and overwrites the previous version of the files. I actually had to split the three git commands into 3 separate batch file steps in Jenkins because a single script would stop after just 1 git call.

The final step was to push the new commit to gh-pages so that the updated files would show up. This was a simple matter of selecting the Git Publisher post-build action and telling it to publish to the gh-pages branch on origin. I also set up a trigger to run the jdbc-persist-site job after any successful jdbc-persist build, ensuring that any changes I make are automatically reflected on the site each time.

I find an approach like this to be far better than the approach of storing generated Javadoc files in the same branch as the source code the way a number of projects I like (Guava and Guice for example) do. Directories containing artifacts like Javadoc that can be derived from the source clutter up the project structure, clutter up commit history with changes to derived files and add to the volume of files that someone checking out the project has to download. I think the ability to do this sort of thing easily speaks well for the ease and power of Git's branching model and for GitHub's awesome features!

Tuesday, February 15, 2011

Git clone error on Jenkins/Hudson on Windows

Recently I set up a new instance of Jenkins (formerly Hudson) running on my Windows 7 desktop computer. I tried to set up a job that would pull from a GitHub repository and do a build but (like every other time I've tried this) was foiled by the job simply hanging at the step where it tries to clone or fetch from GitHub. Cancelling the job leads to the following errors in the console output:

Started by user cgdecker
Checkout:workspace / E:\Colin\.hudson\jobs\temp\workspace - hudson.remoting.LocalChannel@692769e1
Using strategy: Default
Checkout:workspace / E:\Colin\.hudson\jobs\temp\workspace - hudson.remoting.LocalChannel@692769e1
GitAPI created
Cloning the remote Git repository
Cloning repository origin
$ git clone -o origin git@github.com:cgdecker/jdbc-persist.git E:\Colin\.hudson\jobs\temp\workspace
ERROR: Error cloning remote repo 'origin' : Could not clone git@github.com:cgdecker/jdbc-persist.git
ERROR: Cause: Error performing git clone -o origin git@github.com:cgdecker/jdbc-persist.git E:\Colin\.hudson\jobs\temp\workspace
null
Trying next repository
ERROR: Could not clone repository
FATAL: Could not clone
hudson.plugins.git.GitException: Could not clone
 at hudson.plugins.git.GitSCM$2.invoke(GitSCM.java:809)
 at hudson.plugins.git.GitSCM$2.invoke(GitSCM.java:740)
 at hudson.FilePath.act(FilePath.java:756)
 at hudson.FilePath.act(FilePath.java:738)
 at hudson.plugins.git.GitSCM.checkout(GitSCM.java:740)
 at hudson.model.AbstractProject.checkout(AbstractProject.java:1171)
 at hudson.model.AbstractBuild$AbstractRunner.checkout(AbstractBuild.java:499)
 at hudson.model.AbstractBuild$AbstractRunner.run(AbstractBuild.java:415)
 at hudson.model.Run.run(Run.java:1362)
 at hudson.maven.MavenModuleSetBuild.run(MavenModuleSetBuild.java:405)
 at hudson.model.ResourceController.execute(ResourceController.java:88)
 at hudson.model.Executor.run(Executor.java:145)

First, I tried the usual suspects:

  • Ensuring that Jenkins was running as the correct user.
  • Ensuring that the user's .ssh directory and keys were in place and checked out.
  • Trying the full path to git.exe in Git's bin directory rather than just using git as the path.

I did a search for the error, but as in the past it turned up no solutions that actually fixed the problem for me. I then tried running a job that just executed a Windows batch command to run git clone on the same repository... which worked fine!

The solution

Double-checking things, I noticed that my PATH contained two directories for my Git installation (msysgit): one for Git/bin and another for Git/cmd, which I hadn't been aware of before. Looking in Git/cmd, I noticed that it contained a file git.cmd which appeared to be some kind of script wrapping git.exe. So, I went into the Jenkins configuration and changed the path to the git executable to git.cmd. And that fixed everything!

Based on what I've seen when searching for this error, it seems like there are quite a few potential causes for it. But this is what worked for me in my situation and I haven't seen this solution elsewhere, so I thought I'd write it down... if nothing else to help me remember if it happens again.