Org-mode website
At the end of January I re-did my website: I switched from WordPress to a static web site (i.e. a web site that's just HTML files/content, with no programs running on the server).
For many years prior, I'd been running a WordPress site, but I virtually never wrote anything. I diligently upgraded modules and the WordPress core, but the signal:noise ratio was low. That is, I was spending very little time being creative ("signal") and lots of time maintaining the site ("noise").
At the same time, my web site is really simple. Even my WordPress theme selection was simple. So I wasn't using much of the power of WordPress.
I'd been thinking about switching to a static site but I'd prefer not having to write raw HTML or Javascript. When reading about Org-mode, I saw people were using it to generate web sites. I wanted a way to create a web site from org-mode without having to write a lot of elisp, and I found this article, Using org to Blog with Jekyll. And more or less, that's what I set up! Below I describe what I'm currently doing.
Table of Contents
Brief summary
The major pieces to my web site publishing workflow are:
File system setup
My file structure is laid out like this:
org/jb.com/org: _posts/ about.org index.org ... org/jb.com/jekyll _posts/ (other stuff Jekyll created) _site/ _config.yml Makefile ...
Emacs
Emacs initial configuration
Before writing any posts, I needed to set up org-mode to be able to publish content. Also a couple of days in I realized I needed a way to link between blog posts that would use Jekyll's "post_url
".
In my init.el
:
;; This lets you publish a "project" via org-mode export: (setq org-publish-project-alist '( ;; This defines how to publish the org-mode content: ("org-jb" ;; Path to your org files. :base-directory "~/path/to/org/jb.com/org/" :base-extension "org" ;; Path to your Jekyll project. :publishing-directory "~/path/to/org/jb.com/jekyll/" :recursive t :publishing-function org-html-publish-to-html :headline-levels 4 :html-extension "html" :body-only t ;; Only export section between <body> </body> :with-toc nil :auto-preamble nil ) ;; This defines how to publish non-org-mode content e.g. txt files ("org-static-jb" :base-directory "~/path/to/org/jb.com/org/" :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf\\|php\\|mov\\|html\\|txt\\|" :publishing-directory "~/path/to/org/jb.com/jekyll/" :recursive t :publishing-function org-publish-attachment) ;; This says "jb.com" is both of the above: ("jb.com" :components ("org-jb" "org-static-jb")) )) ; creates a new link-type, "jekyll-post". I don't have autocompletion ; working for this yet. (defun org-jekyll-post-link-follow (path) (org-open-file-with-emacs path)) (defun org-jekyll-post-link-export (path desc format) (cond ((eq format 'html) (format "<a href=\"{%% post_url %s %%}\">%s</a>" (file-name-sans-extension path) desc)))) (org-add-link-type "jekyll-post" 'org-jekyll-post-link-follow 'org-jekyll-post-link-export)
The above gets me to the point where C-c C-e
(Org-mode exporting) has an option P p
for publishing the current project.
Writing the post
I go into org/jb.com/org/_posts/
and create a new post named YYYY-MM-DD-postname.org
, e.g. 2019-02-13-org-mode-website.org
.
When I first started I created a yasnippet (with C-c & C-n
). I insert this snippet with C-c & C-s
:
#+OPTIONS: num:nil #+BEGIN_EXPORT HTML --- layout: posts title: $1 excerpt: $2 --- $0 #+END_EXPORT
The snippet lets me type the title ($1
), then hit tab to go to the excerpt ($2
), and then go to the body ($0
).
Note well: I had read that you could use BEGIN_HTML for the above instead of BEGIN_EXPORT, but you can't. BEGIN_EXPORT will put content at the absolute beginning of the resulting HTML file, which is what Jekyll needs for reading the content as Jekyll "frontmatter."
At this point I use org-mode formatting. The only special thing I've done is add #+TOC: headlines 3
to longer articles if I want a table of contents.
Publishing to Jekyll
Configuring Jekyll
When I first set up this environment, I installed Jekyll and then ran jekyll new
:
cd ~/path/to/org/jb.com
jekyll new jekyll
This creates a bunch of stuff in a directory that I have boringly named jekyll
, such as a starter _config.yml
file.
I then edited this config file, setting properties such as:
- title
- url
- description
- theme
- header_pages
- twitter_username (theme-specific property)
At that point, Jekyll was ready to receive HTML files published from org-mode.
Sending the org-mode file to Jekyll
Within the org-mode blog post, I save the file and then hit C-c C-e P p
. This makes org-mode generate html files from the org files, putting them in the :publishing-directory
specified in init.el
. Thus org/jb.com/org/_posts/2019-02-13-org-mode-website.org
is compiled into org/jb.com/jekyll/_posts/2019-02-13-org-mode-website.html
by Emacs.
At this point I can go into the Terminal to run a jekyll server if I want:
cd ~/path/to/org/jb.com/jekyll
jekyll serve
This starts a web server, by default http://127.0.0.1:4000, where I can see the site. If all looks good then I'm ready to have Jekyll do its own export to the _site/
directory.
Building the Jekyll content
This step is relatively simple:
cd ~/path/to/org/jb.com/jekyll/ JEKYLL_ENV=production jekyll build
This "compiles" the Jekyll content and puts it into _site/
. The JEKYLL_ENV
variable is used by my theme at least to know whether to add Google Analytics code into the site. The content built by jekyll build
should be the same as what you see with jekyll serve
.
Uploading the final content
At this point all I need to do is send the content to my web server, which I can do with rsync:
cd ~/path/to/org/jb.com/jekyll/
rsync -avz _site/ user@example.com:path/to/web/root/
Note well: Any time you use rsync without --delete
you have to be kind of careful; if you delete a post, it won't be deleted from the server! I probably should change this.
Makefile
I love make, so of course I built a quick Makefile to build and publish my site:
site: JEKYLL_ENV=production jekyll build pushup: site rsync -avz _site/ user@example.com:path/to/web/root/
Voila! It's that easy! :-P
Bonus: Converting WordPress posts into Jekyll
As a bonus I used the WordPress to Jekyll Exporter plugin for WordPress to export my old posts. These actually sit in my jekyll/_posts/
folder without existing in org/_posts/
, which I think makes sense because I'm not going to go back and edit them beyond fixing obvious errors.
Oh and also everything is in git.
Credits
Special thanks to…
- Using org to Blog with Jekyll
- Org-mode to GitHub pages with Jekyll
- Creating Websites With Org-mode and Jekyll
- many, many other blog posts, web sites, and Stack Exchange/Stack Overflow questions