~mrp/markpenner.space

0f0f982bf14e05d0f238c7feb55b4006c1c0b3d0 — Mark Penner 10 months ago b9b7d9f
add post "Blogging With Hugo and Org-mode"
6 files changed, 105 insertions(+), 41 deletions(-)

M config.toml
A content/blog/blogging-hugo-org-mode.org
D content/blog/first-post.org
A layouts/_default/rss.xml
A layouts/blog/single.html
M static/styles.css
M config.toml => config.toml +1 -0
@@ 3,6 3,7 @@ cleanDestinationDir = true
copyright = 'This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.'
languageCode = 'en-us'
pagination = 0
rssLimit = 10
title = 'markpenner.space'
[author]
  email = 'mrpenner at mailbox.org'

A content/blog/blogging-hugo-org-mode.org => content/blog/blogging-hugo-org-mode.org +49 -0
@@ 0,0 1,49 @@
#+title: Blogging With Hugo and Org-mode
#+date: 2023-08-02T19:00:00-05:00
#+draft: false
#+tags[]: blog website

* Blogging With Hugo and Org-mode
This blog is generated with [[https://gohugo.io][Hugo]]. The source is currently [[https://markpenner.space/gitweb/markpenner.space.git][here]].

I set it up a bit differently than many tutorials, so I thought I would describe what I did and why. I did not use a [[https://themes.gohugo.io][theme]]; I just wrote a few layouts and a short CSS file for a [[https://brutalist-web.design][brutalist design]]. I wanted to be able to understand and control my design rather than try to figure out how to get what I wanted out of an existing theme.

I like the [[https://karl-voit.at/2017/09/23/orgmode-as-markup-only/][expressivity and clarity]] of [[https://orgmode.org/][Org mode]] syntax rather than Markdown. Thankfully, Hugo [[https://gohugo.io/content-management/formats/#list-of-content-formats][can read Org files]] good enough for me. [[https://github.com/niklasfasching/go-org][go-org]], the library Hugo uses to parse Org files, is intended /"to support a reasonable subset of Org mode"/. There is also [[https://ox-hugo.scripter.co][ox-hugo]], an Org exporter to Hugo Markdown, but I have not tried it yet.

I am using a [[https://linode.com][Linode]] VPS (virtual private server) to host the website (among other things). I have the source in a git repository with this post-receive hook to deploy the site after a successful ~git push~:
#+BEGIN_SRC bash
#!/bin/bash

GIT_REPO=/var/lib/git/markpenner.space.git
WORK_DIR=/tmp/work_dir
TARGET=/var/www/html
BACKUP=/tmp/backup_html

echo "Post receive hook starting"
set -e

rm -rf $WORK_DIR $BACKUP

echo "Making temporary directories."
mkdir -p $WORK_DIR $BACKUP

rsync -aqz $TARGET/ $BACKUP
trap "echo 'A problem occurred.  Reverting to backup.'; rsync -aqz --del $BACKUP/ $TARGET; rm -rf $WORK_DIR $BACKUP" EXIT

echo "Pulling into temporary directory."
git clone $GIT_REPO $WORK_DIR

echo "Removing existing site."
rm -rf $TARGET/*

echo "building new site"
hugo -s $WORK_DIR -d $TARGET

echo "Removing temporary directories."
rm -rf $WORK_DIR $BACKUP
trap - EXIT
#+END_SRC

I'm not sure how much I will blog, but probably not a lot. My main motivation for starting a blog is to write down things I have learned. Hopefully some of what I write will be helpful to others, but I also want something for myself to refresh my memory later. I have learned a lot from people that write down things they have learned, whether they are experts or not. I want to do my part to keep that tradition strong.

Of course, it's also possible there will be long periods of silence occasionally interrupted by posts about my intentions to start blogging again. ;)

D content/blog/first-post.org => content/blog/first-post.org +0 -40
@@ 1,40 0,0 @@
#+title: First Post
#+date: 2023-07-25T21:50:01-05:00
#+draft: true
#+tags[]: blog website

* First Post

2022-07-02

I often find myself reading other people's blogs to find out how they
accomplished something technical. Sometimes, I find things have changed
since that post was written, or what I want to accomplish is slightly
different from what they did, so I thought in those cases maybe I should
write down what worked for me. I would like something that I could look
back at to remind myself what I did, and it's possible someone else
might find it useful also.

The obvious thing to do then, would be to pick a static site generator
(SSG) from the [[https://jamstack.org/generators/][many options]]. As I
looked at the different SSGs and their approaches, I had the sensation
of following a path many others had travelled before me. I couldn't find
an SSG that I really liked, and I thought "how hard could it be to write
my own?", which I suspect is how many of the options were started. The
basic idea is quite simple, write your content in Markdown or whatever
you prefer and use a parser and templating system to generate HTML for
you. Of course many SSGs can do much more than that, which is part of
what I didn't like about them. I thought if I needed to learn a magic
abstraction layer anyway, I might as well write my own that didn't have
all the extra magic that I didn't need.

Since there were many general purpose options already, and I only wanted
to generate my own website and blog, I decided to start with a simple
Python script that I would keep alongside the website source files.
Markdown parsing is provided by
[[https://github.com/miyuchina/mistletoe][mistletoe]]. I chose
[[https://mustache.github.io/][Mustache]] for the templates, using the
library [[https://github.com/PennyDreadfulMTG/pystache][pystache]]. I'll
see how it goes, maybe I'll decide at some point to just use a
mainstream SSG, but so far this is fun! Website source can be found
[[https://markpenner.space/gitweb/markpenner.space.git][here]].

A layouts/_default/rss.xml => layouts/_default/rss.xml +39 -0
@@ 0,0 1,39 @@
{{- $pctx := . -}}
{{- if .IsHome -}}{{ $pctx = .Site }}{{- end -}}
{{- $pages := slice -}}
{{- if or $.IsHome $.IsSection -}}
{{- $pages = $pctx.RegularPages -}}
{{- else -}}
{{- $pages = $pctx.Pages -}}
{{- end -}}
{{- $limit := .Site.Config.Services.RSS.Limit -}}
{{- if ge $limit 1 -}}
{{- $pages = $pages | first $limit -}}
{{- end -}}
{{- printf "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" | safeHTML }}
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>{{ if eq  .Title  .Site.Title }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{.}} on {{ end }}{{ .Site.Title }}{{ end }}</title>
    <link>{{ .Permalink }}</link>
    <description>Recent content {{ if ne  .Title  .Site.Title }}{{ with .Title }}in {{.}} {{ end }}{{ end }}on {{ .Site.Title }}</description>
    <generator>Hugo -- gohugo.io</generator>{{ with .Site.LanguageCode }}
    <language>{{.}}</language>{{end}}{{ with .Site.Author.email }}
    <managingEditor>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</managingEditor>{{end}}{{ with .Site.Author.email }}
    <webMaster>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</webMaster>{{end}}{{ with .Site.Copyright }}
    <copyright>{{.}}</copyright>{{end}}{{ if not .Date.IsZero }}
    <lastBuildDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</lastBuildDate>{{ end }}
    {{- with .OutputFormats.Get "RSS" -}}
    {{ printf "<atom:link href=%q rel=\"self\" type=%q />" .Permalink .MediaType | safeHTML }}
    {{- end -}}
    {{ range $pages }}
    <item>
      <title>{{ .Title }}</title>
      <link>{{ .Permalink }}</link>
      <pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate>
      {{ with .Site.Author.email }}<author>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</author>{{end}}
      <guid>{{ .Permalink }}</guid>
      <description>{{ .Content | html }}</description>
    </item>
    {{ end }}
  </channel>
</rss>

A layouts/blog/single.html => layouts/blog/single.html +14 -0
@@ 0,0 1,14 @@
{{ define "main" }}
<article>
  {{ .Date.Format "2006-01-02" }}
  {{ .Content }}
  {{ $taxonomy := "tags" }}
  {{ with .GetTerms $taxonomy }}
    <p>{{ (site.GetPage $taxonomy).LinkTitle }}:
      {{ range . }}
        <a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a>
      {{ end }}
    </p>
  {{ end }}
</article>
{{ end }}

M static/styles.css => static/styles.css +2 -1
@@ 9,7 9,8 @@ nav {
body {
  width: device-width;
  max-width: 50em;
  margin: 0 auto+1em;
  margin: 0 auto;
  padding: 0 1em;
  font-family: sans-serif;
}
footer {