Adventures in automation - entry 001: writing new posts

I’m currently in Breda, southern Netherlands on a train to Brussels with no wi-fi and no 4G service. So I thought what better time to write a blog post about automating … blog posts.

This is most likely going to be an ongoing series where I share my thought process behind automating some of the most tedious and boring tasks I do on the daily. So here goes.

Now this one is going to be very quick and easy: one of the reasons I don’t publish as often as I had hoped to, is because I find the preliminary process of writing an article to be dreadful. Let’s fix it.

The process of publishing a post is divided into 3 steps:

  • navigating to my jekyll posts directory.
  • creating a file that follows the specific jekyll nomenclature of YYYY-MM-DD-post-title.md
  • adding the front matter section at the top of the file, for metadata and ease of navigation

I initially intended to do it as a fish function, but I thought it’d be best to make an executable bash script instead. Why not python you ask ? Because it’s incredibly slow (and I love python). It would simply be overkill to use it for such a simple use-case.

1. The Outline

I always start with a written outline of what the program should do just to get a general idea of how I’m going to reason about things. For this instance it is as follows:

The program should be able to take a title as argv[1] and create 
the associated file along with its yaml frontmatter header information.

ex: for the following input 
  newpost "This is a TITLE"
a file YYYY-MM-DD-this-is-a-title.md should be created under ~/dev/0xfishr.xyz/_posts/

with the following content:

---
layout: post
title: This is a TITLE
permalink: /notes/this-is-a-title
---

2. The Implementation

Alright, now on to the fun part. First things first, we add the shebang line at the top of the script.

#!/usr/bin/bash

This is to make sure that it uses bash instead of the default shell to interpret the program, which can lead to errors and other potential security issues.

In accordance with the previously written outline, we need to be able to create a file that contains the date in the YYYY-MM-DD format, so we’ll use the date utility with the correct formatting argument and store the result in a variable.

TODAYS_DATE=$(date +%F)

Next, we need to capture the actual argument passed to the program. In bash, it’s stored as $1. We’ll store that in a variable too for readability and consistency.

POST_TITLE=$1

Now that we have the title and the date in the correct format, we need to store the path to the blog posts on the system. For me, it happens to be ~/dev/0xfishr.xyz/_posts/

BLOG_PATH=$HOME/dev/0xfishr.xyz/_posts

If we go back to the outline, the filename should be in the format date-title-separated-by-hypends.md. This means we must replace all spaces with hypens. There are many ways of doing this but tr seems to be the most straightforward. (Sorry, no sed/awk fu today).

FORMATTED_POST_TITLE=$(echo $POST_TITLE | tr " " "-")

So that’s the spaces dealt with, but we forgot one edge case. What about uppercase characters ? The filename should be all lowercase. Not to worry, tr can to that too. All we have to do is pipe the previously formatted title to tr again.

FORMATTED_POST_TITLE=$(echo $POST_TITLE | tr " " "-" | tr "[:upper:]" "[:lower:]")

this will transform all uppercase characters to lowercase.

Ok, we’re almost done. All that’s left to do for this first part is to assemble the filename and actually create the file.

FILENAME="$BLOG_PATH$TODAYS_DATE-$FORMATTED_POST_TITLE.md"
touch $FILENAME

The last thing we have to do is populate the newly created file with the corresponding yaml frontmatter metadata. Fortunately this can easily be accomplished with echo and output redirection.

echo -e "---\nlayout: post\ntitle: $POST_TITLE\npermalink: /notes/$FORMATTED_POST_TITLE\n---" > $FILENAME

and there we go.

We have a fully functioning program in less than 10 lines of bash (and that’s for readability, we could’ve made it even shorter).

Let’s wrap things up by committing this script to our dotfiles and make it executable. I have a directory called bin where I store my automation scripts to make them portable across my servers and computers.

git commit newpost.sh
git push origin master

To make it useable system wide we’ll just symlink it to a directory in our path. I like to put my custom scripts under $HOME/.local/bin/, but you can choose whichever location you like, as long as it’s in your $PATH.

ln -s ~/.dotfiles/bin/newpost.sh $HOME/.local/bin/newpost

And we’re finally done.

I hope to make many more of these quick tutorials / technical notes in the futures, so stay tuned for more.

Have a nice one,

Karim aka 0xfishr.