Writing a static site generator using org-mode.

This site is now generated through org-mode, an emacs library which is used for outlining. The generation of the HTML lies in the export functionality of outlines. The benefits of this system is that its easy, uses a tool that I'm already familiar with, and extensible.

Motivations

My old site was written in Django and was consuming a fair amount of system resources. This has little to do with Django itself, but more with my hosting setup. In an effort to simplify, I've changed hosting and, with this, changed what generates my website. Part of this change is removing many of the "expensive" operations in a server, such as python interpreters that run per site. Now, while sustaining 150 requests per second, the hosting setup only consumes 4% of my 64mb of ram allotment.

Emacs as a static site generator

org_mode_article_in_progress.jpg

Figure 1: This blog post in progress.

The heart of my blog lies in org-modes export format. You can find the documentation for it here. This post, currently looks something like the picture above. Standard org-mode stuff.

The exporting stuff lives in a small amount of elisp (which is in a non-exported node of my index.org (which turns into index.html)). When I export my org project, it publishes via tramp to my server's web root.

Show me the code

  (require 'org-publish)
  (setq org-publish-project-alist
        '(("blog"
           :components ("blog-content" "blog-static"))
          ("blog-content"
           :base-directory "~/src/blogposts/"
           :base-extension "org"
           :publishing-directory "/ssh:justinlilly@caesium.justinlilly.com:/var/www/blog/"
           :recursive t
           :publishing-function org-publish-org-to-html
           :export-with-tags nil
           :headline-levels 4             ; Just the default for this project.
           :table-of-contents nil
           :section-numbers nil
           :sub-superscript nil
           :todo-keywords nil
           :author nil
           :creator-info nil
           :html-preamble "blog header goes here"
           :html-postamble nil
           :style "This is raw html for stylesheet <link>'s"
           :timestamp t
           :exclude-tags ("noexport" "todo")
           :auto-preamble t)
          ("blog-static"
           :base-directory "~/src/blogposts/static/"
           :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf\\|otf"
           :publishing-directory "/ssh:justinlilly@caesium.justinlilly.com:/var/www/blog/static/"
           :recursive t
           :publishing-function org-publish-attachment)))

Whew. That's a lot. Let's dissect it. First we require the proper modules so we have access to the relevant functions. Then we set a variable that orgmode expects. The first stanza says that the "blog" project has 2 components, "blog-content" and "blog-static". This was necessary to separate in order to handle them a little differently.

The blog content section is where the meaty bit is. You tell it where the source is. This is checkout dependent, but this is setup for a soon-to-be deprecated server. You tell it what type of files it should pick up for exporting. You could just as easily choose markdown here, and just change the publishing-function attribute to something that knows how to export markdown. I also tell it where to put the files after exporting them. I use tramp syntax to put it on my remote server. Recursive tells it to look in sub-folders for other things to publish. The publishing function is large and heinous. Its confusing to read, but after hacking through it a bit, I got what I wanted. If you're comfortable with elisp, you'll probably want to write your own.

Most of the other variables are to adjust the output and are really only relevant if you're using the org-publish-org-to-html function too. C-h v tends to help out here. Beyond that, use the source, Luke. Two worth mentioning, sub-superscript turns off the ability to use superscript via an easy markup. I think it was something like underscore made a subscript which totally screws up writing about python and init files. The exclude-tags bit is also helpful. I can have an outline node of drafts, one of todos, and one of the elisp expressions above, and they don't show to you guys.

The blog-static type uploads all of my static assets. They're checked into the git repo too and I use org-links to link them properly. It has a special publish-function for binary files.

That should more or less get you towards a static blog. All that's left is writing the css and the content.