Current Build Process For This Site
Table of Contents
This website is built with org-mode
and hosted through GitHub pages.
GitHub pages expects only a single index.html
file and builds the site the from the docs
directory - making the build process extremely simple.
Naturally, as this site is purely static there's no user tracking or even a requirement for https
!
Build output
The build output will reside in docs
to start we'll delete that directory and copy over the css files (that reside in resources/
and tell GitHub that this isn't a Jekyll site.
rm -rf docs rm -rf src/tags-*.org # Tag files are stored in the format src/tags-TAG.org mkdir docs cp -r src/resources docs/ touch docs/.nojekyll # For GitHub pages not to build a Jekyll site echo -n "abdrysdale.phd" > docs/CNAME
Building the Home Page
The first thing I want on the home page is a list of articles with the article title as the description.
I'd also like a list of tags that are used on the articles so the user can search by topic - topics are stored in the FILETAGS
property.
To do this, I create a cons list of (title . path)
of each article in the src/
directory that isn't index.org
.
First things first, let's define a generic function for extracting properties from an org-mode document.
(defun get-org-property (prop file) "Extract PROP from the org FILE." (when (file-exists-p file) (with-temp-buffer (insert-file-contents file) ;; No need for error handling here as (cdr nil) and (car nil) return nil (car (cdr (car (org-collect-keywords `(,prop))))))))
Next we define a function that returns all of the articles in format (file . link-title)
.
(defun get-titles-from-org-files (directory) "Extract titles from org files in DIRECTORY." (let ((files (directory-files directory t "^[[:alnum:]-_]+.org$"))) (mapcar (lambda (file) (let ((rel-file (file-relative-name file (expand-file-name directory)))) `(,rel-file . ,(concat (get-org-property "TITLE" file) "\t" (get-org-property "FILETAGS" file))))) files)))
Next the home page (index.org
) is built. Currently this is really simple and just includes a template followed by a list of all of the articles and then a list of all the tags.
We'll take this template:
I'm Alex, a PhD candidate (haemodynamics + graph convolutional neural networks) and a trainee magnetic resonance physicist for NHS Wales. Thank you for visiting my little patch of the internet. All views are my own and not that of my employer. You can expect content on programming, Emacs, philosophy, ethics, magnets and bread. In addition to this, I'll try to post explanations of papers that I publish. If I'm not the first author I'll do my best to explain the paper or, more likely, explain my contribution to paper. This is site is intentially minimal and left as an exercise to the reader... * Copying All material is in the website licensed under the the GNU/GPLv3 license - which can be found here.
insert a title and then add the articles and tags.
(defun build-index (author) "Build the index for the AUTHOR." ;; Copies the README.org to the index. (let ((dir "src") (index "src/index.org") (ignore-files '("index.org" "about.org" "sitemap.org")) (used-tags nil)) ;; Inserts title and template (with-current-buffer (find-file-noselect index) (erase-buffer) (insert (format "#+title: %s\n\n" author)) (insert-file-contents "../index-template.org") (end-of-buffer) (insert "\n\n* Articles\n") (save-buffer)) ;; Gets all of the articles (dolist (result (get-titles-from-org-files dir)) (let ((path (car result)) (title (cdr result))) (with-current-buffer (find-file-noselect index) (goto-char (point-max)) (unless (member path ignore-files) (let ((link (format "- [[file:%s][%s]]\n" path title)) (tags (get-org-property "FILETAGS" path))) (insert link) ;; Insert a link to article in the index ;; Insert a link to article in each of the tags file. (dolist (tag (split-string tags ":")) (unless (string-empty-p tag) (let* ((tag-file (concat "tags-" tag ".org")) (tag-entry `(,tag . ,tag-file))) (unless (member tag-entry used-tags) (push tag-entry used-tags)) (with-current-buffer (find-file-noselect tag-file) (unless (file-exists-p tag-file) (erase-buffer) (insert (format "#+title:%s\n\n" tag))) (insert link) (save-buffer))))) (save-buffer)))))) ;; Insert a link to the tag files in the index (with-current-buffer (find-file-noselect index) (insert "\n* Tags\n\n") (dolist (tag-info used-tags) (let ((tag (car tag-info)) (file (cdr tag-info))) (insert (format "- [[file:%s][%s]]\n" file tag)))))))
Add an RSS feed
(defun get-rss-feed-item (title link) "Return an rss feed item with TITLE and LINK." (concat "<item>\n" "<title>" title "</title>\n" "<link>" link "</link>\n" "</item>\n"))
(defun build-rss-feed (title link desc src out) "Build a rss feed for TITLE (DESC) at LINK using the posts in SRC to OUT." (with-current-buffer (find-file-noselect (concat out "feed.xml")) (erase-buffer) (insert (concat "<rss version=\"2.0\">\n" "<channel>\n" "<title>" title "</title>\n" "<description>" desc "</description>\n" "<link>" link "</link>\n")) (dolist (file (directory-files src nil "^[[:alnum:]-_]+.org$")) (insert (get-rss-feed-item (get-org-property "TITLE" (concat src "/" file)) (concat link "/" (car (split-string file ".org")) ".html")))) (insert "</channel>\n</rss>") (save-buffer)))
Publishing the Site
Finally, the site is published using ox-publish
with this article (the README.org
) being copied as an article.
One thing of note is that we always publish the articles under the same theme for continuity.
(require 'ox-publish) (require 'whitespace) (require 'htmlize) (let ((current-theme (if custom-enabled-themes (car custom-enabled-themes) 'modus-operandi)) (publish-theme 'modus-operandi) (whitespace-style nil) (whitespace-mode 0) (org-html-validation-link nil) (org-html-head-include-scripts nil) (org-html-head-include-default-style nil) (org-html-head (concat "<link rel=\"stylesheet\"" "href=\"resources/org.css\"" "type=\"text/css\" />" "<header>" "<a href=\"index.html\">Home</a>" " <a href=\"about.html\">About Me</a>" " <a href=\"https://github.com/abdrysdale/abdrysdale.github.io\">Source</a>" " <a href=\"sitemap.html\">Sitemap</a>" " <a href=\"feed.xml\">RSS</a>" "</header>\n")) (org-src-fontify-natively t) (org-publish-project-alist '(("blog" :base-directory "src" :recursive t :publishing-directory "docs" :auto-sitemap t :recursive t :with-author nil :with-creator t :with-toc t :headline-levels 1 :section-numbers nil :time-stamp-file nil :publishing-function org-html-publish-to-html)))) (copy-file "README.org" "src/colophon.org" t) (build-index "Alex Drysdale") (build-rss-feed "Alex Drysdale" "https://abdrysdale.phd" "Blog posts by Alex Drysdale" "../src/" "docs/") (load-theme publish-theme) (org-publish-all t) (load-theme current-theme) (message "Site built at %s" (format-time-string "%Y-%m-%d %H:%M:%S")))
Git Hooks
This script is tangled into .git/hooks/build.el
which means that we just need to create a pre-commit
hook that runs the build.el
file.
#!/bin/sh emacs --batch -Q --script build.el git add docs/*.html
and make that file executable:
chmod +x .git/hooks/pre-commit
Conclusion
Not the most beautiful blog, or the most elegant build solution but this allows me to just write without think about much each.
There's still a few things I'd like to implement in the build process namely:
- TODO Include the date in the article link title and sort by date - newest first.
At this stage of the site, with the number of posts in the single digits, it's not essential.
- TODO Have each blog have a link to the tags file of the associated
FILETAGS
in that blog.
Similar to above, this will need to be sorted when more posts come into existence.
- TODO Include the following slashpages in the index
- TODO blogroll
A list of blogs that I follow.
- TODO links
Similar to blogroll but for specific posts.
- TODO changelog
- TODO contact
- TODO green
Include some tangible targets and current status/metrics.
- TODO interests
Nice way to express the things I'm interested in.
- TODO why
- TODO ideas
A list of the half-baked ideas I have.
- TODO next or yep
The better ideas should hopefully go here after I've given some thought and deemed them worth my time.
- TODO nope
The not so good ideas will go here to remind myself not to keep thinking about them.
- TODO blogroll
- TODO Include a link a page on projects that I've developed