On blogging using Org mode through Jekyll

nil

I've been using Octopress with pleasure for up to ten years to build my corner on the internet. Sadly, as software lifecycle goes, it relied on a version of ruby that is no more supported by my favorite rbenv ruby version manager.

So I made a drastical choice: starting from scratch, not porting the existing content, using technologies I love, that is Emacs (Doom, but I'll come to it in another post), it's fabulous Org mode, its big brother Jekyll, and of course, GitHub pages for hosting.

There is a paramount reason for which Jekyll is a first choice when it comes to blogging hosted by Github, is that it actually powers Github pages. Therefore, as opposed to other static sites generators, pushing the sources to the master branch of your repository triggers an automatic build of the site. Awesome.

Installing Jekyll

There are many examples on how to install Jekyll, the most simple way is to follow the excellent tutorials on its website. The only tweaks I made and that are still WIP, is to take the theme I chose to be very minimalistic and extensible through Liquid, aka lanyon, out of the bundle system and integrate it directly into my source tree structure, see https://github.com/jwintz/jwintz.github.io.

Configuring Emacs

Org is very powerful and can be used in a vast variety of context, from agenda to GTD, mail redaction, or even as a publishing basis for scientific publications or presentations. Here's the tricky part. I'll assume for the following that you are a bit familiar with Org mode, at least when it comes to its syntax and headers, which is more than sufficiant for a start. Org comes with a publish module that can be used in order to generate HTML pages and it is what we will use in order to replace Mardown.

Emacs in its recent versions comes with a package management system, and we'll focus on one package in particular: org2jekyll. I won't repeat its README section, however, since Jekyll has evolved since the first drafts of this Emacs package, I'll post my setup of the package.

(def-package! org2jekyll
  :defer t
  :commands
  (org2jekyll-create-draft org2jekyll-publish org2jekyll-list-posts)
  :config
  (setq org2jekyll-blog-author "jwintz")
  (setq org2jekyll-source-directory (expand-file-name "~/Sites/jwintz.github.io/"))
  (setq org2jekyll-jekyll-directory (expand-file-name "~/Sites/jwintz.github.io/"))
  (setq org2jekyll-jekyll-drafts-dir "_drafts/")
  (setq org2jekyll-jekyll-posts-dir "_posts/")
  (setq org-publish-project-alist
    `(("default"
       :base-directory ,(org2jekyll-input-directory)
       :base-extension "org"
       :publishing-directory ,(org2jekyll-output-directory)
       :publishing-function org-html-publish-to-html
       :headline-levels 4
       :section-numbers nil
       :with-toc nil
       :html-preamble t
       :recursive t
       :make-index t
       :html-extension "html"
       :body-only t)
      ("post"
       :base-directory ,(org2jekyll-input-directory)
       :base-extension "org"
       :publishing-directory ,(org2jekyll-output-directory org2jekyll-jekyll-posts-dir)
       :publishing-function org-html-publish-to-html
       :headline-levels 4
       :section-numbers nil
       :with-toc nil
       :html-preamble t
       :recursive t
       :make-index t
       :html-extension "html"
       :body-only t)
      ("home"
       :base-directory ,(org2jekyll-input-directory)
       :base-extension "org"
       :publishing-directory ,(org2jekyll-output-directory)
       :publishing-function org-html-publish-to-html
       :headline-levels 4
       :section-numbers nil
       :with-toc nil
       :html-preamble t
       :recursive t
       :make-index t
       :html-extension "html"
       :body-only t)
      ("page"
       :base-directory ,(org2jekyll-input-directory)
       :base-extension "org"
       :publishing-directory ,(org2jekyll-output-directory)
       :publishing-function org-html-publish-to-html
       :headline-levels 4
       :section-numbers nil
       :with-toc nil
       :html-preamble t
       :recursive t
       :make-index t
       :html-extension "html"
       :body-only t)
      ("img"
       :base-directory ,(org2jekyll-input-directory "img")
       :base-extension "jpg\\|png"
       :publishing-directory ,(org2jekyll-output-directory "img")
       :publishing-function org-publish-attachment
       :recursive t)
      ("js"
       :base-directory ,(org2jekyll-input-directory "js")
       :base-extension "js"
       :publishing-directory ,(org2jekyll-output-directory "js")
       :publishing-function org-publish-attachment
       :recursive t)
      ("css"
       :base-directory ,(org2jekyll-input-directory "css")
       :base-extension "css\\|el"
       :publishing-directory ,(org2jekyll-output-directory "css")
       :publishing-function org-publish-attachment
       :recursive t)
      ("web" :components ("img" "js" "css")))))

Note the home and page components that are essential in order for latest Jekyll versions to work as expected.

Usage

There is a bit of terminology to be aware of, once again quickly grasped from Jekyll's excellent documentation. In short, an entry in your website can either be a post (when it comes to blogging) or a page for dedicated content.

org2jekyll provides functions that can be bound within Emacs to quickly create one of those, here's my bindings:

 "C-c b c" 'org2jekyll-create-draft
 "C-c b p" 'org2jekyll-publish
 "C-c b l" 'org2jekyll-list-posts

Invoking those commands will prompt you for some contents to fill up such as title, description, tags, category and create the Org header accordingly. I have not forked org2jekyll to fix it, but make sure to move the AUTHOR, DATE and TITLE at the top of the header otherwise the publish step will fail.

Then, simply compose the content using Org syntax and invoke 'org2jekyll-publish in order to create the HTML file.

Here the rule of thumb:

  • pages are located in the root directory (both ORG and exported HTML)
  • posts drafts are located (WRT/ the setup) in the _drafts folder
  • posts exports are located (WRT/ the setup) in the _posts folder
  • once done, commit both the ORG and HTML files
  • ignore the _site folder under version control

Firing up an *eshell*, run bundle exec jekyll serve and observe a preview at http://localhost:4000.

Remind that ORG files are not automatically exported as you edit them, call 'org2jekyll-publish whenever you are done with your modification and if jekyll-serve is running, the website will automatically be updated.

Publishing

Just push your master branch to origin remote, and the site will be online in a few seconds.