I have some files written in Org that I want to publish & upload to a web server every week at a set time. Why? I’d like to be able to view my notes from anywhere, and others may stumble upon them and find them useful. However, I don’t want to have to remember to publish & upload everytime I make change to these files, and I want this to occur with as little user interaction as possible.

To accomplish this, I’m going to make use of Emacs Batch Mode. Batch Mode will run Emacs in a non-interactive fashion; you feed it a file or an Elisp function to run, Emacs does its work and then exits without displaying a window. ox-twbs is an Emacs package I’ll be using to export my Org files to Bootstrap-themed HTML.

The project I want to set up will be a couple of files that will act as a log of the things I have accomplished each week, goals for the next week and any associated notes or links I’d like to record. I’m going to create a directory for this project at ~/org/log and create a couple of files: index.org will just contain a link to our second file, 2017.org. 2017.org will contain a descending-order list of the weeks of the year with associated notes using the following structure:

* Week 11 (March 13 2017)
** Recap
** Next Week
* Week 10 (March 6 2017)
** Recap
** Next Week

By default, Org mode will create a full table of contents for the page, complete with the headlines being numbered. I’d like to avoid this: I only want to see up to the second level headlines in the table of contents and nothing below. Additionally, I don’t want the headlines to be numbered.

Thankfully, I can easily specify all of these settings in one place by defining a project in Org mode. By defining a project, we can easily generate HTML for our project with one command: (org-publish-project "PROJECT_NAME"). A variable called org-publish-project-alist is used to define any number of Org projects one may have.

The first thing I want to do is create a new Elisp file that will be used to import needed packages, define any required variables, and run the necessary function to build the site. Since I use Spacemacs (which has a fairly high amount of modules to load), the only modules I want to load are the bare minimum required to get Org running so Emacs can do its business. Inside ~/.emacs.d/, I create a new file called org-export.el with the following contents:

(package-initialize)
(require 'org)
(require 'ox)
(require 'ox-publish)

(setq user-full-name "Dale Karp")
(setq user-mail-address "dale@dale.io")

(setq org-publish-project-alist
      '(("my_log"
        :base-directory "~/org/log/"
        :publishing-directory "~/projects/site/log"
        :publishing-function org-twbs-publish-to-html
        :section-numbers nil
        :table-of-contents 2)))


(org-mode)
(org-publish-project "my_log")

First, I import some packages and set some variable such as who the author will be (me!). Next, the Org project gets defined within org-publish-project-alist.

I define a single project called “my_log” with some options. Some of these are self-explanatory: :base-directory tells Org where to look for the files - any .org file in this folder will be processed. Since I’ll be publishing this to a subsection of my website, I’ve set the :publishing-directory to the right location. Because I’m using ox-twbs to generate our HTML, I need to tell Org to use it when publishing instead of the built-in HTML exporter. Finally, I let Org know that I don’t want any sections to have numbers, and that I only want our table of contents to have links for level 2 headlines and above.

Now that our publishing pipeline is ready, I need to set up the ‘automation’ part. I host my site on GitHub Pages, so all I need to do to build my site is to create a new Git commit in my site repository after publishing, and pushing that commit to GitHub. To do all of this, I’ll write a Shell script! Because I’ll be a systemd timer to run this script at a set interval once a week, I need to set up a few things like retrieving SSH keys for the session so I can push to GitHub without being prompted for a pssword.

#!/bin/sh

# load ssh keys from keychain for this session
eval $(keychain --eval --quiet --noask id_rsa)

# call & run our exporter elisp script
emacs --batch -l ~/.emacs.d/orgexport.el

# change directory to our repo
cd ~/projects/site

# commit latest changes
git commit -am "Log for: $(date)"

# push commit to get built on gh pages
git push

With the script written, the last thing to do is to use a scheduler to run it every week. I’m using systemd’s timers to do this, but cron should work as well. I created two files in ~./config/systemd/user with the following contents:

# mylog-org-export.service
[Unit]
Description=Exports Org project "My_Log" and pushes Git to GH Pages

[Service]
ExecStart=/home/dale/.bin/org_upload.sh
# mylog-org-export.timer
[Unit]
Description=Run mylog-org-export.service every Monday at midnight.

[Timer]
OnCalendar=weekly
Persistent=true
Unit=mylog-org-export.service

[Install]
WantedBy=timers.target

Now I can test to make sure our timer works and once verified, enable it:

systemctl --user start mylog-org-export.service
systemctl --user enable mylog-org-export.timer

Verify that the timer is active with systemctl --user list-timers, and we’re good to go!

At this point, I have an automated Org publishing pipeline that will convert our Org files to HTML once a week and then push the new data to GitHub pages to built. This approach can be customized fairly easily to publish anything from project documentation to a personal blog. If you have any suggestions on how to improve this or have any alternative methods, please let me know!