[three_fifth_last][/three_fifth_last]
Managing documentation for open source projects (or closed source, for that matter) can be a real pain. How do you create user-friendly, readable documentation that users can contribute to without having to be HTML ninjas?

I was faced with this very dilemma for my free Open Source project, Snipe-IT. My requirements were:

  • Documentation should live on its own branch of the project repo so that people could submit pull requests against it
  • It should look nice and have useful navigation
  • It should be searchable
  • It should be generated from markdown files, so contributing users don’t need to know any HTML
  • It should include the ability to syntax-highlight code for easy readability.

The Github wiki wasn’t flexible enough for me, and although you can check out wiki files and edit locally, I don’t think they allow pull requests, which defeats the entire purpose. (I also never got nested folder side-navigation to work properly, but that might have just been me donking it up.)

I had previously been using Gitbook, which was nice enough, but there are no template options, so everyone’s docs look the same. Additionally, Gitbook will build every single time you push to your repo, regardless of whether you pushed a documentation branch or not. Since we push often, this resulted in dozens of “build failed” messages every day (since we were pushing to feature branches and develop), and it was getting really annoying. I emailed them (since they don’t reply to issues created on their repo), and they replied that the ability to designate a specific branch is coming, but isn’t implemented yet. Meh.

I looked into Daux.io (which is still a great choice for some projects), but I wanted a little more flexibility in the menu. The advantage to Daux.io is that there is no build process, but a build process didn’t really bother me since I’m pretty comfy with the command line.

I ended up settling on Couscous, an open source project built in PHP (though you can document a project in any language with it, obviously, since you’re just writing markdown.) It was a snap to install and I was up and running in under 2 minutes. Couscous generates HTML based off of a template twig file, and syncs the generated pages to Github Pages for you, without crapping all over your actual documentation branch, which was a must-have for me. I didn’t want my users to have to wade through lots of HTML files just to fix a typo or improve my documentation.

Screen Shot 2015-07-20 at 7.51.31 PM

Couscous comes with some beautiful templates, and I decided to use one inspired by ReadTheDocs. You can load remote templates, or create a local template within your repo using a twig file. (Note: If you want to use a CNAME record with your Github Pages, like docs.snipeitapp.com versus yourname.github.io/your-project, you’ll need to use local templates and add the CNAME file to your template directory. That was missing from the docs, but I opened a pull request to update with that detail, which they quickly accepted. So meta!) I had wanted to make some tweaks to the template anyway, so a local template was fine with me.

There are some config variables you’ll need to change, and you’ll need to build out your side navigation in the couscous.yml file, but it’s well documented and pretty easy to do. You can see my couscous.yml file here. (It looks complicated, but once you set it up, you’ll only ever need to mess with it again if you add nav items.)

Basically, once you have Couscous running and your couscous.yml file set up, you preview the generated docs with couscous preview and deploy to Github Pages using couscous deploy. It’s that simple.

Make an edit to your markdown file. couscous preview. Edit some more. couscous preview. Everything looks good? couscous deploy. Commit your changes. Done.

Couscous itself doesn’t do a few things that I wanted. I needed search, and I really wanted a TOC to be generated on long pages, with anchor links to sections further down the page. Backend search scripts are not possible, since Github Pages doesn’t support that, but fortunately, both the search and the TOC challenges are easily solved with JavaScript.

Table of Contents

For the TOC issue, I used generated-toc by Stuart Langridge. It’s an older JavaScript creation, but it still works great, and is small, which was important to me. This simple script generates a table of contents based on headers on the page, and since Couscous uses header HTML tags to handle markdown parsing, this worked beautifully.

Screen Shot 2015-07-20 at 7.53.06 PM

One thing – I use a single # for my page header, and generated-toc will grab all headers by default. To fix this, just use class="generate_from_h2" on the div, for example:

<div id="generated-toc" class="generate_from_h2"></div>

That will make sure your initial h1 doesn’t get included in the TOC.

Search

For the search, I ended up using Tipue, a library that uses pure JavaScript/jQuery to power your search, so no backend programming languages to worry about. It’s really simple to set up, and it’s well-documented.

Screen Shot 2015-07-20 at 7.52.08 PM

Tipue Search uses various modes for loading content. Static mode uses a JavaScript object, while JSON mode uses JSON. Live mode grabs content from a list of pages dynamically. Make sure to use Tipue in “live” mode, versus static mode. (Live mode might get a little slow for very large documentation bases, just keep that in mind. There’s a lot of stuff going on in the DOM, so lots and lots of pages could present a problem.)

You’ll need to include the appropriate CSS and JavaScript files, and set your search url targets in the tipuesearch_set.js file, like this:

var tipuesearch_pages = [
    "http://docs.snipeitapp.com/index.html",
    "http://docs.snipeitapp.com/security.html",
    "http://docs.snipeitapp.com/requirements.html",
    "http://docs.snipeitapp.com/installation/index.html",
     ...
];

Next, create a search.md page and insert the display code.

<form action="search.html">
<input type="text" name="q" id="tipue_search_input" autocomplete="off" required>
</form>
<div id="tipue_search_content"></div>

Then just add your search form to the default.twig and you’re all set. You’ll invoke the Tipue code at the bottom of the page with some options:

<script>
$(document).ready(function() {
     $('#tipue_search_input').tipuesearch({
          'mode': 'live',
          'liveDescription': 'h1',
          'liveContent': '.document'
     });
});
</script>

And you’re good to go. Check the documentation for a more complete explanation and walkthrough.

Make sure you put the JavaScript and CSS files into the website directory (or whatever directory you’re using for your templates and link to them via , etc in your default.twig.)

Something you should know about me: I get unreasonably excited about documentation. When I got all of this working and configured, I couldn’t stop myself from just looking at my own documentation over and over. It’s so beautiful! And usable! And useful! Wouldn’t the world be a better place if it had better docs?

Check out the full source to my documentation branch to see how all of this fits together. It works great, and if you can set up a YAML file and copy+paste some javascript, you’ll be cranking out gorgeous code documentation in no time, using just markdown.

Another few options to look at if none of the above fit the bill for you:

<shameless plug>And hey, if your company needs IT Asset Management Software, check out Snipe-IT. Some people think it’s pretty cool, it’s free, and I like it enough to form a corporation and focus on it mostly full time. If you don’t want to manage a server for it, we have affordable hosting plans that fit any small business budget. </shameless plug>

Advertisement

Themeforest

Advertisement

468x60_makemoney
funny-shocked-surprised-cat-scales-weigh-liar-pics
Previous post

Start-Up Scaling on a Budget

Next post

Demystifying Custom Auth in Laravel 5

snipe

snipe

I’m a tech geek/dev/infosec-nerd/scuba diver/blacksmith/sword-fighter/crime fighter/ENTP/warcrafter/activist. I'm the CTO at Mass Mosaic and the CEO of Grokability, Inc. in San Diego, CA. Tweet at me @snipeyhead or read more...

  • lomifeh

    This is what I’ve been looking for at work to do some stuff. Great find.

    • Awesome, I’m so glad to hear that! (Seems my timing was good for a few people. 🙂 )

  • Samantha Gray

    I’ve been looking for a documentation solution…I may have just found it. Very nice.

    Also, just a heads up, in the first line of your post, it looks like you have a shortcode that may not be working: “[three_fifth_last][/three_fifth_last]”