Migrating from Hugo to org-mode
Table of Contents
I've been wanting to migrate the blog from Hugo to org-mode for quite a long time now. This weekend I decided it was time to finally tackle it, here is how I did it.
Requirements
First of all, all it's needed for publishing a blog on org-mode is emacs, nothing else. I also wanted to be able to automatically deploy new posts whenever new content is pushed to the repository.
To be able to achieve all this, I needed to set up an org-mode project and create a Github action, the next sections explains how to do it.
Set up and publish an org-mode project
In order to set up the project, I followed two good resources, the own org-mode documentation on Publishing and an introduction to Publishing Org-mode files to HTML. At the end I ended up with this piece of code:
(require 'ox-publish) (setq org-html-head-include-default-style nil) (setq org-html-htmlize-output-type 'css) (setq org-publish-project-alist '( ("org-notes" :base-directory "." :base-extension "org" :exclude "static/.*" :publishing-directory "public/" :recursive t :publishing-function org-html-publish-to-html :headline-levels 6 :section-numbers nil :auto-preamble t :auto-sitemap t :with-toc t :sitemap-filename "sitemap.org" :sitemap-title "" :sitemap-sort-files anti-chronologically :makeindex t ;; :html-use-infojs t :html-preamble t :html-postamble t ) ("org-static" :base-directory "." :exclude "static/.*" :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf" :publishing-directory "public/" :recursive t :publishing-function org-publish-attachment ) ("org" :components ("org-notes" "org-static")) )) (org-publish "org" t )
The full code used in the actual blog can be found at projects.lisp
The variable org-publish-project-alist
This variable holds all the projects to be build, usually a good practice is to split static content from dynamic one. In the code above, "org-notes"
are the actual blog posts, while "org-static"
publish, as its name says, all the static content.
Compatibility with Hugo
As I have more than 500 posts written since I started blogging (First on blogger, then self-hosted Wordpress, Jekyll, Hugo…) and it was a high time investment trying to migrate all of them without breaking links and style, I decided to leave all of them as they were on Hugo.
I copied the static files generated by hugo to a folder called static/hugo
. But org-mode does nothing with this content, since I do not want it to be handled by it. I will elaborate more on this later.
Enable privacy aware commenting system
As I want to leave disqus behind, I have found an usefull Github bot called utterance/utterances that enable users to post comments on every page of the blog, go to the end of the pages and try it out!. The only thing required is a github account, but since this is a tech blog, hopefully almost all readers have one.
Set up a Github Action for automated deployments
At this point I have a ready to publish project. The next step is to automate the deployment so every time I push to elbaulp/elbaulp.github.io a github action runs and deploy the content to Github Pages.
Write a custom Github Action
In order to write a github actions one needs to create a file called action.yml
at the root of the repository. This one in particular is very simple, since it only specify that it will execute docker using a Dokerfile:
name: 'Build Blog' description: 'Build an org-mode project and publish also old hugo blog content' runs: using: 'docker' image: 'Dockerfile'
Write a Dockerfile
The Dockerfile
I've used is based on alpine-emacs
and it is also very simple:
- Installs
npm
andhtml-minifier-terser
- Copy the necessary files with the org-mode project
- Copy some
org-templates
. - Copy the old version of the blog, build with Hugo
- Execute the following script:
1: #!/bin/sh 2: 3: PUBLIC_DIR="${GITHUB_WORKSPACE}/public" 4: 5: function print_info() { 6: echo -e "\e[36mINFO: ${1}\e[m" 7: } 8: 9: emacs --batch --no-init-file --load ./projects.lisp --funcall toggle-debug-on-error 10: 11: html-minifier-terser --collapse-whitespace --remove-comments --remove-optional-tags --remove-redundant-attributes --remove-script-type-attributes --remove-tag-whitespace --use-short-doctype --minify-css true --minify-js true --input-dir ${PUBLIC_DIR} --output-dir ${PUBLIC_DIR} --file-ext html 12: 13: html-minifier-terser --collapse-whitespace --remove-comments --remove-optional-tags --remove-redundant-attributes --remove-script-type-attributes --remove-tag-whitespace --use-short-doctype --minify-css true --minify-js true --input-dir ${PUBLIC_DIR} --output-dir ${PUBLIC_DIR} --file-ext css 14: 15: mv ${GITHUB_WORKSPACE}/static/hugo/* ${PUBLIC_DIR}/ 16: mv ${GITHUB_WORKSPACE}/static/googlee20ada3c6370fb77.html ${PUBLIC_DIR}/
emacs --batch --no-init-file --load ./projects.lisp --funcall toggle-debug-on-error
runs emacs on batch
mode to execute the project.lisp
file and generate the blog, then it copies the old version of the blog in Hugo and minimize the contents.
Conclusion
And that is basically it, now everytime I publish to the repository the blog is automatically rebuild.
Future Improvements
[ ]
Generate a proper rss feed[ ]
Handle categories and tags[ ]
Enable info-js[ ]
Improve semantic content tags[ ]
Improve css
Useful resources
- https://pavpanchekha.com/blog/org-mode-publish.html
- https://orgmode.org/worg/org-tutorials/org-publish-html-tutorial.html
- https://stackoverflow.com/a/24087061/1612432
- https://stackoverflow.com/q/47508315/1612432
- https://nicolas.petton.fr/blog/blogging-with-org-mode.html
- https://orgmode.org/worg/code/org-info-js/
- https://www.reddit.com/r/emacs/comments/czh6d6/migrating_from_jekyll_to_orgmode_and_github/