Setting up the blog

Getting Started

Inspired by this blog created using Jekyll and Github Pages, I set out to make my own. I had no idea how website hosting worked (I still pretty much don’t), but I was pretty familiar with github, so this seemed like minimal-setup way to get a site up and running. A couple of failed attempts later, and without much time on my hands, it was clear I was missing something or this method involved a little more tinkering than I was willing to put into it. I left the idea in the back of my head for a while.

Then I discovered this blog post describing how to use Hugo and Gitlab Pages to make your own blog. Hugo seemed like a neat alternative to Jekyll, and the Gitlab Pages setup seemed easier to get started with. Plus, the built-in continuous integration looked like it could be handy. So I followed the instructions, forked a template blog, and in no time I had a generic lorem-ipsum website running at dieggsy.gitlab.io/blog.

Domain Name

I also knew I wanted a custom domain name, but had no idea how to go about that either. After some googling, I came across Google Domains, and I think I trusted google enough to know what they’re talking about in this case to just go with it. I had talked to someone earlier about how it would be cool if there was a website called whyarethingsthewaytheyare.com, where I could get random questions answered, like why sliced bread is the particular size and shape it is. Interestingly enough, this domain name wasn’t already taken, so I decided to use it for my blog.

I don’t remember the exact process of getting this custom domain to point to the Gitlab one (it was a while ago), so I’ll try my best. One step is going to the “Configure DNS” tab of google domains, and adding a “Custom resource record” as follows:

I think this just points the page at Gitlab’s servers, or something (again, pretty clueless about how it all works). Then, under the “Pages” settings of the blog repo, you can add your custom domain name. Additionally, in the same tab of google domains, you can add subdomain forwarding so that the www variant of your page redirects to the “naked” domain name.

Customization

I don’t know anything about HTML and have broken this site a couple times. Please help.

Actually, it’s not very easy to break the site exactly, since Gitlab Pages will catch any errors and simply not update the website if something’s not working. This makes experimentation for an HTML newbie like me fairly painless.

Thank god.

Workflow

Excellent. The last thing to do was figure out a workflow for blogging. The source for posts in Hugo (and Jekyll) is Markdown, but I’m not a huge fan of markdown. I wanted to know if there was a way to use org-mode in Emacs to keep a single file where each heading corresponds to one post, and headings could then be exported to individual markdown files.

Org-mode is an excellent organizational tool with many built-in an external export options, so I knew there had to be a way. I looked around and a couple of people had done similar things, but none of them worked exactly how I wanted, so I wrote a custom emacs-lisp function to do this, making use of ox-pandoc:

(defun diego/org-hugo-export ()
  "Export current subheading to markdown using pandoc."
  (interactive)
  ;; Save cursor position
  (save-excursion
    ;; Go to top level heading for subtree (you can change the number 10
    ;; if you /really/ need more than 10 sublevels...)
    (unless (eq (org-current-level) 1)
      (outline-up-heading 10))
    ;; Set export format, pandoc options, post properties
    (let* ((org-pandoc-format 'markdown)
           (org-pandoc-options-for-markdown '((standalone . t)
                                              (atx-headers . t)
                                              (columns . 79)))
           (hl (org-element-at-point))
           (filename (org-element-property :EXPORT_FILE_NAME hl))
           (title (format "\"%s\"" (org-element-property :title hl)))
           (slug (format "\"%s\"" (org-element-property :SLUG hl)))
           (date (format "\"%s\"" (org-element-property :DATE hl)))
           (categories
            (format "[\"%s\"]"
                    (mapconcat 'identity (org-get-tags) "\",\""))))
      ;; Make the export
      (org-export-to-file
          'pandoc
          (org-export-output-file-name
           (concat (make-temp-name ".tmp") ".org") t)
        nil t nil nil nil
        (lambda (f) (org-pandoc-run-to-buffer-or-file f 'markdown t nil)))
      ;; Use advice-add to add advice to existing process sentinel
      ;; to modify file /after/ the export process has finished.
      (advice-add
       #'org-pandoc-sentinel
       :after
       `(lambda (process event)
          ;; Grab the file using with-temp-file, which saves our changes
          ;; after evaluation.
          (with-temp-file ,filename
            (insert-file-contents ,filename)
            (goto-char (point-min))
            ;; Remove default header
            (re-search-forward "---\\(.\\|\n\\)+?---\n\n")
            (replace-match "")
            (goto-char (point-min))
            ;; Insert new properties
            (insert
             (format
              "---\ntitle: %s\nslug: %s\ndate: %s\ncategories: %s\n---\n\n"
              ,title ,slug ,date ,categories))
            ;; Demote headings and tweak code blocks
            (dolist (reps '(("^#" . "##")
                            ("\n``` {\\.\\(.+?\\)}" . "```\\1")))
              (goto-char (point-min))
              (while (re-search-forward (car reps) nil t)
                (replace-match (cdr reps))))))
       '((name . "hugo-advice")))
      ;; We don't want our advice to stick around afterwards
      (advice-remove #'org-pandoc-sentinel 'hugo-advice))))

Moving past how I probably still suck at emacs-lisp, this function basically just ensures you’re at a top level org subtree, then extracts post properties from the :PROPERTIES: drawer for that subtree.1 The title comes from the heading title, and categories are parsed from org tags. After the subtree is converted to a markdown file, the file is then slightly modified to demote headings and insert the post properties at the top. 2

To automate creation of the properties drawer, I set up this yasnippet snippet to automatically get the appropriate information and insert the drawer on typing bprops [TAB]:

# key: bprops
# name: blog-properties
# --
:PROPERTIES:
:EXPORT_FILE_NAME: ~/blog/content/posts/`(replace-regexp-in-string " " "-" (replace-regexp-in-string "[^a-zA-Z0-9 ]" "" (downcase (cdr (assoc "ITEM" (org-entry-properties))))))`.md
:SLUG:     `(replace-regexp-in-string " " "-" (replace-regexp-in-string "[^a-zA-Z0-9 ]" "" (downcase (cdr (assoc "ITEM" (org-entry-properties))))))`
:DATE:     `(format-time-string "%Y-%m-%d")`
:END:

$0

This just converts the current heading to a filename and url friendly format (removing non-alphanumeric chars and replacing spaces with dashes) for the exported file and post permalink, and inserts the current date.

With this setup, a subtree like this:

Turns into a markdown file like this:

At Last

Now that I have the workflow set up and the website running, I can waste time on creating mediocre content! Fantastic!


  1. Updated 2017-02-18. Thanks to reddit user goldfather8 for suggesting a neater way to get the heading properties.

    [return]
  2. A previous version of the post used (sleep-for 0.5) as a super hackish way to wait for the export process to finish. The new method properly ensures the process has fininshed by executing after the existing process sentinel for the pandoc process.

    [return]

  « »