Streamlining documentation on your project websites

When hacking on small open source projects you end up having to be a jack of all trades - hacker, documentor, community manager. Documentation can be annoying when you just want to hack, but if you want people to your code it’s essential.

One problem i’ve experienced is keeping documentation on the project’s website up to date with how the software actually works.

Generally you have two sets of documentation - one in your project’s source code repo, and one on your project’s website. The website docs tend to cover meta information about the project, and maybe a quick install guide or demo. Your source code docs will cover the nitty-gritty of setting up your app, and maybe how to hack on the code.

More often than not the docs in the project’s source will get updated more regularly than on the website, and after a while the website docs may end up diverging from how the software actually works. This can be especially noticeable in installation docs.

I’ve tried attacking this problem on a new project i’m hacking on.

Lately i’ve been using nanoc to build a few simple sites. Rather than a full blown application server running in the background a la Rails or Django, nanoc simply compiles the templates and layouts on your site and spits out static HTML.

nanoc makes it easy to write your own helpers to be used during the compile phase, so i’ve written a simple helper to include documentation from an external source inline with my content:

#lib/external_docs.rb


def generate_docs_from_source(name)
  doc_path = File.join(@config[:code_src], 'doc')
  filename = File.join(doc_path, "#{name.upcase}.md")

  if File.exists?(filename)
    output = `rdiscount #{filename}`
    Haml::Helpers::find_and_preserve(output, ["pre"])
  else
    "<span style='color: red'>Error: #{name.upcase}.md doesn't exist in #{doc_path}</span>"
  end
end

This calls out to rdiscount to generate HTML from a Markdown document. You can use it easily in your templates:

%div#installation

  %div#install.generated

    = generate_docs_from_source('install')

Which will render INSTALL.md in your source code docs/ directory under div#install. This makes it easy to theme the generated docs, courtesy of the .generated selector.

The nanoc command line tool has a simple way to compile a site:

$ nanoc compile

Which is useful when you want to call a compile from other scripts. You can probably see where i’m going with this:

Docs build cycle

On the main project source repo i’ve configured a post-receive hook to POST to docs.flapjack-project.com, which is a Sinatra app that triggers a build of the website. Before it does the build, it checks out the latest copy of the project and website sources.

The nice thing about this approach is you can easily interpolate your own static docs with those from the source code:

%div#developing

  %h2 Source code
  %p  
    Flapjack's code is maintained in two repositories on GitHub:
    %ul 
      %li 
        %a{:href => "http://github.com/auxesis/flapjack/tree/master"}> flapjack
        , the core of the monitoring system.
      %li 
        %a{:href => "http://github.com/auxesis/flapjack-admin/tree/master"}> flapjack-admin  
        , the admin interface.
 
  %p  
    Flapjack is open source, released under the 
    = link_to "MIT Licence", "http://en.wikipedia.org/wiki/MIT_License"
    \.  
  
  %div.generated#developing
    = generate_docs_from_source('developing')

The code for those interested: