as a matter of course

This web log as been through a few iterations. First I used Jekyll on Github Pages, then I switched to Hugo using a docker container and Gitlab’s CI/CD pipeline, then finally the current setup.

I moved away from the docker container as the build started failing then I was hitting too many issues trying to get it working. In an effort to simplify everything I thought, why couldn’t I write my entries in markdown then use pandoc to convert them to HTML?

pandoc -f markdown -t html -o new_entry.html new_entry.md

Sounds so easy.

The main script, pyratelog.sh does all the heavy lifting. When a new entry is published the script does some prep then runs pandoc to convert the markdown file to html. I use a template file so my markdown only has to be the main body of content. This is how the command looks in my script

pandoc -s \
    --template=./entry_template.html \
    --metadata title="${input_title}" \
    -f markdown \
    -t html \
    -o entry/${input}.html \
    entry/${input}.md

The pyratelog.sh script also sets the link on the main page as well as adding the entry to my rss.xml file.

In order for the script to know when a new entry has been published I use inotifywait to watch for files in the entry/ directory

inotifywait -m -e create -e moved_to /var/www/html/entry/ | xargs -L 1 /var/www/html/pyratelog.sh

Over time I have included three more scripts to improve my writing workflow, draft, preview, and publish.

clapperboard

The draft script will checkout a new or existing branch based on the name I pass it, the name will be the title of the entry.

# use arg as title and set entry file path
TITLE=$1
ENTRY="entry/${TITLE}.md"

# checkout the correct branch
git branch | grep ${TITLE} && \
    git checkout -q ${TITLE} || \
    $(git checkout -q main && git checkout -q -b ${TITLE})

It will then open up a new or the existing file with that same name in vim.

# if entry file does not exist yet touch it
[ -f "${ENTRY}" ] || touch "${ENTRY}"

# open entry file in favourite editor
vim "${ENTRY}"

Now I let my creative juices flow to produce more enjoyable content for you.

Once I have finished and close vim, the draft script continues by adding then committing the changes to git

# when vim closes add and commit the changes
# if there are any
git status | grep "${ENTRY}" || exit 0
git add "${ENTRY}"
git commit -S -m "${TITLE}"

advanced screening

The preview script was written as sometimes I like to see what the entry looks like in the browser, especially when I put pictures in. It is useful to read through in the browser as well, I have caught a number of spelling mistakes along with poorly worded sentences that way.

To do this the script creates a temporary directory, copies the entry in question, the template file for pandoc, and my custom CSS

# use arg as title and set entry file path
TITLE=$1
ENTRY="entry/${TITLE}.md"

# create a tmp dir
mkdir demo

# copy entry file, entry_template and css
# to temp dir
cp ${ENTRY} demo/index.md
cp entry_template.html style.css demo/

# small change to entry_template for stylesheet
sed -i 's%\.\./%%' demo/entry_template.html

The same pandoc command is used to generate the HTML

# generate html file
pandoc -s \
    --template=demo/entry_template.html \
    --metadata title="demolog" \
    -f markdown \
    -t html \
    -o demo/index.html \
    demo/index.md

Using busybox the script starts a simple web server then waits for me to finish reviewing before killing the process and tidying up the temporary directory

# start web server and capture pid for later
busybox httpd -f -h ./demo -p 8080 &
busypid=$!

# open demo ENTRY file in browser
xdg-open http://localhost:8080

# wait until ready to stop web server
echo press any key to cancel
read -n 1

# kill the web server and remove temp dir
kill ${busypid}
rm -rf demo/

Unfortunately this script didn’t work on my phone while using Termux because pandoc is not available to install. As a workaround I started using AnLinux to give me a full Linux distro to work in.

it’s a wrap

Finally I can run the publish script to send my entry out into the world. This is the simplest of the three scripts. It will rename the entry to prefix the date then merge the branch into the main branch before pushing to my git server

# use arg as title and set entry file path
# set published file path with date
TITLE=$1
ENTRY="entry/${TITLE}.md"
PUBLISH="entry/$(date +%Y%m%d)-${TITLE}.md"

# checkout the correct branch
git branch | grep ${TITLE} && \
    git checkout ${TITLE} || \
    git checkout -b ${TITLE}

# rename entry to set date
git mv "${ENTRY}" "${PUBLISH}"

# commit the rename as published
git commit -S -m "publish ${TITLE}"

# checkout main branch and merge published entry
git checkout main
git merge "${TITLE}"

# push new entry
git push

In my last entry I spoke about using git hooks on my git server to keep my wiki up to date. I use the same post-receive hook for my web log.

#!/bin/sh
ssh logserver "cd /var/www/html ; git pull"

The only thing I have to do after hitting publish is toot about it. I will be adding in an auto toot in the publish script soon, similar to my weeklymusictoot, to save me having to even do that little job.

behind the scenes

Now because I seem to like using Makefiles for running things I had a go at one for the draft, preview, and publish scripts.

It got a bit complicated with the arguments, but I was able to make it work, even setting it so that if I am already on the correct branch I don’t have to pass any arguments

#TITLE := $(shell git branch --show-current)
RND := ""
# https://stackoverflow.com/a/14061796
# If the first argument is "draft"
ifeq (draft,$(firstword $(MAKECMDGOALS)))
  # use the rest as arguments for "draft"
  ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
  # ...and turn them into do-nothing targets
  $(eval $(ARGS):;@:)
else
ifeq (preview,$(firstword $(MAKECMDGOALS)))
  ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
  $(eval $(ARGS):;@:)
else
ifeq (publish,$(firstword $(MAKECMDGOALS)))
  ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
  $(eval $(ARGS):;@:)
endif
endif
endif

ifeq ($(ARGS), )
    ARGS := $(shell git branch --show-current)
endif

prog: # ...
    # ...

.PHONY: draft preview publish

draft : prog
    scripts/draft $(ARGS) $(RND)

preview : prog
    scripts/preview $(ARGS)

publish : prog
    scripts/publish $(ARGS) $(RND)

As an example, to start a new entry I incant

make draft as_a_matter_of_course

An empty buffer is opened in vim so I can start writing. After some time I decide I need a break so I save and exit vim. When I come back I know I am already on the as_a_matter_of_course branch so I can incant

make draft

Once the entry is finished I save and exit vim then incant

make preview

After reviewing the finished piece I can incant

make publish

These three scripts have sped up my writing process as well as making it even easier to switch between drafts if I have more than one entry in the works at a time.

One issue that I have had to overcome, it’s a big one, is that I need to think of the title before I start the draft. A few of my previous entries have had working titles right up until the moment they are published. I have thought about adding in a step to the publish script to change the title before it goes out, but for now I will force myself to think harder about the titles.

There are still some tweaks and improvements I want to look into but I have been happy with this process since I created it, adding in the additional scripts has helped a lot. It has certainly sped up my workflow and I haven’t had any issues with the automated publishing, long may it last.