2023-10-19 01:04:39 +02:00
|
|
|
+++
|
|
|
|
title = "Setting Up Blogging with Emacs"
|
|
|
|
[categories]
|
|
|
|
tags = [ "emacs" ]
|
|
|
|
categories = [ "apps" ]
|
|
|
|
+++
|
|
|
|
I\'d like to blog more notes on stuff I do and it would be nice to have
|
|
|
|
a smooth workflow in my editor of choice.
|
|
|
|
|
|
|
|
It is too late to explain a lot, but all these things were proudly found
|
|
|
|
elsewhere. See the references list at the end of this post.
|
|
|
|
|
|
|
|
# Creating the blog project
|
|
|
|
|
|
|
|
To deploy to github as a personal blog you have to create a repo in the
|
|
|
|
form **\<username\>.github.io**. Since I name my projects the same as
|
|
|
|
the repos the name was a quick choice.
|
|
|
|
|
|
|
|
The structure is as follows:
|
|
|
|
|
2024-04-06 00:24:53 +02:00
|
|
|
```
|
2023-10-19 01:04:39 +02:00
|
|
|
<root> -+- blog -+- posts -+- <blog posts>
|
|
|
|
| +- org-template -+- <templates>
|
|
|
|
| +- css -+- <css files>
|
|
|
|
+- public -+- <built website>
|
|
|
|
+- .github -+- workflow --- main.yml <github actions>
|
|
|
|
+- publish.el
|
|
|
|
+- Makefile <local develop actions>
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
# Configuring org-publish
|
|
|
|
|
|
|
|
This is what the **publish.el** file is for.
|
|
|
|
|
|
|
|
Prepare some snippets for the HTML pages
|
|
|
|
|
|
|
|
First off, a link to the CSS:
|
|
|
|
|
2024-04-06 00:24:53 +02:00
|
|
|
``` lisp
|
2023-10-19 01:04:39 +02:00
|
|
|
(setq website-html-head "<link rel=\"stylesheet\" href=\"css/site.css\"
|
|
|
|
type=\"text/css\"/>")
|
|
|
|
```
|
|
|
|
|
|
|
|
Let\'s also add a navigation menu at the top of each page:
|
|
|
|
|
2024-04-06 00:24:53 +02:00
|
|
|
``` lisp
|
2023-10-19 01:04:39 +02:00
|
|
|
(setq website-html-preamble
|
|
|
|
"<div class=\"nav\">
|
|
|
|
<ul>
|
|
|
|
<li><a href=\"/\">Home</a></li>
|
|
|
|
<li><a href=\"https://github.com/ptillemans\">GitHub</a></li>
|
|
|
|
</ul>
|
|
|
|
</div>")
|
|
|
|
```
|
|
|
|
|
|
|
|
And a footer :
|
|
|
|
|
2024-04-06 00:24:53 +02:00
|
|
|
``` lisp
|
2023-10-19 01:04:39 +02:00
|
|
|
(setq website-html-postamble "<div class=\"footer\"> Copyright 2020 %a (%v
|
|
|
|
HTML).<br> Last updated %C.<br> Built with %c. </div>")
|
|
|
|
```
|
|
|
|
|
|
|
|
And now we can all tie it together by creating the
|
|
|
|
**org-publish-project-alist**:
|
|
|
|
|
2024-04-06 00:24:53 +02:00
|
|
|
``` lisp
|
2023-10-19 01:04:39 +02:00
|
|
|
(setq org-publish-project-alist
|
|
|
|
`(("posts"
|
|
|
|
|
|
|
|
;; configure project structure
|
|
|
|
:base-directory "blog/posts/"
|
|
|
|
:base-extension "org"
|
|
|
|
:publishing-directory "public/"
|
|
|
|
:recursive t
|
|
|
|
:publishing-function org-html-publish-to-html
|
|
|
|
|
|
|
|
;; configure index creation
|
|
|
|
:auto-sitemap t
|
|
|
|
:sitemap-title "Blog Index"
|
|
|
|
:sitemap-filename "index.org"
|
|
|
|
:sitemap-style tree
|
|
|
|
:sitemap-file-entry-format "%d - %t"
|
|
|
|
:sitemap-sort-files anti-chronologically
|
|
|
|
|
|
|
|
:html-doctype "html5"
|
|
|
|
:html-html5-fancy t
|
|
|
|
:html-head ,website-html-head
|
|
|
|
:html-preamble ,website-html-preamble
|
|
|
|
:html-postamble ,website-html-postamble
|
|
|
|
|
|
|
|
:author "Peter Tillemans"
|
|
|
|
:email "pti@snamellit.com"
|
|
|
|
:with-creator t)
|
|
|
|
|
|
|
|
("blog-static"
|
|
|
|
:base-directory "blog/posts/"
|
|
|
|
:base-extension "png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf"
|
|
|
|
:publishing-directory "public_html/"
|
|
|
|
:recursive t
|
|
|
|
:publishing-function org-publish-attachment)
|
|
|
|
|
|
|
|
("css"
|
|
|
|
:base-directory "blog/css/"
|
|
|
|
:base-extension "css"
|
|
|
|
:publishing-directory "public/css"
|
|
|
|
:publishing-function org-publish-attachment
|
|
|
|
:recursive t)
|
|
|
|
|
|
|
|
("all" :components ("posts" "css" "blog-static"))))
|
|
|
|
```
|
|
|
|
|
|
|
|
# Make local testing easy
|
|
|
|
|
|
|
|
The commands to build the blog are not hard, but hard to remember and
|
|
|
|
hard to type.
|
|
|
|
|
|
|
|
Let\'s make a makefile to help:
|
|
|
|
|
|
|
|
``` makefile
|
|
|
|
.PHONY: all publish publish_no_init
|
|
|
|
|
|
|
|
all: publish
|
|
|
|
|
|
|
|
publish: publish.el
|
|
|
|
@echo "Publishing... with current Emacs configurations."
|
|
|
|
emacs --batch --load publish.el --funcall org-publish-all
|
|
|
|
|
|
|
|
publish_no_init:
|
|
|
|
@echo "Publishing... with --no-init"
|
|
|
|
emacs --batch --no-init --load publish.el --funcall org-publish-all
|
|
|
|
|
|
|
|
clean:
|
|
|
|
@echo "Cleaning up..."
|
|
|
|
rm -rf public
|
|
|
|
@rm -rvf *.elc
|
|
|
|
@rm -rvf public
|
|
|
|
@rm -rvf ~/.org-timestamps/*
|
|
|
|
|
|
|
|
serve: publish
|
|
|
|
@echo "Serving site"
|
|
|
|
python -m http.server --directory public
|
|
|
|
```
|
|
|
|
|
|
|
|
For local testing just do:
|
|
|
|
|
2024-04-06 00:24:53 +02:00
|
|
|
``` sh
|
2023-10-19 01:04:39 +02:00
|
|
|
$ make clean serve
|
|
|
|
```
|
|
|
|
|
|
|
|
If the only change is new content then not cleaning is much faster.
|
|
|
|
|
|
|
|
# Deploy to Github Pages
|
|
|
|
|
|
|
|
A slightly modified version of the initial workflow will do the
|
|
|
|
publishing:
|
|
|
|
|
|
|
|
``` yaml
|
|
|
|
# This is a basic workflow to help you get started with Actions
|
|
|
|
|
|
|
|
name: CI
|
|
|
|
|
|
|
|
# Controls when the action will run. Triggers the workflow on push or pull request
|
|
|
|
# events but only for the master branch
|
|
|
|
on:
|
|
|
|
push:
|
|
|
|
branches: [ master ]
|
|
|
|
pull_request:
|
|
|
|
branches: [ master ]
|
|
|
|
|
|
|
|
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
|
|
|
jobs:
|
|
|
|
# This workflow contains a single job called "build"
|
|
|
|
build:
|
|
|
|
# The type of runner that the job will run on
|
|
|
|
runs-on: ubuntu-latest
|
|
|
|
|
|
|
|
# Steps represent a sequence of tasks that will be executed as part of the job
|
|
|
|
steps:
|
|
|
|
- uses: actions/checkout@master
|
|
|
|
with:
|
|
|
|
fetch-depth: 1
|
|
|
|
|
|
|
|
- name: build
|
|
|
|
uses: docker://iquiw/alpine-emacs
|
|
|
|
if: github.event.deleted == false
|
|
|
|
with:
|
|
|
|
args: emacs --batch --load publish.el --funcall org-publish-all
|
|
|
|
|
|
|
|
|
|
|
|
- name: deploy
|
|
|
|
uses: peaceiris/actions-gh-pages@v1.1.0
|
|
|
|
if: success()
|
|
|
|
env:
|
|
|
|
GITHUB_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
|
|
|
|
PUBLISH_BRANCH: gh-pages
|
|
|
|
PUBLISH_DIR: ./public
|
|
|
|
```
|
|
|
|
|
|
|
|
Note that you need to put a secret **PERSONAL~ACCESSTOKEN~** with an
|
|
|
|
access token which has basic push access to the repo to push the built
|
|
|
|
site to the gh-pages branch.
|
|
|
|
|
|
|
|
For the emacs call, I just copied the command from the **Makefile**.
|
|
|
|
|
|
|
|
After a push the site is usually up by the time I check, say in about a
|
|
|
|
minute.
|
|
|
|
|
|
|
|
# Setting up a Capture Template
|
|
|
|
|
|
|
|
This proved to be the hardest part to get working.
|
|
|
|
|
|
|
|
I am using **Doom Emacs** so I wrap everything in
|
|
|
|
**with-eval-after-load**.
|
|
|
|
|
|
|
|
The challenge was that the title is needed to create the slug for the
|
|
|
|
filename and then again as title for the post. So my ugly solution is to
|
|
|
|
stuff it in a variable and get the variable back in the template.
|
|
|
|
|
2024-04-06 00:24:53 +02:00
|
|
|
``` lisp
|
2023-10-19 01:04:39 +02:00
|
|
|
(with-eval-after-load 'org-capture
|
|
|
|
(defvar snamellit/blog-title "test-title")
|
|
|
|
|
|
|
|
(setq snamellit-blog-template "#+title: %(progn snamellit-blog-title)
|
|
|
|
#+date: %t
|
|
|
|
#+author: Peter Tillemans
|
|
|
|
#+email: pti@snamellit.com
|
|
|
|
|
|
|
|
%?")
|
|
|
|
|
|
|
|
(defun snamellit/capture-blog-post-file ()
|
|
|
|
(let* ((title (read-from-minibuffer "Post Title: "))
|
|
|
|
(slug (replace-regexp-in-string "[^a-z0-9]+" "-" (downcase title))))
|
|
|
|
(setq snamellit/blog-title title)
|
|
|
|
(format "~/Projects/ptillemans.github.io/blog/posts/%s-%s.org"
|
|
|
|
(format-time-string "%Y-%m-%d" (current-time))
|
|
|
|
slug)))
|
|
|
|
|
|
|
|
(add-to-list 'org-capture-templates
|
|
|
|
'("b" "Blog Post" plain
|
|
|
|
(file snamellit/capture-blog-post-file)
|
|
|
|
(file "~/.doom.d/tpl-blog-post.org"))))
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
The **tpl-blog-post.org** template file :
|
|
|
|
|
2024-04-06 00:24:53 +02:00
|
|
|
```
|
2023-10-19 01:04:39 +02:00
|
|
|
#+title: %(progn snamellit/blog-title)
|
|
|
|
#+date: %<%Y-%m-%d>
|
|
|
|
|
|
|
|
%?
|
|
|
|
```
|
|
|
|
|
|
|
|
It is very minimal and I\'d like to keep it that way.
|
|
|
|
|
|
|
|
# In use
|
|
|
|
|
|
|
|
To create a blog post
|
|
|
|
|
|
|
|
- SPC-X b will create the post
|
|
|
|
- Give a title for the post
|
|
|
|
- A template file is created (unfortunately in plain text)
|
|
|
|
- Enter the idea, hook and save with C-c C-c
|
|
|
|
- Open the org file with SPC-f r (open recent file)
|
|
|
|
- Flesh out the post using org-mode goodness
|
|
|
|
- save, commit and push to git
|
|
|
|
|
|
|
|
After push the github action will bring it live
|
|
|
|
|
|
|
|
# References
|
|
|
|
|
|
|
|
Following links were useful in setting this up:
|
|
|
|
|
|
|
|
- [How to blog with Emacs Org
|
|
|
|
mode](https://opensource.com/article/20/3/blog-emacs)
|
|
|
|
- [GitHub Pages
|
|
|
|
documentation](https://docs.github.com/en/free-pro-team@latest/github/working-with-github-pages/about-github-pages)
|
|
|
|
- [The Org Mode Manual](https://orgmode.org/org.html)
|
|
|
|
- [The Meg in Progress post on building a static blog with
|
|
|
|
org-mode.](https://meganrenae21.github.io/Meg-in-Progress/posts/blogging-with-org-mode.html)
|
|
|
|
- [Website with org-mode](https://thenybble.de/projects/orgsite.html)
|
|
|
|
- [Richard Kallos\' post on site generation with Org
|
|
|
|
Mode](https://rkallos.com/blog/2017/01/02/static-site-generation-with-org-mode/)
|
|
|
|
- [Blogging with Org
|
|
|
|
mode](https://www.brautaset.org/articles/2017/blogging-with-org-mode.html)
|
|
|
|
- [Org Capture Tricks from
|
|
|
|
Storax](https://storax.github.io/blog/2016/05/02/org-capture-tricks/)
|